PROJECT GUIDE · 09

Deploy ABC Retail on Kubernetes (K8s)

Graduate from Docker Compose to enterprise-grade container orchestration. Install MicroK8s locally, write Kubernetes YAML manifests for all services, understand pods/deployments/services/ingress, implement horizontal pod autoscaling, and perform rolling deployments — all on your Ubuntu VM.

Environment
Ubuntu VM (MicroK8s)
Local Kubernetes cluster
Difficulty
⭐⭐⭐⭐ Advanced
Most complex project
Course Module
Chapter 9
Cloud-Native Architecture & K8s
Duration
7–10 Days
K8s fundamentals + full deployment

1. Kubernetes Architecture – From Docker to K8s

Kubernetes (K8s) is an open-source container orchestration system that automates deployment, scaling, and management of containerized applications. It was created by Google (based on internal system "Borg") and donated to the CNCF (Cloud Native Computing Foundation). Every major cloud provider offers managed Kubernetes: AWS EKS, Azure AKS, and Google GKE. Understanding K8s is arguably the most valuable skill in modern cloud engineering.

KUBERNETES CLUSTER ARCHITECTURE — ABC RETAIL CONTROL PLANE (Master Node) API Server kubectl gateway etcd Cluster state DB Scheduler Place pods on nodes Controller Manager Desired state enforcement cloud-controller-manager AWS/Azure/GCP integration WORKER NODES (where your containers run) Worker Node 1 Pod: web-1 abc-retail container Pod: web-2 abc-retail container Pod: db-1 mariadb container Pod: redis-1 redis container Worker Node 2 Pod: web-3 abc-retail container Pod: minio-1 minio container HPA: web pods 2→5 based on CPU % Horizontal Pod Autoscaler K8s API Objects Deployment → manages Pods Service → network access Ingress → HTTP routing ConfigMap → config data Secret → sensitive data PersistentVolumeClaim → storage HPA → autoscaling

2. Kubernetes Core Concepts Reference

K8s ObjectWhat It DoesDocker/Linux EquivalentWhen to Use
PodSmallest deployable unit — one or more containers that share network and storagedocker run (single container)Usually don't create directly — use Deployments instead
DeploymentManages a set of identical Pods; handles rolling updates and rollbacks automaticallydocker-compose service with replicasAll stateless applications (web servers, APIs)
StatefulSetLike Deployment but for stateful apps — pods get stable network IDs and persistent storagedocker-compose with persistent volumesDatabases, message queues, caches
ServiceStable network endpoint for Pods — acts as load balancer between pod replicasHAProxy / Docker network hostnameEvery Deployment needs a Service to be reachable
IngressHTTP/HTTPS routing — maps domain names and URL paths to ServicesNginx reverse proxy configurationExposing multiple services via single IP/domain
ConfigMapStores non-sensitive configuration data (environment variables, config files)Environment variables / .env fileApplication config that varies by environment
SecretLike ConfigMap but base64-encoded — for passwords, API keys, TLS certsHashiCorp Vault secretsDatabase passwords, API tokens, TLS certificates
PersistentVolumeClaimRequest for storage — K8s finds a matching PersistentVolume and binds itDocker named volumeDatabase data, uploaded files, logs
HPAHorizontal Pod Autoscaler — automatically scales replicas based on CPU/memory usageManual: docker run more containersAny Deployment that handles variable load
NamespaceVirtual cluster — isolates groups of resources within a clusterDocker networks (isolation)Separate dev/staging/prod on the same cluster

3. Step-by-Step Action Items

PHASE 1 · STEP 1 Install MicroK8s – A Lightweight Local Kubernetes Cluster

MicroK8s is a lightweight, official Kubernetes distribution by Canonical (the company behind Ubuntu). It runs on a single machine, making it perfect for local development and learning. The same kubectl commands you use with MicroK8s work identically with AWS EKS, Azure AKS, and Google GKE in production.

