docs: add Kubernetes CKS study notes

This commit is contained in:
2026-08-04 23:18:45 +02:00
parent a6ee7a2b07
commit 91a1849009
57 changed files with 8313 additions and 0 deletions

View File

@@ -0,0 +1,237 @@
# 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 nodeX` → `docker 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)
```bash
#!/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)
```bash
# 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.