Files
kodekloud-engineer/kubernetes/mock exams/cka-exam-pack-2.md

13 KiB

CKA Mock Exam Pack v2 — 120 min, harder cut

17 weighted tasks, 120 minutes, pass = 66. Range: kind drills (+ Cilium, Gateway API CRDs, metrics-server, helm). Difficulty delta vs v1: control-plane components are broken and tasks interact — damage you don't fix early will block tasks you attempt later. Triage is part of the score. Kind-isms: ssh nodeXdocker exec -it drills-<node> bash. One kubectl context.

PROTOCOL

  1. Save MASTER SETUP to /tmp/setup2.sh, run blind: bash /tmp/setup2.sh > /tmp/setup2.log 2>&1. Do not read it — spoilers.
  2. mkdir -p /tmp/exam2. Hard 120:00 timer. No pauses, no Claude, no solutions file.
  3. Any order. Flag >8 min. Last 10 min = verification lap.
  4. After the timer: run VALIDATE blocks, fill the score sheet, then open the solutions doc for the autopsy.

MASTER SETUP (paste blind)

#!/usr/bin/env bash
set +e
for ns in ex2-neptune ex2-web ex2-canary ex2-data ex2-guard ex2-front ex2-batch ex2-side ex2-store; do kubectl create ns $ns; done

# workloads FIRST (scheduler dies later)
kubectl -n ex2-front create deploy shop-ui --image=nginx --replicas=2
kubectl -n ex2-front patch deploy shop-ui --type=json -p='[{"op":"add","path":"/spec/template/spec/containers/0/readinessProbe","value":{"httpGet":{"path":"/","port":8080},"periodSeconds":5}}]'
kubectl -n ex2-data run db --image=nginx --labels=role=db --port=80
kubectl -n ex2-data run app --image=nginx --labels=role=app --port=80
kubectl -n ex2-data run rogue --image=nginx --labels=role=rogue --port=80
kubectl -n ex2-web create deploy web-v1 --image=hashicorp/http-echo -- /http-echo -text=v1 -listen=:5678
kubectl -n ex2-web expose deploy web-v1 --port=5678
kubectl -n ex2-canary create deploy web-v2 --image=hashicorp/http-echo -- /http-echo -text=v2 -listen=:5678
kubectl -n ex2-canary expose deploy web-v2 --port=5678
kubectl apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata: {name: exam2-class}
spec: {controllerName: example.com/exam2}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata: {name: shop-gate, namespace: ex2-web}
spec:
  gatewayClassName: exam2-class
  listeners: [{name: http, port: 80, protocol: HTTP}]
EOF
kubectl -n ex2-neptune create deploy pinned --image=nginx --replicas=2
kubectl -n ex2-neptune patch deploy pinned --type=json -p='[{"op":"add","path":"/spec/template/spec/nodeSelector","value":{"kubernetes.io/hostname":"drills-worker2"}}]'
kubectl apply -f - <<'EOF'
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: {name: pinned-pdb, namespace: ex2-neptune}
spec:
  maxUnavailable: 0
  selector: {matchLabels: {app: pinned}}
EOF
kubectl label ns ex2-guard env=guarded
# storage scenarios
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata: {name: keeper-pv}
spec:
  capacity: {storage: 1Gi}
  accessModes: [ReadWriteOnce]
  persistentVolumeReclaimPolicy: Retain
  storageClassName: keeper
  hostPath: {path: /opt/keeper}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: old-claim, namespace: ex2-store}
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: keeper
  resources: {requests: {storage: 1Gi}}
EOF
sleep 5; kubectl -n ex2-store delete pvc old-claim   # -> PV Released
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata: {name: narrow-pv}
spec:
  capacity: {storage: 1Gi}
  accessModes: [ReadWriteOnce]
  storageClassName: narrow
  hostPath: {path: /opt/narrow}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: wide-claim, namespace: ex2-store}
spec:
  accessModes: [ReadWriteMany]
  storageClassName: narrow
  resources: {requests: {storage: 1Gi}}
