Skip to main content
mastering ckad certified kubernetes application developer

Pod and Deployment Solutions

6 min read Chapter 29 of 87
Summary

Step-by-step solutions for Exercises 1 and 2: creating...

Step-by-step solutions for Exercises 1 and 2: creating a multi-container Pod with a sidecar log reader, and performing a rolling update Deployment with rollback verification.

Pod and Deployment Solutions

Solution: Exercise 1 — Sidecar Logging Pod

This exercise requires a Pod with two containers sharing a volume: an application container that writes log lines, and a sidecar container that reads them.

Step 1: Write the Pod Manifest

Create the manifest file:

cat > sidecar-logging.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: sidecar-logging
  labels:
    app: sidecar-demo
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c"]
      args:
        - |
          i=0
          while true; do
            echo "$(date '+%Y-%m-%d %H:%M:%S') log-line-$i" >> /var/log/app/output.log
            i=$((i+1))
            sleep 2
          done
      volumeMounts:
        - name: log-volume
          mountPath: /var/log/app
    - name: log-reader
      image: busybox:1.36
      command: ["sh", "-c"]
      args:
        - tail -f /var/log/app/output.log
      volumeMounts:
        - name: log-volume
          mountPath: /var/log/app
  volumes:
    - name: log-volume
      emptyDir: {}
EOF

The key structural decisions in this manifest:

  • emptyDir volume: Created when the Pod is assigned to a node. Both containers mount it at /var/log/app, giving them shared filesystem access. The volume lives as long as the Pod runs on that node — it survives container restarts but not Pod rescheduling.

  • app container: Uses a shell loop to write timestamped log lines to /var/log/app/output.log every 2 seconds. The >> append operator ensures log lines accumulate rather than overwriting.

  • log-reader container: Runs tail -f to stream new lines from the same file. In production, this sidecar would forward logs to a centralized system (Fluentd, Loki, CloudWatch). For this exercise, it demonstrates the shared volume pattern.

Step 2: Apply and Verify

kubectl apply -f sidecar-logging.yaml

Wait for the Pod to reach Running status:

kubectl get pod sidecar-logging -w

Expected output after a few seconds:

NAME              READY   STATUS    RESTARTS   AGE
sidecar-logging   2/2     Running   0          8s

The 2/2 confirms both containers are running. If you see 1/2, one container has not started yet — wait a few more seconds.

Step 3: Verify Log Output

Read the sidecar’s stdout to confirm it is receiving log lines:

kubectl logs sidecar-logging -c log-reader

Expected output (timestamps will differ):

2026-03-01 10:15:00 log-line-0
2026-03-01 10:15:02 log-line-1
2026-03-01 10:15:04 log-line-2
2026-03-01 10:15:06 log-line-3

You can also verify the app container’s log file directly:

kubectl exec sidecar-logging -c app -- cat /var/log/app/output.log

This should produce the same lines. Both containers see the same file because they share the log-volume mount.

Step 4: Stream Live Logs

To watch the sidecar stream in real time:

kubectl logs sidecar-logging -c log-reader -f

Press Ctrl+C to stop following. New lines should appear every 2 seconds, confirming the sidecar continuously tails the application log.

Troubleshooting

SymptomCauseFix
0/2 containers readyImage pull failureCheck image name: busybox:1.36
log-reader shows no outputVolume mount path mismatchVerify both containers mount to /var/log/app
CrashLoopBackOff on appShell script syntax errorCheck the args block uses literal block scalar (|)
Pod stays PendingNo node available or resource pressureRun kubectl describe pod sidecar-logging and check Events

Cleanup

kubectl delete pod sidecar-logging

Solution: Exercise 2 — Rolling Update Deployment

This exercise requires creating a Deployment, performing a rolling update to a new image version, verifying the rollout, and then rolling back.

Step 1: Create the Initial Deployment

cat > rolling-deploy.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: nginx
          image: nginx:1.24
          ports:
            - containerPort: 80
EOF

Key fields:

  • strategy.rollingUpdate.maxSurge: 1: During an update, Kubernetes can create at most 1 Pod beyond the desired replica count (so up to 4 Pods temporarily exist).
  • strategy.rollingUpdate.maxUnavailable: 1: At most 1 Pod can be unavailable during the update. Combined with maxSurge: 1, this means the update proceeds one Pod at a time — creating a new Pod, verifying it is ready, then terminating an old one.

Apply the Deployment:

kubectl apply -f rolling-deploy.yaml

Verify all 3 replicas are running:

kubectl get deployment web-app

Expected output:

NAME      READY   UP-TO-DATE   AVAILABLE   AGE
web-app   3/3     3            3           12s

Step 2: Record the Initial Revision

Check the rollout history:

kubectl rollout history deployment/web-app

Expected output:

deployment.apps/web-app 
REVISION  CHANGE-CAUSE
1         <none>

Step 3: Perform the Rolling Update

Update the container image from nginx:1.24 to nginx:1.25:

kubectl set image deployment/web-app nginx=nginx:1.25

Watch the rollout progress:

kubectl rollout status deployment/web-app

Expected output:

Waiting for deployment "web-app" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "web-app" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "web-app" rollout to finish: 2 of 3 updated replicas are available...
deployment "web-app" successfully rolled out

Step 4: Verify the Update

Confirm the new image is running across all Pods:

kubectl get pods -l app=web-app -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'

Expected output:

web-app-6d9f8b7c5-abc12    nginx:1.25
web-app-6d9f8b7c5-def34    nginx:1.25
web-app-6d9f8b7c5-ghi56    nginx:1.25

All three Pods should show nginx:1.25. The Pod names will differ — what matters is the image version.

Check rollout history now shows two revisions:

kubectl rollout history deployment/web-app
deployment.apps/web-app 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

Step 5: Roll Back to the Previous Version

kubectl rollout undo deployment/web-app

Expected output:

deployment.apps/web-app rolled back

Verify the rollback completed:

kubectl rollout status deployment/web-app

Confirm the image reverted to nginx:1.24:

kubectl get deployment web-app -o jsonpath='{.spec.template.spec.containers[0].image}'

Expected output:

nginx:1.24

Check rollout history — the revision number advances, it does not go backwards:

kubectl rollout history deployment/web-app
deployment.apps/web-app 
REVISION  CHANGE-CAUSE
2         <none>
3         <none>

Revision 1 disappears because undo promoted revision 1 to become the new revision 3. The Deployment always moves forward in revision numbering.

Troubleshooting

SymptomCauseFix
Rollout stuck at “1 out of 3 updated”New image fails readinessCheck kubectl describe pod for image pull errors or crash loops
kubectl rollout undo reports “no last revision”Only one revision existsYou need at least 2 revisions before undo works
Old Pods not terminatingmaxUnavailable: 0 with maxSurge: 0At least one must be > 0 or the update deadlocks
Pods show old image after updateCached jsonpath outputRe-run the get command; old Pods terminate asynchronously

Cleanup

kubectl delete deployment web-app