264 lines
14 KiB
Markdown
264 lines
14 KiB
Markdown
# CKA Mock Exam Pack v1 — full 120-minute simulation
|
|
|
|
> **Format-matched to the real thing:** 17 weighted tasks, 120 minutes, 66% to pass.
|
|
> Domain distribution ≈ real curriculum: Troubleshooting ~30%, Cluster Architecture ~25%, Services & Networking ~20%, Workloads & Scheduling ~15%, Storage ~10%.
|
|
> Range: kind cluster `drills` (1 CP + 2 workers, Calico, Gateway API CRDs, metrics-server, helm).
|
|
>
|
|
> **Kind-isms (read once, then forget):** the real exam says `ssh nodeX` — here it's `docker exec -it drills-worker bash` (or `drills-worker2`, `drills-control-plane`). Real exam switches kubectl contexts per task — here it's one context; the discipline you're simulating is *reading the target of every task carefully*.
|
|
|
|
---
|
|
|
|
## EXAM PROTOCOL
|
|
|
|
1. Run the entire **MASTER SETUP** below. Do not read it — it contains spoilers (it breaks things on purpose). Pipe it to a file and execute blind:
|
|
copy the block into `/tmp/setup.sh`, then `bash /tmp/setup.sh > /tmp/setup.log 2>&1`.
|
|
2. Set a hard 120:00 timer. No pauses, no notes, no Claude.
|
|
3. Solve in any order. Flag anything stuck >8 min and move on. Reserve the last 10 minutes for a verification lap.
|
|
4. When the timer dies: run the **VALIDATION** blocks, fill the score sheet, compute your percentage from the weights.
|
|
5. Paste score sheet + your command history to Claude for the autopsy.
|
|
|
|
---
|
|
|
|
## MASTER SETUP (do not read — paste and run)
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
set +e
|
|
# --- namespaces
|
|
for ns in ex-neptune ex-mars ex-venus ex-pluto ex-saturn ex-mercury ex-titan ex-io ex-rescue; do kubectl create ns $ns; done
|
|
|
|
# Q1: kill kubelet on worker2
|
|
docker exec drills-worker2 systemctl stop kubelet
|
|
|
|
# Q5: broken deployment — missing CM + bad image tag
|
|
kubectl -n ex-neptune create deploy web-portal --image=nginx:1.99-fake --replicas=2
|
|
kubectl -n ex-neptune set env deploy/web-portal --from=configmap/portal-config 2>/dev/null
|
|
kubectl -n ex-neptune patch deploy web-portal --type=json -p='[{"op":"add","path":"/spec/template/spec/containers/0/envFrom","value":[{"configMapRef":{"name":"portal-config"}}]}]'
|
|
|
|
# Q6: service with selector mismatch
|
|
kubectl -n ex-mars create deploy api-backend --image=nginx --replicas=2
|
|
kubectl -n ex-mars label deploy api-backend app=api-backend --overwrite
|
|
kubectl -n ex-mars apply -f - <<'EOF'
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata: {name: api-svc, namespace: ex-mars}
|
|
spec:
|
|
selector: {app: api-backent}
|
|
ports: [{port: 80, targetPort: 80}]
|
|
EOF
|
|
|
|
# Q16: crashing pod
|
|
kubectl -n ex-rescue run data-proc --image=busybox --restart=Never -- sh -c 'echo "FATAL: config /etc/proc/settings.ini not found" >&2; exit 1'
|
|
|
|
# Q17: broken static pod on worker
|
|
docker exec drills-worker bash -c 'mkdir -p /etc/kubernetes/manifests && cat > /etc/kubernetes/manifests/edge-cache.yaml <<EOF
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: edge-cache}
|
|
spec:
|
|
containers:
|
|
- name: cache
|
|
image: redis:7-alpinee
|
|
ports: [{containerPort: 6379}]
|
|
EOF'
|
|
|
|
# Q4: gateway for httproute task
|
|
kubectl apply -f - <<'EOF'
|
|
apiVersion: gateway.networking.k8s.io/v1
|
|
kind: GatewayClass
|
|
metadata: {name: exam-class}
|
|
spec: {controllerName: example.com/exam}
|
|
---
|
|
apiVersion: gateway.networking.k8s.io/v1
|
|
kind: Gateway
|
|
metadata: {name: main-gate, namespace: ex-venus}
|
|
spec:
|
|
gatewayClassName: exam-class
|
|
listeners: [{name: http, port: 80, protocol: HTTP}]
|
|
EOF
|
|
kubectl -n ex-venus create deploy web-v1 --image=hashicorp/http-echo -- /http-echo -text=v1 -listen=:5678
|
|
kubectl -n ex-venus create deploy web-v2 --image=hashicorp/http-echo -- /http-echo -text=v2 -listen=:5678
|
|
kubectl -n ex-venus expose deploy web-v1 --port=5678
|
|
kubectl -n ex-venus expose deploy web-v2 --port=5678
|
|
|
|
# Q3: netpol targets
|
|
kubectl -n ex-pluto run frontend --image=nginx --labels=role=frontend --port=80
|
|
kubectl -n ex-pluto run backend --image=nginx --labels=role=backend --port=80
|
|
kubectl -n ex-pluto run cache --image=nginx --labels=role=cache --port=80
|
|
|
|
# Q7: rbac targets
|
|
kubectl -n ex-saturn create sa deploy-bot
|
|
|
|
# Q11: rollout history to inspect
|
|
kubectl -n ex-mercury create deploy release-app --image=nginx:1.25
|
|
kubectl -n ex-mercury set image deploy/release-app nginx=nginx:1.26
|
|
kubectl -n ex-mercury set image deploy/release-app nginx=nginx:1.27-bogus
|
|
|
|
# Q12: scheduling
|
|
kubectl taint node drills-worker tier=critical:NoSchedule --overwrite
|
|
kubectl label node drills-worker zone=east --overwrite
|
|
kubectl label node drills-worker2 zone=west --overwrite
|
|
|
|
# Q13/Q14: storage
|
|
kubectl apply -f - <<'EOF'
|
|
apiVersion: storage.k8s.io/v1
|
|
kind: StorageClass
|
|
metadata: {name: local-manual}
|
|
provisioner: kubernetes.io/no-provisioner
|
|
volumeBindingMode: WaitForFirstConsumer
|
|
EOF
|
|
docker exec drills-worker mkdir -p /opt/vol-alpha
|
|
kubectl -n ex-io create deploy metrics-writer --image=busybox -- sleep 3600
|
|
echo "SETUP DONE"
|
|
```
|
|
|
|
---
|
|
|
|
## THE EXAM — 120:00 starts now
|
|
|
|
### Q1 — 8% — Troubleshooting
|
|
Node `drills-worker2` is `NotReady`. Find the cause, fix it, make the node `Ready` again. Write the name of the failed component into `/tmp/exam/q1-component.txt`.
|
|
|
|
### Q2 — 8% — Cluster Architecture
|
|
Create a snapshot of the cluster's etcd database and save it **inside the control-plane node** at `/root/etcd-backup.db`. Then verify the snapshot and write the number of keys/revision info into `/tmp/exam/q2-status.txt` on your host.
|
|
*(Hint allowed by the real exam's phrasing: certs live where kubeadm puts them. Enter the CP with `docker exec -it drills-control-plane bash`.)*
|
|
|
|
### Q3 — 6% — Services & Networking
|
|
In `ex-pluto`: Pods `role=backend` must accept ingress **only** from Pods `role=frontend` in the same namespace on port 80. Traffic from `role=cache` (and everything else) must be denied. Name the policy `backend-shield`. Egress stays open.
|
|
|
|
### Q4 — 7% — Services & Networking
|
|
In `ex-venus`, attached to the existing Gateway `main-gate`, create HTTPRoute `release-route`: all requests to path prefix `/` go to `web-v1:5678`, **except** requests with header `X-Version: v2` (exact) which go to `web-v2:5678`. Spec-level correctness counts (no dataplane on this range).
|
|
|
|
### Q5 — 7% — Troubleshooting
|
|
Deployment `web-portal` in `ex-neptune` has 0/2 ready. There are **two independent faults**. Fix both. Desired end state: 2/2 Ready, env sourced from a ConfigMap named `portal-config` containing key `MODE=production`.
|
|
|
|
### Q6 — 6% — Troubleshooting
|
|
Service `api-svc` in `ex-mars` returns no endpoints. Diagnose and repair **the service** (the deployment is correct). Prove endpoints exist afterward.
|
|
|
|
### Q7 — 7% — Cluster Architecture
|
|
In `ex-saturn`, using **imperative commands only**: allow ServiceAccount `deploy-bot` to create, list, and delete Deployments in `ex-saturn` — nothing else, namespace-scoped. Verify with `kubectl auth can-i` (one positive, one negative check) and save both outputs to `/tmp/exam/q7-cani.txt`.
|
|
|
|
### Q8 — 6% — Cluster Architecture
|
|
Using Helm: install chart `bitnami/redis` as release `cache-layer` in namespace `ex-titan`, chart version **one minor behind latest**, with `architecture=standalone` and auth disabled (`auth.enabled=false`) set via CLI. Then upgrade to the latest chart version preserving both overrides. Record `helm history` output to `/tmp/exam/q8-history.txt`.
|
|
|
|
### Q9 — 4% — Cluster Architecture
|
|
A base kustomization exists at `/tmp/drill-kz/base` (from drill pack v1; recreate it if you cleaned it). Create overlay `/tmp/drill-kz/overlays/exam` that deploys into namespace `ex-titan` with name prefix `exam-` and image `nginx:1.27`. Apply it with `-k`.
|
|
|
|
### Q10 — 5% — Workloads & Scheduling
|
|
Create HPA `writer-hpa` in `ex-io` for deployment `metrics-writer`: min 1, max 4, target 70% average CPU. Scale-down stabilization window 240s. (The deployment lacks CPU requests — part of the task is making the HPA functional.)
|
|
|
|
### Q11 — 5% — Workloads & Scheduling
|
|
Deployment `release-app` in `ex-mercury` is stuck mid-rollout on a bad image. Roll it back to the **previous working revision**, then record: current image and the revision number you rolled back to → `/tmp/exam/q11-rollback.txt`.
|
|
|
|
### Q12 — 5% — Workloads & Scheduling
|
|
Create deployment `edge-daemon` (image `busybox`, `sleep 3600`, 2 replicas) in namespace `ex-io` that runs **only on `drills-worker`**: tolerate its taint and pin via nodeAffinity on the `zone=east` label. Both replicas must land there.
|
|
|
|
### Q13 — 5% — Storage
|
|
Create PV `vol-alpha`: 2Gi, RWO, storageClassName `local-manual`, hostPath `/opt/vol-alpha`. Create PVC `claim-alpha` in `ex-io` requesting 1Gi, same class. Explain in one line inside `/tmp/exam/q13-why.txt` why the PVC is Pending at this point. Then mount it in a pod `vol-user` (nginx) at `/data` and confirm Bound.
|
|
|
|
### Q14 — 5% — Storage
|
|
Make `local-manual` the **default** StorageClass of the cluster. Then create PVC `claim-default` in `ex-io` with **no storageClassName** and prove (one command's output to `/tmp/exam/q14-proof.txt`) that it was assigned `local-manual` automatically.
|
|
|
|
### Q15 — 5% — Services & Networking
|
|
In `ex-mars`, create an Ingress `api-ingress` (imperative generator allowed): host `api.exam.local`, path `/v1` (Prefix) → service `api-svc:80`, ingressClassName `nginx`. The controller isn't installed — spec-level correctness counts. Ensure pathType is explicitly `Prefix`.
|
|
|
|
### Q16 — 6% — Troubleshooting
|
|
Pod `data-proc` in `ex-rescue` is failing. Save **its complete logs** (the error line included) to `/tmp/exam/q16-logs.txt`, then recreate the pod so it runs successfully with the same name and image, sleeping 1 hour instead of crashing.
|
|
|
|
### Q17 — 5% — Troubleshooting
|
|
A static pod `edge-cache` was configured on node `drills-worker` but never appears in `kubectl get pods`. Enter the node, find the fault, fix it, and get the mirror pod visible and Running in the `default` namespace.
|
|
|
|
---
|
|
|
|
## VALIDATION (run only after the timer — expected outputs in comments)
|
|
|
|
```bash
|
|
EXAM_DIR="${EXAM_DIR:-./exam}"
|
|
mkdir -p "$EXAM_DIR"
|
|
# Q1
|
|
kubectl get node drills-worker2 # Ready
|
|
cat "$EXAM_DIR/q1-component.txt" # kubelet
|
|
# Q2
|
|
docker exec drills-control-plane ls -la /root/etcd-backup.db # exists, >1MB
|
|
cat "$EXAM_DIR/q2-status.txt" # snapshot status table/json
|
|
# Q3
|
|
PIP=$(kubectl -n ex-pluto get pod backend -o jsonpath='{.status.podIP}')
|
|
kubectl -n ex-pluto exec frontend -- curl -s -m 2 $PIP | head -1 # HTML (allowed)
|
|
kubectl -n ex-pluto exec cache -- curl -s -m 2 $PIP; echo EXIT=$? # EXIT=28 (blocked)
|
|
# Q4
|
|
kubectl -n ex-venus get httproute release-route -o yaml
|
|
# header match in same rule-entry as its path; default rule present; parentRef=main-gate
|
|
# Q5
|
|
kubectl -n ex-neptune get deploy web-portal # 2/2
|
|
kubectl -n ex-neptune get cm portal-config -o jsonpath='{.data.MODE}' # production
|
|
# Q6
|
|
kubectl -n ex-mars get endpoints api-svc # two pod IPs
|
|
# Q7
|
|
cat "$EXAM_DIR/q7-cani.txt" # yes + no
|
|
kubectl auth can-i delete deployments --as=system:serviceaccount:ex-saturn:deploy-bot -n ex-saturn # yes
|
|
kubectl auth can-i create pods --as=system:serviceaccount:ex-saturn:deploy-bot -n ex-saturn # no
|
|
# Q8
|
|
helm list -n ex-titan # cache-layer, latest chart
|
|
helm get values cache-layer -n ex-titan # architecture: standalone, auth.enabled: false
|
|
cat "$EXAM_DIR/q8-history.txt" # rev1 + rev2
|
|
# Q9
|
|
kubectl -n ex-titan get deploy exam-portal -o jsonpath='{.spec.template.spec.containers[0].image}' # nginx:1.27
|
|
# Q10
|
|
kubectl -n ex-io get hpa writer-hpa -o yaml | grep -E 'averageUtilization|stabilizationWindowSeconds'
|
|
# 70 + 240 under scaleDown; deploy now has cpu requests
|
|
# Q11
|
|
kubectl -n ex-mercury get deploy release-app -o jsonpath='{.spec.template.spec.containers[0].image}' # nginx:1.26
|
|
cat "$EXAM_DIR/q11-rollback.txt"
|
|
# Q12
|
|
kubectl -n ex-io get pods -l app=edge-daemon -o wide # both on drills-worker
|
|
# Q13
|
|
kubectl -n ex-io get pvc claim-alpha # Bound
|
|
cat "$EXAM_DIR/q13-why.txt" # WaitForFirstConsumer explanation
|
|
# Q14
|
|
kubectl get sc local-manual -o jsonpath='{.metadata.annotations.storageclass\.kubernetes\.io/is-default-class}' # true
|
|
cat "$EXAM_DIR/q14-proof.txt" # claim-default → local-manual
|
|
# Q15
|
|
kubectl -n ex-mars get ingress api-ingress -o jsonpath='{.spec.rules[0].http.paths[0].pathType}' # Prefix
|
|
# Q16
|
|
grep FATAL "$EXAM_DIR/q16-logs.txt" # the error line
|
|
kubectl -n ex-rescue get pod data-proc # Running
|
|
# Q17
|
|
kubectl get pod edge-cache-drills-worker # Running
|
|
```
|
|
|
|
> **Configurable path:** the validation block reads/writes under `$EXAM_DIR` (defaults to `/tmp/exam`). Override by exporting it before running, e.g. `EXAM_DIR=/tmp/exam-attempt2 bash -c '...'` or `export EXAM_DIR=/tmp/exam-attempt2` beforehand. The exam task descriptions above still reference the literal `/tmp/exam/...` paths — either keep writing there during the exam, or export the same `EXAM_DIR` value before starting the timer and use `$EXAM_DIR/...` for every task's output path too.
|
|
|
|
## SCORE SHEET
|
|
| Q | Weight | Domain | Pass? | Time |
|
|
|---|---|---|---|---|
|
|
| 1 | 8 | TS | | |
|
|
| 2 | 8 | CA | | |
|
|
| 3 | 6 | SN | | |
|
|
| 4 | 7 | SN | | |
|
|
| 5 | 7 | TS | | |
|
|
| 6 | 6 | TS | | |
|
|
| 7 | 7 | CA | | |
|
|
| 8 | 6 | CA | | |
|
|
| 9 | 4 | CA | | |
|
|
| 10 | 5 | WS | | |
|
|
| 11 | 5 | WS | | |
|
|
| 12 | 5 | WS | | |
|
|
| 13 | 5 | ST | | |
|
|
| 14 | 5 | ST | | |
|
|
| 15 | 5 | SN | | |
|
|
| 16 | 6 | TS | | |
|
|
| 17 | 5 | TS | | |
|
|
|
|
**Score = sum of passed weights. Pass line: 66.**
|
|
|
|
## RESET (between attempts)
|
|
```bash
|
|
for ns in ex-neptune ex-mars ex-venus ex-pluto ex-saturn ex-mercury ex-titan ex-io ex-rescue; do kubectl delete ns $ns --wait=false; done
|
|
kubectl delete gatewayclass exam-class; kubectl delete pv vol-alpha
|
|
kubectl taint node drills-worker tier=critical:NoSchedule- 2>/dev/null
|
|
kubectl label node drills-worker zone- ; kubectl label node drills-worker2 zone-
|
|
kubectl patch sc local-manual -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
|
|
docker exec drills-worker rm -f /etc/kubernetes/manifests/edge-cache.yaml
|
|
docker exec drills-worker2 systemctl start kubelet 2>/dev/null
|
|
kubectl delete pod edge-cache-drills-worker --force 2>/dev/null
|
|
``` |