EOF
# broken helm chart
mkdir -p /tmp/exam2 && cd /tmp/exam2 && helm create shipper >/dev/null 2>&1
sed -i 's/^apiVersion: v2/apiVersion: v3/' /tmp/exam2/shipper/Chart.yaml
sed -i 's/{{ .Values.replicaCount }}/{{ .Values.replicaCount }/' /tmp/exam2/shipper/templates/deployment.yaml
# broken kubeconfig for developer
kubectl config view --raw --minify > /tmp/exam2/developer.kubeconfig
SRV=$(grep server /tmp/exam2/developer.kubeconfig | awk '{print $2}')
PORT=${SRV##*:}; NEW=$((PORT+1))
sed -i "s|$SRV|${SRV%:*}:$NEW|" /tmp/exam2/developer.kubeconfig
kubectl -n ex2-front wait --for=condition=Available deploy --all --timeout=90s
kubectl -n ex2-data wait --for=condition=Ready pod --all --timeout=90s
# BREAK coredns
kubectl -n kube-system get cm coredns -o yaml > /tmp/exam2/.coredns-backup.yaml
kubectl -n kube-system patch cm coredns --type merge -p '{"data":{"Corefile":".:53 {\n    errors\n    forward . /etc/resolv.conf\n    bogusplugin\n}\n"}}'
kubectl -n kube-system rollout restart deploy coredns
# BREAK scheduler (last)
docker exec drills-control-plane sed -i 's|kube-scheduler|kube-schedulerr|' /etc/kubernetes/manifests/kube-scheduler.yaml
echo "SETUP2 DONE"

THE EXAM

Q1 — 8% — Troubleshooting

New Pods across the cluster stay Pending with no scheduling events. A control-plane component is at fault. Find it, fix it on the node, prove new pods schedule again. Component name → /tmp/exam2/q1.txt.

Q2 — 7% — Troubleshooting

Cluster DNS is down (CoreDNS crash-looping). Diagnose and repair so that a busybox:1.28 pod can nslookup kubernetes.default. Root cause, one line → /tmp/exam2/q2.txt.

Q3 — 6% — Troubleshooting

The kubeconfig at /tmp/exam2/developer.kubeconfig is broken — kubectl --kubeconfig /tmp/exam2/developer.kubeconfig get nodes fails. Fix the file (not your admin config) so the command succeeds. Fault, one line → /tmp/exam2/q3.txt.

Q4 — 5% — Troubleshooting

Drain node drills-worker2 (ignore DaemonSets). Something will block it. Resolve the blocker with the minimal change that still keeps ≥1 replica of the affected app available at all times, complete the drain, then uncordon. Blocker → /tmp/exam2/q4.txt.

Q5 — 4% — Troubleshooting

Deployment shop-ui in ex2-front shows 0/2 READY though containers run. Find why and fix the deployment (the container itself is fine).

Q6 — 8% — Cluster Architecture

Take an etcd snapshot to /root/etcd-v2.db on the control-plane node. Then create ConfigMap marker (kubectl create cm marker -n default --from-literal=state=after-backup), and restore the snapshot so that marker no longer exists. Prove it.

Q7 — 6% — Cluster Architecture

The Helm chart at /tmp/exam2/shipper fails to install. Find and fix all faults (start with helm lint), then install it as release shipper in namespace ex2-batch with 2 replicas set via CLI.

Q8 — 6% — Cluster Architecture

Namespace ex2-guard is labeled env=guarded. Create a ValidatingAdmissionPolicy require-owner + binding: every Deployment created in namespaces labeled env=guarded must carry a label owner (any value); violations are denied with a message mentioning "owner". Prove: one denied create, one accepted.

Q9 — 5% — Cluster Architecture

Under /tmp/exam2/kz, build a kustomization (no base reuse — from scratch): deploys nginx:1.27 deployment board, namespace ex2-batch, replicas patched to 3 via a JSON6902 patch, plus a generated ConfigMap board-cfg with MODE=exam. Apply with -k. The deployment must reference the generated ConfigMap via envFrom (hash suffix handled by kustomize).

Q10 — 8% — Services & Networking

Create HTTPRoute split-route in ex2-web on Gateway shop-gate: path prefix /shop, traffic split 90% to web-v1:5678 (same ns) and 10% to web-v2:5678 — which lives in namespace ex2-canary. Make the cross-namespace backend reference legal. Spec-level correctness counts.

Q11 — 7% — Services & Networking

In ex2-data: Pods role=app may send egress only to Pods role=db on TCP 80, plus DNS (TCP+UDP 53) anywhere. Everything else outbound denied. Policy name app-egress. Prove: app→db works, app→rogue times out.

Q12 — 5% — Services & Networking

Expose deployment web-v1 in ex2-web via a NodePort service web-np on port 5678, nodePort 30080. Prove reachability with curl from a node (docker exec).

Q13 — 6% — Workloads & Scheduling

Create PriorityClass exam-critical (value 100000, not default). In ex2-batch create deployment spread-app (nginx, 4 replicas, priorityClassName exam-critical) with a topologySpreadConstraint: maxSkew 1 over kubernetes.io/hostname, DoNotSchedule. End state: 2 pods per worker.

Q14 — 5% — Workloads & Scheduling

Create DaemonSet node-agent in ex2-batch (busybox, sleep 3600) that runs on all three nodes including the control-plane. Prove 3/3.

Q15 — 4% — Workloads & Scheduling

In ex2-side: deployment audit-app (1 replica): main container app (busybox) writes the date to /var/log/audit/audit.log every 5s; native sidecar shipper (busybox) tails it. Shared emptyDir. Prove heartbeats via kubectl logs ... -c shipper.

Q16 — 6% — Storage

PV keeper-pv is Released and must be reused without deleting the PV or its data. Make it bindable again and bind a new PVC new-claim (1Gi, RWO, class keeper) in ex2-store. Both must reach Bound. What field did you touch → /tmp/exam2/q16.txt.

Q17 — 4% — Storage

PVC wide-claim in ex2-store is Pending and will never bind. Diagnose; fix by changing the claim so it binds to PV narrow-pv. Root cause → /tmp/exam2/q17.txt.


VALIDATE (after the timer)

# Q1
kubectl -n default run probe --image=nginx --restart=Never && sleep 5 && kubectl get pod probe   # Running
cat /tmp/exam2/q1.txt                                        # kube-scheduler
# Q2
kubectl run dnstest --image=busybox:1.28 --restart=Never --rm -it -- nslookup kubernetes.default  # resolves
# Q3
kubectl --kubeconfig /tmp/exam2/developer.kubeconfig get nodes   # 3 nodes
# Q4
kubectl get node drills-worker2                              # Ready, SchedulingDisabled absent
kubectl -n ex2-neptune get pdb pinned-pdb -o jsonpath='{.spec}'  # loosened but still protective
kubectl -n ex2-neptune get deploy pinned                     # 2/2 (back after uncordon or rescheduled)
# Q5
kubectl -n ex2-front get deploy shop-ui                      # 2/2
# Q6
kubectl get cm marker -n default                             # NotFound (proof of restore)
docker exec drills-control-plane ls -la /root/etcd-v2.db     # exists
# Q7
helm list -n ex2-batch | grep shipper                        # deployed
kubectl -n ex2-batch get deploy -l app.kubernetes.io/name=shipper -o jsonpath='{.items[0].spec.replicas}'  # 2
# Q8
kubectl -n ex2-guard create deploy bad --image=nginx 2>&1 | grep -i owner    # denied, message mentions owner
kubectl -n ex2-guard create deploy good --image=nginx && kubectl -n ex2-guard label deploy good owner=me  # wrong order — see solutions
kubectl -n ex2-guard delete deploy good 2>/dev/null
# Q9
kubectl -n ex2-batch get deploy board -o jsonpath='{.spec.replicas}'         # 3
kubectl -n ex2-batch get cm | grep board-cfg                                 # board-cfg-<hash>
# Q10
kubectl -n ex2-web get httproute split-route -o yaml         # weights 90/10, ns on the v2 backendRef
kubectl -n ex2-canary get referencegrant -o yaml             # allows HTTPRoute/ex2-web -> Service
# Q11
DBIP=$(kubectl -n ex2-data get pod db -o jsonpath='{.status.podIP}')
RGIP=$(kubectl -n ex2-data get pod rogue -o jsonpath='{.status.podIP}')
kubectl -n ex2-data exec app -- curl -s -m 2 $DBIP >/dev/null && echo DB-OK
kubectl -n ex2-data exec app -- curl -s -m 2 $RGIP; echo EXIT=$?             # EXIT=28
# Q12
docker exec drills-worker curl -s -m 2 localhost:30080       # v1
# Q13
kubectl -n ex2-batch get pods -l app=spread-app -o wide      # 2 + 2 across workers
# Q14
kubectl -n ex2-batch get ds node-agent                       # DESIRED 3, READY 3
# Q15
kubectl -n ex2-side logs deploy/audit-app -c shipper | tail -3   # dated lines
kubectl -n ex2-side get pod -o jsonpath='{.items[0].spec.initContainers[0].restartPolicy}'  # Always
# Q16
kubectl get pv keeper-pv                                     # Bound to ex2-store/new-claim
cat /tmp/exam2/q16.txt                                       # claimRef
# Q17
kubectl -n ex2-store get pvc wide-claim                      # Bound
cat /tmp/exam2/q17.txt                                       # accessModes mismatch

SCORE SHEET

Q W Dom Pass Time Q W Dom Pass Time
1 8 TS 10 8 SN
2 7 TS 11 7 SN
3 6 TS 12 5 SN
4 5 TS 13 6 WS
5 4 TS 14 5 WS
6 8 CA 15 4 WS
7 6 CA 16 6 ST
8 6 CA 17 4 ST
9 5 CA

Pass: 66. Rebuild the range between attempts (kind delete cluster --name drills + range-up) — v2's damage is deep enough that RESET scripts lie.