Hands-on Lesson 11 of 14

Deploy an App End-to-End

Build, containerize, and deploy a full application to Kubernetes with ConfigMaps, Secrets, Services, Ingress, and scaling.

๐Ÿง’ Simple Explanation (ELI5)

Imagine you've built a lemonade stand. Now you need to set it up in a busy marketplace. You need to 1) pack all your supplies (containerize), 2) pick a spot and set up the stand (deploy), 3) put up a sign so people can find you (service/ingress), 4) hire helpers when lines get long (scaling), and 5) protect the recipe (secrets). This lesson puts it all together.

๐Ÿ—๏ธ What We're Building

Architecture Overview
Internet
Users
โ†’
Ingress
NGINX Controller
myapp.local
โ†’
Service
ClusterIP
webapp-svc:80
โ†’
Deployment (3 replicas)
Pod 1
Pod 2
Pod 3
โ†’
Data
ConfigMap
Secret

โŒจ๏ธ Step 1: Create the Namespace

bash
# Create a dedicated namespace for isolation
kubectl create namespace webapp-demo

# Set it as default context (optional but convenient)
kubectl config set-context --current --namespace=webapp-demo

โŒจ๏ธ Step 2: ConfigMap for App Configuration

yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
  namespace: webapp-demo
data:
  APP_ENV: "production"
  APP_PORT: "8080"
  LOG_LEVEL: "info"
  WELCOME_MESSAGE: "Welcome to SKILLY Kubernetes Demo!"
bash
kubectl apply -f configmap.yaml

โŒจ๏ธ Step 3: Secret for Sensitive Data

bash
# Create secret imperatively (values auto base64-encoded)
kubectl create secret generic webapp-secret \
  --from-literal=DB_PASSWORD='s3cureP@ss!' \
  --from-literal=API_KEY='sk-abc123xyz' \
  -n webapp-demo
โš ๏ธ
Production Note

In production, use external secret managers (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault) integrated with Kubernetes via CSI driver or operators. Never commit secrets to version control.

โŒจ๏ธ Step 4: Deployment

yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  namespace: webapp-demo
  labels:
    app: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0    # Zero-downtime deployment
  template:
    metadata:
      labels:
        app: webapp
        version: v1
    spec:
      containers:
        - name: webapp
          image: nginx:1.25-alpine
          ports:
            - containerPort: 80
          envFrom:
            - configMapRef:
                name: webapp-config
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: webapp-secret
                  key: DB_PASSWORD
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              cpu: 250m
              memory: 128Mi
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 3
            periodSeconds: 5
bash
kubectl apply -f deployment.yaml

# Watch pods come up
kubectl get pods -w -n webapp-demo

# Verify all 3 replicas are Running
kubectl get deployment webapp -n webapp-demo

โŒจ๏ธ Step 5: Service

yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-svc
  namespace: webapp-demo
spec:
  type: ClusterIP
  selector:
    app: webapp
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
bash
kubectl apply -f service.yaml

# Verify endpoints exist (should show 3 pod IPs)
kubectl get endpoints webapp-svc -n webapp-demo

# Quick test via port-forward
kubectl port-forward svc/webapp-svc 8080:80 -n webapp-demo
# Visit http://localhost:8080

โŒจ๏ธ Step 6: Ingress

yaml
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
  namespace: webapp-demo
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: myapp.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: webapp-svc
                port:
                  number: 80
bash
kubectl apply -f ingress.yaml

# On local machine, add to /etc/hosts (or C:\Windows\System32\drivers\etc\hosts)
# 127.0.0.1  myapp.local

# Verify ingress
kubectl get ingress -n webapp-demo

โŒจ๏ธ Step 7: Horizontal Pod Autoscaler

yaml
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: webapp-hpa
  namespace: webapp-demo
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: webapp
  minReplicas: 3
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
bash
kubectl apply -f hpa.yaml

# Check HPA status
kubectl get hpa -n webapp-demo

โŒจ๏ธ Step 8: Rolling Update

bash
# Update the image version
kubectl set image deployment/webapp webapp=nginx:1.26-alpine -n webapp-demo

# Watch the rollout
kubectl rollout status deployment/webapp -n webapp-demo

# View rollout history
kubectl rollout history deployment/webapp -n webapp-demo

# If something goes wrong, rollback
kubectl rollout undo deployment/webapp -n webapp-demo

โœ… Verification Checklist

bash
# Run through entire verification
echo "=== Namespace ==="
kubectl get ns webapp-demo

echo "=== ConfigMap ==="
kubectl get configmap webapp-config -n webapp-demo

echo "=== Secret ==="
kubectl get secret webapp-secret -n webapp-demo

echo "=== Deployment ==="
kubectl get deployment webapp -n webapp-demo

echo "=== Pods ==="
kubectl get pods -n webapp-demo -o wide

echo "=== Service ==="
kubectl get svc webapp-svc -n webapp-demo

echo "=== Endpoints ==="
kubectl get endpoints webapp-svc -n webapp-demo

echo "=== Ingress ==="
kubectl get ingress -n webapp-demo

echo "=== HPA ==="
kubectl get hpa -n webapp-demo

echo "=== Events ==="
kubectl get events -n webapp-demo --sort-by=.lastTimestamp | tail -10

๐Ÿงน Cleanup

bash
# Delete everything at once by removing the namespace
kubectl delete namespace webapp-demo

๐Ÿ› Common Issues During Deployment

IssueCheckFix
Pods stuck in Pendingkubectl describe pod โ†’ EventsInsufficient resources; reduce requests or add nodes
ImagePullBackOffkubectl describe pod โ†’ image nameFix image name/tag or add imagePullSecrets
Service has no endpointskubectl get endpointsFix selector labels to match pod labels
Ingress returns 404kubectl describe ingressCheck service name, port, and ingressClassName
HPA shows <unknown>kubectl top podsInstall metrics-server

๐Ÿ“ Summary

โ† Back to Kubernetes Course