⚠ VM Resources Required MicroK8s requires at least 2 GB RAM and 2 CPU cores. In VirtualBox, power off your VM, go to Settings → System → Processor (set to 2 CPUs) and Memory (set to 4096 MB / 4 GB), then restart. A Kubernetes cluster runs many system processes alongside your applications — it needs headroom.
1
Install MicroK8s using snap (Ubuntu's universal package manager):
sudo snap install microk8s --classic --channel=1.28/stable
Snap packages are self-contained application bundles that include all dependencies. The --classic flag grants MicroK8s full system access (required for a Kubernetes cluster to manage networking and storage). --channel=1.28/stable installs a specific Kubernetes version — pinning to a version is important in production to prevent unexpected upgrades. Version 1.28 is an LTS (Long Term Support) release.
2
Add your user to the microk8s group and configure kubectl:
sudo usermod -aG microk8s $USER sudo chown -R $USER ~/.kube newgrp microk8s # Create a kubectl alias (microk8s uses its own kubectl command) echo "alias kubectl='microk8s kubectl'" >> ~/.bashrc source ~/.bashrc
The microk8s group grants permission to run MicroK8s commands without sudo. The alias kubectl='microk8s kubectl' lets you use the standard kubectl command name instead of microk8s kubectl — making your commands identical to what you'd type in a cloud Kubernetes cluster. The kubectl tool is the primary interface for all Kubernetes operations.
3
Wait for MicroK8s to be ready and check the cluster status:
microk8s status --wait-ready kubectl get nodes
The microk8s status --wait-ready command blocks until the cluster is fully initialized. kubectl get nodes lists all nodes in the cluster — on a single-machine MicroK8s setup, you should see one node with STATUS "Ready". A Kubernetes cluster needs at least one ready node before you can deploy workloads. In production, a cluster might have dozens or hundreds of worker nodes.
4
Enable essential MicroK8s add-ons:
microk8s enable dns storage ingress metrics-server # Wait for add-ons to be ready kubectl get pods -n kube-system
MicroK8s add-ons extend the cluster's capabilities: dns — adds CoreDNS for service name resolution (containers find each other by service name). storage — adds local storage provisioner so PersistentVolumeClaims work. ingress — adds Nginx Ingress Controller for HTTP routing. metrics-server — collects CPU/memory usage metrics (required for HPA autoscaling). These are equivalent to managed services that cloud Kubernetes providers include by default.
PHASE 1 · STEP 2 Create Namespace, ConfigMaps, and Secrets
1
Create a namespace for ABC Retail:
kubectl create namespace abc-retail kubectl get namespaces
Namespaces provide isolation within a Kubernetes cluster — like having separate folders for different projects. Resources in different namespaces don't conflict with each other (you can have a "web" deployment in both the "abc-retail" namespace and a "demo" namespace). All our ABC Retail resources will be created in the abc-retail namespace. Use -n abc-retail with every kubectl command to target this namespace.
2
Create a ConfigMap for application configuration:
cat > ~/abc-retail-iac/k8s/configmap.yaml << 'EOF' apiVersion: v1 kind: ConfigMap metadata: name: abc-retail-config namespace: abc-retail labels: app: abc-retail managed-by: kubectl data: APP_ENV: "production" APP_PORT: "80" DB_HOST: "mariadb-service" # Service name in K8s (DNS-resolved) DB_PORT: "3306" DB_NAME: "abc_retail" CACHE_HOST: "redis-service" CACHE_PORT: "6379" LOG_LEVEL: "info" EOF kubectl apply -f ~/abc-retail-iac/k8s/configmap.yaml kubectl get configmap -n abc-retail
ConfigMaps store non-sensitive configuration as key-value pairs. Applications read these values as environment variables or mounted files. Notice the DB_HOST value is mariadb-service — the name of the Kubernetes Service that will front-end the database pods. Kubernetes DNS automatically resolves service names within the cluster, so pods don't need IP addresses — they use service names. This means services can be scaled and moved without updating application configuration.
3
Create a Secret for database credentials:
cat > ~/abc-retail-iac/k8s/secrets.yaml << 'EOF' apiVersion: v1 kind: Secret metadata: name: abc-retail-secrets namespace: abc-retail type: Opaque stringData: # stringData is auto-base64-encoded DB_PASSWORD: "AppDB@K8s2024" DB_ROOT_PASSWORD: "RootDB@K8s2024" MINIO_ACCESS_KEY: "minioadmin" MINIO_SECRET_KEY: "MinioPass@2024" EOF kubectl apply -f ~/abc-retail-iac/k8s/secrets.yaml kubectl get secrets -n abc-retail
Kubernetes Secrets store sensitive data separately from ConfigMaps. Secrets are base64-encoded (not encrypted by default in a basic cluster — encryption at rest requires additional configuration). In production on cloud Kubernetes (EKS, AKS, GKE), Secrets are encrypted using the cloud provider's KMS (Key Management Service). Secrets can be mounted as files or injected as environment variables. The key advantage: application code never handles the actual secret values — they are injected by Kubernetes at pod startup.
PHASE 2 · STEP 1 Deploy MariaDB Database as StatefulSet
1
Create the MariaDB StatefulSet manifest:
nano ~/abc-retail-iac/k8s/mariadb-statefulset.yaml
2
Type the manifest:
apiVersion: apps/v1 kind: StatefulSet metadata: name: mariadb namespace: abc-retail spec: serviceName: mariadb-service replicas: 1 selector: matchLabels: app: mariadb template: metadata: labels: app: mariadb spec: containers: - name: mariadb image: mariadb:10.11 ports: - containerPort: 3306 envFrom: - configMapRef: name: abc-retail-config # Loads all ConfigMap values as env vars env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: abc-retail-secrets # Load specific key from Secret key: DB_ROOT_PASSWORD - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: abc-retail-secrets key: DB_PASSWORD - name: MYSQL_DATABASE value: "abc_retail" - name: MYSQL_USER value: "retailapp" resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" volumeMounts: - name: mariadb-data mountPath: /var/lib/mysql volumeClaimTemplates: # StatefulSet creates PVC per pod - metadata: name: mariadb-data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 5Gi --- apiVersion: v1 kind: Service metadata: name: mariadb-service namespace: abc-retail spec: clusterIP: None # Headless service for StatefulSet selector: app: mariadb ports: - port: 3306 targetPort: 3306
This YAML defines two Kubernetes objects (separated by ---). The StatefulSet is used instead of Deployment because MariaDB is stateful — each pod gets a persistent identity and its own PersistentVolumeClaim. Key concepts: envFrom: configMapRef loads ALL ConfigMap keys as environment variables automatically. valueFrom: secretKeyRef loads a specific key from a Secret. resources.requests specifies how much CPU/memory to reserve for the pod, and resources.limits sets the maximum. The Service with clusterIP: None creates a "headless" service — pods in the StatefulSet get DNS names like mariadb-0.mariadb-service.abc-retail.svc.cluster.local.
3
Apply the StatefulSet:
kubectl apply -f ~/abc-retail-iac/k8s/mariadb-statefulset.yaml kubectl get pods -n abc-retail -w # Watch pods start (-w flag = watch mode)
The kubectl apply -f command applies the YAML manifest — creating or updating the resources defined in it. The -w flag watches for changes in real-time (press Ctrl+C to stop watching). You should see the mariadb pod go through states: Pending → ContainerCreating → Running. Once Running, the database is accepting connections.
PHASE 2 · STEP 2 Deploy the Web Application with Horizontal Pod Autoscaling
1
Create the web Deployment and Service manifest:
cat > ~/abc-retail-iac/k8s/web-deployment.yaml << 'EOF' apiVersion: apps/v1 kind: Deployment metadata: name: abc-retail-web namespace: abc-retail labels: app: abc-retail-web version: "v1.0" spec: replicas: 2 selector: matchLabels: app: abc-retail-web strategy: type: RollingUpdate # Zero-downtime deployments rollingUpdate: maxSurge: 1 # Allow 1 extra pod during update maxUnavailable: 0 # Never go below desired replica count template: metadata: labels: app: abc-retail-web spec: containers: - name: web image: abc-retail:v1.0 ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" livenessProbe: # Kill and restart if this fails httpGet: path: / port: 80 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: # Stop sending traffic if this fails httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: web-service namespace: abc-retail spec: type: ClusterIP selector: app: abc-retail-web ports: - port: 80 targetPort: 80 --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-hpa namespace: abc-retail spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: abc-retail-web minReplicas: 2 maxReplicas: 5 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # Scale up when average CPU > 70% EOF kubectl apply -f ~/abc-retail-iac/k8s/web-deployment.yaml
This manifest defines three objects: The Deployment manages web pods with a RollingUpdate strategy — during an update, Kubernetes starts a new pod (maxSurge: 1) before removing an old one (maxUnavailable: 0), ensuring zero downtime. Liveness Probe checks if the container is alive — if it fails, Kubernetes restarts the pod. Readiness Probe checks if the container is ready to serve traffic — if it fails, the pod is removed from the load balancer until it recovers. The HorizontalPodAutoscaler watches CPU usage and scales the Deployment from 2 to up to 5 pods when load increases — automatically. In production, this handles traffic spikes without manual intervention.
2
Create the Ingress to expose the application externally:
cat > ~/abc-retail-iac/k8s/ingress.yaml << 'EOF' apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: abc-retail-ingress namespace: abc-retail annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: public rules: - host: abc-retail.local # Use this hostname in /etc/hosts http: paths: - path: / pathType: Prefix backend: service: name: web-service port: number: 80 EOF kubectl apply -f ~/abc-retail-iac/k8s/ingress.yaml
The Ingress resource defines HTTP routing rules. An Ingress Controller (Nginx, in this case — enabled as a MicroK8s add-on) watches for Ingress resources and configures Nginx routing automatically. The rules section says: requests for abc-retail.local on path / should be forwarded to web-service on port 80. To test: add [VM-IP] abc-retail.local to your host's /etc/hosts file, then open http://abc-retail.local in your browser.
PHASE 2 · STEP 3 Verify Deployment and Perform a Rolling Update
1
Check the status of all deployed resources:
kubectl get all -n abc-retail
The kubectl get all command lists all resources in the namespace: pods, deployments, services, statefulsets, HPAs, and more. This is the equivalent of checking your entire infrastructure in one command. The STATUS column shows if everything is Running and READY.
2
Inspect a specific pod in detail:
kubectl describe pod -l app=abc-retail-web -n abc-retail
The kubectl describe command shows detailed information about a resource: current state, events, conditions, environment variables, volume mounts, and health check results. The Events section at the bottom is particularly useful for debugging — it shows what Kubernetes did and any errors encountered. If a pod is stuck in "Pending" or "CrashLoopBackOff" state, the Events section usually explains why.
3
View logs from a running pod:
kubectl logs -l app=abc-retail-web -n abc-retail --tail=20
The kubectl logs command retrieves container output. The -l app=abc-retail-web uses a label selector — it shows logs from all pods with that label (your 2+ web replicas). --tail=20 shows only the last 20 lines. Add -f to follow logs in real-time (like tail -f). This is how you debug application issues in production Kubernetes clusters.
4
Perform a rolling update — deploy a new image version with zero downtime:
# First, build a new version of the image (add a change to HTML) docker build -t abc-retail:v2.0 . microk8s ctr images import <(docker save abc-retail:v2.0) # Update the deployment to use v2.0 kubectl set image deployment/abc-retail-web web=abc-retail:v2.0 -n abc-retail # Watch the rolling update happen kubectl rollout status deployment/abc-retail-web -n abc-retail
The kubectl set image command triggers a rolling update — Kubernetes starts new pods with the v2.0 image, waits for them to become ready, then removes v1.0 pods, one at a time (based on the RollingUpdate strategy settings). The kubectl rollout status command shows the progress of the update. During the entire update process, the application continues serving traffic from v1.0 pods until v2.0 pods are confirmed healthy.
5
Roll back if the new version has a bug:
kubectl rollout undo deployment/abc-retail-web -n abc-retail kubectl rollout history deployment/abc-retail-web -n abc-retail
The kubectl rollout undo command immediately rolls back to the previous version — Kubernetes knows what the previous state was (stored in the Deployment's revision history). This rollback takes effect within seconds, even for a deployment with many replicas. The rollout history command shows all previous versions and when they were deployed. This automated rollback capability is one of the most valuable features of Kubernetes — in a manual Docker setup, a rollback requires manually stopping containers, pulling old images, and restarting — a process that takes much longer and is prone to human error.
PHASE 3 · STEP 1 Apply All Manifests and Verify Full Stack
1
Apply all manifests at once from the directory:
kubectl apply -f ~/abc-retail-iac/k8s/ kubectl get all -n abc-retail
2
Test the complete stack from inside the cluster:
# Run a temporary debug pod to test internal service connectivity kubectl run -it --rm debug --image=curlimages/curl -n abc-retail -- sh # Inside the debug pod: curl http://web-service/ # Access web via service name curl http://mariadb-service:3306 --max-time 2 # Test DB connectivity exit
The temporary debug pod demonstrates service discovery — containers inside the cluster reach each other by service name without needing IP addresses. kubectl run --rm creates a pod that is automatically deleted when you exit. This is the Kubernetes equivalent of docker exec — you jump into the cluster's network namespace to debug connectivity issues. Cloud engineers use this technique constantly to diagnose network or DNS problems in production clusters.
3
Add the local hostname to test ingress (on your HOST machine):
• Windows: Edit C:\Windows\System32\drivers\etc\hosts as Administrator
• Linux/Mac: sudo nano /etc/hosts
• Add: [VM-IP] abc-retail.local
• Open http://abc-retail.local in browser

4. Complete Kubernetes Manifests

Complete kubectl Quick-Reference Commands
# Cluster overview kubectl get nodes kubectl get all -n abc-retail # Pod operations kubectl get pods -n abc-retail -o wide # Shows which node each pod is on kubectl logs pod-name -n abc-retail -f # Follow logs kubectl exec -it pod-name -n abc-retail -- bash # Shell into pod kubectl describe pod pod-name -n abc-retail # Full pod details # Deployment operations kubectl scale deployment abc-retail-web --replicas=4 -n abc-retail kubectl rollout status deployment/abc-retail-web -n abc-retail kubectl rollout undo deployment/abc-retail-web -n abc-retail kubectl rollout history deployment/abc-retail-web -n abc-retail # Resource management kubectl top pods -n abc-retail # CPU/memory usage kubectl top nodes # Node resource usage kubectl get hpa -n abc-retail # HPA status + current replicas # Delete resources kubectl delete -f manifest.yaml # Remove resources from file kubectl delete namespace abc-retail # Delete ALL resources in namespace

5. Deliverables Summary

📄 Files to Submit

  • k8s/configmap.yaml
  • k8s/secrets.yaml
  • k8s/mariadb-statefulset.yaml
  • k8s/web-deployment.yaml (Deployment + Service + HPA)
  • k8s/ingress.yaml
  • Screenshot: kubectl get all -n abc-retail (all Running)
  • Screenshot: Website accessible at abc-retail.local
  • Screenshot: HPA status showing current replicas
  • Screenshot: Rolling update kubectl rollout status
  • Kubernetes Architecture explanation document

✅ Verification Checklist

  • MicroK8s installed and cluster is Ready
  • DNS, Storage, Ingress, Metrics-server add-ons enabled
  • Namespace "abc-retail" created
  • ConfigMap and Secret created and verifiable
  • MariaDB StatefulSet pod is Running
  • PVC (storage) is Bound to the MariaDB pod
  • Web Deployment has 2 Running pods
  • HPA created and showing pod count
  • Ingress configured and website accessible
  • Rolling update performed and rollback tested
  • Debug pod connectivity test successful

6. Why We Did This & What It Accomplishes

Strategic Intent & Operational Impact

Why We Did This

What This Accomplishes