CKA Mock Exam · Set 3
Your third full sitting, and the one built to expose a specific weakness: can you actually troubleshoot a cluster you can't fully trust, under a clock, with nothing but the tools the real exam allows? Twelve performance-based tasks in one unbroken 120-minute block, weighted like Set 1 and Set 2 across all five CKA domains — but this time Troubleshooting carries 39 of the 100 points, well above its already-large 30% exam weight, because that's the domain most candidates under-rehearse. Five of the twelve tasks hand you a cluster that is subtly, specifically broken — a certificate stuck mid-rotation, a DNS loop, a control-plane node that won't even let you run kubectl — and grade only the state you leave behind, exactly like the real thing. The other seven tasks are fresh build-from-scratch work across Cluster Architecture, Workloads, Networking and Storage, none of it repeated from Set 1 or Set 2. Every task has an objective done when check and a worked solution folded away until you've actually attempted it. Sit it honestly, total the sheet, and let the domain breakdown — not the raw score — choose what you study next.
Your first two practice fire drills were in a school you know well — you'd already walked every hallway. This one drops you in a building you've never seen, with the added twist that half the doors don't open the way doors normally do, and your job is to figure out why before you can even get to the fire. That's what leaning into Troubleshooting means here: it's not that the questions get meaner, it's that more of them start with something already broken instead of starting from a blank room. The real exam is exactly this shape — more time spent figuring out what's wrong than building something new from scratch — so a practice paper that doesn't lean the same way would be practicing the easier, less common part of the actual test.
Before you start — exam conditions
☺ Like you're 10: A fire drill only teaches you something if you run it for real — one timer, no peeking at the answer key, nobody standing next to you whispering the fix.
Build a throwaway cluster the night before — kubeadm on disposable VMs is closest to the real exam surface (you'll want real control-plane nodes for T5 and C1, which a single-node kind cluster can't fully exercise); a multi-node kind or minikube setup with at least one extra worker will cover everything else. Install a CNI that supports NetworkPolicy (Calico or Cilium — see Calico / Cilium) and ingress-nginx ahead of time; N1 and N2 need both. Start one 120-minute timer and don't pause it — a cluster that eats eight minutes of your time being uncooperative is itself realistic data, not an excuse to stop the clock. Keep only kubernetes.io/docs, kubernetes.io/blog, and whatever's in-cluster (kubectl explain, --help, existing objects) open — no search engine, no AI assistant, no notes from Set 1 or Set 2. Read all twelve tasks first; five minutes spent doing that is the highest-return five minutes of the sitting, because it's the only way to know which of the five troubleshooting tasks is going to eat the most time before you've committed to one.
When a task passes its budgeted minutes below with no passing done-when check, stop, write one line about where you stalled, leave your partial work in place, and move to the next task. This matters more on a troubleshooting-heavy paper than any other kind: a broken cluster can absorb unlimited time if you let it, and the real exam's clock does not care how close you were. Timmy's version, unchanged since Set 1: you are not paid to finish tasks, you are paid to bank points.
Your time budget and domain weights
☺ Like you're 10: The clock gets split up ahead of time, the same way the points do — and this time the biggest slice by far goes to fixing things, not building them.
Compare this table with the five domain weights on the study plan: Cluster Architecture, Workloads, Networking and Storage are all slightly below their real exam weight on this specific paper, and Troubleshooting is well above it. That's deliberate for one sitting, not a claim about the real exam's own split — sit Set 1 or Set 2 if you want a paper weighted closer to the published curriculum.
| Domain | Tasks | Points | Share of paper | Minutes |
|---|---|---|---|---|
| 🦊 Troubleshooting | T1–T5 | 39 | 39% (real exam: 30%) | 43 |
| 🦉 Cluster Architecture, Installation & Configuration | C1–C2 | 22 | 22% (25%) | 20 |
| 🦫 Workloads & Scheduling | W1–W2 | 13 | 13% (15%) | 13 |
| 🐦 Services & Networking | N1–N2 | 16 | 16% (20%) | 16 |
| 🐘 Storage | S1 | 10 | 10% (10%) | 8 |
| Total | 12 tasks | 100 | 100% | 100 + 5 read + 15 verify |
Troubleshooting — T1 to T5 (39 points)
☺ Like you're 10: Every one of these five starts with something already wrong — your job isn't to build, it's to work out why the thing in front of you doesn't do what it's supposed to.
This block leans on the Troubleshooting blueprint page and a troubleshooting methodology — describe before logs, logs before exec, node before app — and on the muscle built in the broken-cluster, stuck-pod, networking-failure and failed-upgrade drills.
T1 · kubectl logs/exec fail against one node — a stuck kubelet-serving CSR (12 pts)
kubectl logs and kubectl exec against any pod scheduled on worker-3 fail with an x509 certificate error, while kubectl get nodes still shows worker-3 as Ready — its heartbeats, which use a different certificate, are unaffected.
Done when: kubectl get csr shows no CSR in Pending state for worker-3, and kubectl logs against a pod on that node succeeds.
Show the worked solution
kubectl get csr # find the Pending one
kubectl describe csr <name> # confirm: requestor system:node:worker-3,
# signerName kubernetes.io/kubelet-serving
kubectl certificate approve <name>
kubectl get csr <name> # Approved,Issued
kubectl logs -n default <a-pod-on-worker-3> --tail=1 # works now, no kubelet restart neededWhy: two different certificates carry two different jobs. The kubelet client cert (signer kubernetes.io/kube-apiserver-client-kubelet) is what the node uses to talk to the API server — kubeadm clusters auto-approve its routine renewal, which is why the node stayed Ready. The kubelet serving cert (signer kubernetes.io/kubelet-serving) is what the API server uses to call back into the kubelet for logs, exec, and metrics-server scraping — and that one is not auto-approved by the built-in controller on most kubeadm installs unless an add-on like kubelet-csr-approver is running. Once it expires with nothing approving the renewal, the node itself looks perfectly healthy while a whole class of operations against it quietly breaks.
T2 · A healthy app in CrashLoopBackOff — the probe is checking the wrong thing (6 pts)
web deployment pods are stuck CrashLoopBackOff. kubectl logs shows the app starting cleanly and serving on port 8000; nothing in the app log looks wrong.
Done when: the pods reach Running and stay there — restart count stops climbing for at least two probe intervals.
Show the worked solution
kubectl describe pod <pod> | grep -A3 Liveness # Liveness probe failed: connection refused, port 8080 kubectl edit deploy web # livenessProbe.httpGet.port: 8080 → 8000 kubectl rollout status deploy/web
Why: describe pod's Events, not the container's own logs, is where a probe failure shows up — the app never gets a chance to log anything wrong because nothing is wrong with it. The liveness probe was pointed at a port the container never listens on, so kubelet kills a perfectly working container on every check interval, forever. This is exactly the kind of fix that's invisible from application logs alone, which is why the methodology page puts describe before logs in the check order.
T3 · Pods stuck Pending on a dedicated node pool (6 pts)
Three nodes are labeled and tainted dedicated=batch:NoSchedule for the batch workload pool, correctly, for other tenants' protection. New batch-worker deployment pods that are supposed to run there are stuck Pending.
Done when: all batch-worker replicas are Running and scheduled only on the three tainted nodes — not spread onto any other node in the cluster.
Show the worked solution
# kubectl describe pod shows: 0/6 nodes are available: 3 node(s) had untolerated taint
# {dedicated: batch}. Fix the Deployment's pod template — do NOT remove the taint.
spec:
template:
spec:
tolerations:
- key: dedicated
operator: Equal
value: batch
effect: NoSchedule
nodeSelector:
dedicated: batch # tolerating the taint only says "may" — this says "must"Why: a taint and a toleration solve one direction of a two-way problem — the taint keeps other workloads off the pool, and a matching toleration is what lets batch-worker back in, but tolerating a taint never requires scheduling there. Without the accompanying nodeSelector (or a nodeAffinity), a tolerating pod is merely eligible for the tainted nodes and could just as easily land elsewhere, which isn't what "dedicated pool" means. Removing the taint instead would fix these pods and silently break the isolation every other tenant on that pool depends on — the taint is not the bug.
T4 · CoreDNS crash-looping with a Loop error (8 pts)
CoreDNS pods are restarting repeatedly. kubectl logs -n kube-system -l k8s-app=kube-dns shows a line ending Loop (127.0.0.1:53153 -> :53) detected for zone ".", see https://coredns.io/plugins/loop#troubleshooting.
Done when: CoreDNS pods are Running with a stable restart count, and kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default resolves successfully.
Show the worked solution
kubectl -n kube-system get cm coredns -o yaml # Corefile: forward . /etc/resolv.conf
cat /etc/resolv.conf # on the node — nameserver points back
# at the cluster DNS Service's own ClusterIP
kubectl -n kube-system edit cm coredns
# forward . /etc/resolv.conf → forward . 8.8.8.8 1.1.1.1
kubectl -n kube-system rollout restart deploy corednsWhy: CoreDNS's loop plugin sends a canary query and kills the process if the answer comes back to itself — a deliberate safety valve, not the bug itself. The actual bug lives one layer down: the node's own /etc/resolv.conf was pointed at the cluster's DNS Service IP (a cloud-init or DHCP misconfiguration), so forward . /etc/resolv.conf hands CoreDNS a query loop back to itself. The fix is never to remove or disable the loop plugin — that just hides a real infinite loop behind silence — the fix is to stop the Corefile's upstream from pointing at itself, either by fixing the node's resolver or by hardcoding a real external upstream. See networking & the CNI for the rest of CoreDNS's resolution path.
T5 · A typo in a static pod manifest locks out kubectl itself (7 pts)
After a routine flag change on the control-plane node, kubectl commands from your workstation all fail with connection refused. You still have SSH access to the control-plane node.
Done when: crictl ps on the control-plane node shows the kube-apiserver container Running and not restarting, and kubectl get nodes from your workstation succeeds again.
Show the worked solution
# kubectl itself is unusable while the API server is down — work on the node directly sudo crictl ps -a | grep apiserver # container repeatedly exiting sudo crictl logs <container-id> # Error: unknown flag: --etcd-server=https://127.0.0.1:2379 (missing the trailing "s") sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml # --etcd-server=... → --etcd-servers=... # no "kubectl apply" needed or possible here — the kubelet's static pod manager # watches this exact file and recreates the pod automatically on save sudo crictl ps | grep apiserver # Running, stable kubectl get nodes
Why: the four control-plane components on a kubeadm cluster are static pods — manifests read directly off disk by each control-plane node's own kubelet, not objects stored in etcd, precisely so the cluster can bootstrap and self-heal its own control plane without a working API server. That also means the normal toolbox is briefly unavailable: no kubectl, no kubectl logs, no kubectl edit — the fix has to happen with crictl and a text editor over SSH. Once the YAML in /etc/kubernetes/manifests/ is valid again, nothing needs to be "applied": the kubelet's file watcher notices the change and restarts the pod on its own, usually within seconds.
"None of these five ever show up in my dashboards as 'certificate' or 'static pod' anything. What I see is: my deploy is stuck, or my app's logs tab in our internal tool is just blank for one specific pod, or DNS lookups inside my service are randomly slow before they fail outright. The actual cause is always two or three layers below what I can see from the app side — which is exactly why somebody on the platform team has to be fluent in this stuff. I am never going to be the one who finds a Loop error in a CoreDNS log.”
Cluster Architecture, Installation & Configuration — C1 to C2 (22 points)
☺ Like you're 10: These two are the "keep the whole cluster's memory safe" and "give people only the keys they actually need" tasks — both are on almost every real CKA sitting.
Background: the D1 blueprint page, control-plane internals, and RBAC & admission control.
C1 · Snapshot and restore etcd (13 pts)
Your task: take an etcdctl snapshot of the running cluster's etcd, then restore it into a fresh data directory and repoint the etcd static pod at that directory — proving you can recover the cluster's entire state, not just back it up.
Done when: ETCDCTL_API=3 etcdctl --write-out=table snapshot status /tmp/etcd-backup.db shows a valid revision count, and after the restore, kubectl get all -A against the cluster still returns the same objects it did before the snapshot.
Show the worked solution
ETCDCTL_API=3 etcdctl snapshot save /tmp/etcd-backup.db \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key ETCDCTL_API=3 etcdctl --write-out=table snapshot status /tmp/etcd-backup.db ETCDCTL_API=3 etcdctl snapshot restore /tmp/etcd-backup.db \ --data-dir=/var/lib/etcd-restored sudo vi /etc/kubernetes/manifests/etcd.yaml # hostPath for the etcd-data volume: /var/lib/etcd → /var/lib/etcd-restored # kubelet's static pod manager restarts etcd automatically once the manifest is saved sudo crictl ps | grep etcd kubectl get all -A
Why: etcd, not any Kubernetes object itself, is the cluster's single source of truth — every Deployment, Secret and ConfigMap is a record inside it, so restoring the cluster genuinely means restoring etcd, not re-applying manifests. snapshot restore deliberately writes to a new data directory rather than overwriting the live one in place, so the only way the restored data actually takes effect is by pointing etcd's static pod manifest — via its hostPath volume — at that new directory, the same self-healing mechanism T5 relies on. This exact task, worded slightly differently, appears on real CKA sittings often enough that it's worth being able to do from memory, not from a lookup, inside your study plan's readiness checklist.
C2 · Scope a CI service account with RBAC, not cluster admin (9 pts)
A CI pipeline currently deploys using a ClusterRoleBinding to cluster-admin — far more than it needs. It only ever creates, updates and reads Deployments and Pods, and only inside the build namespace.
Done when: kubectl auth can-i create deployments -n build --as=system:serviceaccount:build:ci-deployer returns yes, and kubectl auth can-i list secrets -A --as=system:serviceaccount:build:ci-deployer returns no.
Show the worked solution
apiVersion: v1
kind: ServiceAccount
metadata: { name: ci-deployer, namespace: build }
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: { name: deployer, namespace: build }
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: { name: deployer-binding, namespace: build }
subjects:
- kind: ServiceAccount
name: ci-deployer
namespace: build
roleRef: { kind: Role, name: deployer, apiGroup: rbac.authorization.k8s.io }kubectl delete clusterrolebinding ci-cluster-admin # remove the over-broad binding kubectl apply -f rbac.yaml kubectl auth can-i create deployments -n build --as=system:serviceaccount:build:ci-deployer # yes kubectl auth can-i list secrets -A --as=system:serviceaccount:build:ci-deployer # no
Why: a namespaced Role plus a RoleBinding in that same namespace is what keeps a grant contained — a ClusterRole bound with a ClusterRoleBinding reaches every namespace in the cluster, which is exactly the blast radius a CI pipeline compromise shouldn't have. kubectl auth can-i --as is the fast way to prove a grant's boundary without ever creating a real token — it re-runs the same authorization check the API server would, impersonating the subject. See the RBAC-hardening drill for the fuller version of this exercise, including aggregated ClusterRoles.
Workloads & Scheduling — W1 to W2 (13 points)
☺ Like you're 10: One teaches the cluster to grow and shrink a service on its own; the other teaches it to spread copies out instead of stacking them all in one place.
Background: the D2 blueprint page, scheduling & resource management, and autoscaling.
W1 · Autoscale a Deployment on CPU (7 pts)
The web Deployment (from T2, now healthy) needs to scale between 2 and 8 replicas, targeting 60% average CPU utilization.
Done when: kubectl get hpa web shows a real TARGETS percentage (not <unknown>) and the configured min/max replicas.
Show the worked solution
# the trap: metrics-server can't compute a percentage without a requests.cpu baseline kubectl set resources deploy web --requests=cpu=200m --limits=cpu=500m kubectl autoscale deploy web --cpu-percent=60 --min=2 --max=8 kubectl get hpa web -w
Why: an HPA's CPU percentage is always relative to the pod's requests.cpu — no requests set means no denominator, and kubectl get hpa sits at <unknown> forever no matter how correct the HPA object itself is. This is the single most common reason a from-scratch HPA task fails on the real exam: the manifest is right and the metric is simply undefined.
W2 · Spread replicas evenly across zones (6 pts)
web currently has all its replicas landing on nodes in a single zone — a single zone outage would take the whole service down. Nodes are labeled topology.kubernetes.io/zone across three zones.
Done when: after scaling to 6 replicas, no zone holds more than one extra pod compared to any other zone (kubectl get pods -o wide cross-referenced with node zone labels).
Show the worked solution
spec:
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels: { app: web }Why: maxSkew: 1 is the actual promise — the busiest zone and the least-busy zone can never differ by more than one pod — and DoNotSchedule makes that promise hard rather than advisory, refusing to place a pod that would break it rather than merely preferring not to. This is a different tool from a podAntiAffinity, which reasons about pairs of pods, not about the whole set's balance across a topology key at once.
Services & Networking — N1 to N2 (16 points)
☺ Like you're 10: One task teaches the cluster to say no by default and yes to one specific visitor; the other teaches it to send different visitors to different rooms based on the address they asked for.
Background: the D3 blueprint page and networking & the CNI.
N1 · Default-deny ingress, then allow one caller (8 pts)
The payments namespace currently allows ingress from anywhere. Lock it down so only pods labeled role: frontend, in the same namespace, can reach it on tcp/8443 — everything else should be refused.
Done when: a request from a pod labeled role: frontend to a payments pod on 8443 succeeds, and the same request from an unlabeled pod times out.
Show the worked solution
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny-ingress, namespace: payments }
spec:
podSelector: {}
policyTypes: [Ingress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: allow-frontend, namespace: payments }
spec:
podSelector: {}
policyTypes: [Ingress]
ingress:
- from: [{ podSelector: { matchLabels: { role: frontend } } }]
ports: [{ protocol: TCP, port: 8443 }]Why: an empty podSelector: {} means "every pod in this namespace," so the first policy is a blanket deny; NetworkPolicies are additive, so the second policy doesn't need to repeat the deny — it just opens one specific hole in it. Note the from selector here has no namespaceSelector, so it only ever matches role: frontend pods inside payments itself; reaching across namespaces would need that selector added explicitly. Requires a CNI that enforces NetworkPolicy — Calico or Cilium, never the default bridge alone.
N2 · Route two paths on one host through Ingress (8 pts)
Two Services, api-svc and web-svc, both listening on port 80, need to be reachable behind one Ingress on host shop.internal — /api to api-svc, / to web-svc.
Done when: curl -H "Host: shop.internal" http://<ingress-controller-ip>/api/health and curl -H "Host: shop.internal" http://<ingress-controller-ip>/ reach the correct Service respectively.
Show the worked solution
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
annotations: { nginx.ingress.kubernetes.io/rewrite-target: /$2 }
spec:
ingressClassName: nginx
rules:
- host: shop.internal
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend: { service: { name: api-svc, port: { number: 80 } } }
- path: /
pathType: Prefix
backend: { service: { name: web-svc, port: { number: 80 } } }Why: path ordering and specificity both matter — the more specific /api rule has to be evaluated before the catch-all / prefix, or every request would match web-svc first. The regex-capture-and-rewrite pair is ingress-nginx's specific mechanism for stripping the /api prefix before it reaches a backend that doesn't expect it in its own routes; a plain pathType: Prefix on /api works too if api-svc is written to expect the prefix itself. See the ingress-nginx tool guide for the annotation's exact behavior across controller versions.
Storage — S1 (10 points)
☺ Like you're 10: A PVC sitting at "Pending" isn't always broken — sometimes it's doing exactly what you told it to do, and this task is about knowing the difference.
Background: the D4 blueprint page and storage & the CSI.
S1 · A PVC that's correctly Pending, until it isn't (10 pts)
Create a StorageClass named fast-local using a provisioner with volumeBindingMode: WaitForFirstConsumer, then a PVC requesting 1Gi against it. Explain — and prove — why kubectl get pvc shows Pending immediately after creation, then get it bound.
Done when: the PVC shows Pending right after creation with no Pod referencing it, and shows Bound once a Pod that mounts it exists.
Show the worked solution
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata: { name: fast-local }
provisioner: rancher.io/local-path # or your cluster's own dynamic provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: data-claim }
spec:
storageClassName: fast-local
accessModes: [ReadWriteOnce]
resources: { requests: { storage: 1Gi } }kubectl apply -f sc-and-pvc.yaml
kubectl get pvc data-claim # STATUS Pending — correct, nothing consumes it yet
kubectl run reader --image=busybox --restart=Never --overrides='
{"spec":{"containers":[{"name":"reader","image":"busybox","command":["sleep","3600"],
"volumeMounts":[{"name":"d","mountPath":"/data"}]}],
"volumes":[{"name":"d","persistentVolumeClaim":{"claimName":"data-claim"}}]}}'
kubectl get pvc data-claim # STATUS Bound, now that a Pod references itWhy: WaitForFirstConsumer deliberately delays binding — and often the underlying volume's own provisioning — until a Pod that actually uses the claim is scheduled, so the provisioner can place the volume in the same zone or node as that Pod rather than guessing. A candidate who doesn't know this reflexively "fixes" a perfectly correct Pending PVC by deleting and recreating it, which changes nothing, because the actual missing ingredient was never the PVC — it was a consumer.
Score yourself
☺ Like you're 10: Add up the points, but look harder at whether your misses are clustered in Troubleshooting — that's the domain this whole paper was built to test.
Mark only after attempting all twelve. Full credit only when the done-when check actually passes on your cluster — partial YAML that never got applied, or a fix you believe worked but never verified, scores nothing, exactly as the real exam grades it.
| Task | Domain | Points | Your score |
|---|---|---|---|
| T1 · stuck kubelet-serving CSR | Troubleshooting | 12 | |
| T2 · liveness probe wrong port | Troubleshooting | 6 | |
| T3 · untolerated taint | Troubleshooting | 6 | |
| T4 · CoreDNS loop | Troubleshooting | 8 | |
| T5 · static pod manifest typo | Troubleshooting | 7 | |
| C1 · etcd snapshot & restore | Cluster Architecture | 13 | |
| C2 · scoped CI RBAC | Cluster Architecture | 9 | |
| W1 · CPU-based HPA | Workloads & Scheduling | 7 | |
| W2 · zone spread | Workloads & Scheduling | 6 | |
| N1 · default-deny + allow | Services & Networking | 8 | |
| N2 · Ingress path routing | Services & Networking | 8 | |
| S1 · WaitForFirstConsumer PVC | Storage | 10 | |
| Total | All five domains | 100 |
The real CKA's published pass mark is 66% — this paper uses the same number as a study reference. Below that here, don't just re-read the explanations above; that teaches you these twelve exact answers, not the underlying skill. Go re-run the matching drill instead: T1/T5 point back to fix a broken cluster, T2/T3 to debug a stuck pod, T4/N1/N2 to diagnose a networking failure, C1 to troubleshoot a failed upgrade, C2 to harden an RBAC configuration, and S1 to recover from a storage incident.
This is an independent, unofficial study resource — not affiliated with the CNCF or the Linux Foundation. The 120-minute duration, 66% pass mark, task count and permitted-documentation allowlist referenced on this page all change over time. Confirm current details on the official Linux Foundation CKA page and the CNCF certification page before you pay for anything. See Platform Engineering's CKA page and SRE's CKA page for the same exam from a different angle, and the Golden Astronaut course if the CKA is one stop on your way to the full Golden Kubestronaut ladder.
Foxy: T1 got me. I saw "Ready" on the node and stopped looking — never occurred to me a node could be Ready and still have a completely broken certificate.
Timmy: That's the entire lesson of this paper, though. "Ready" only means one specific heartbeat succeeded. It was never a promise about everything else working.
Gizmo: Easy fix for next time — just restart the kubelet on every node whenever anything looks weird. Works often enough. 🤑
Timmy: It "works often enough" the way turning a light off and on works often enough — sometimes it hides the actual fault instead of fixing it. T1's fix was one CSR approval. A kubelet restart wouldn't have touched the real problem at all.
Remy: I burned four minutes on T4 before I even opened the CoreDNS logs. Reading the paper first like Foxy said would've told me that one was the expensive one.
Sol: And S1 is the opposite trap — burning time "fixing" a PVC that was never broken. Slow down long enough to ask whether the thing in front of you is actually wrong.
Foxy: Different failure, same root cause every time, though — trust the symptom you can actually see, not the label you expected to see.
1. Why can a node show Ready in kubectl get nodes while kubectl logs against pods on that node still fails outright? 2. In T3, why is adding a toleration alone not enough to guarantee the batch pods land only on the dedicated pool? 3. What's the actual bug in T4's CoreDNS loop, and why is disabling the loop plugin the wrong fix? 4. Why does kubectl stop working entirely during T5, and how do you fix a static pod manifest without it? 5. In S1, what does a Pending PVC with WaitForFirstConsumer actually mean before any Pod exists, and what makes it wrong to conclude the PVC itself is broken?
Check your answers
- Because node readiness and
kubectl logs/execdepend on two different certificates — the kubelet client cert for outbound heartbeats to the API server, and the kubelet serving cert for the API server's inbound calls back into the kubelet — and only the second one was stuck in an unapproved CSR. - A toleration only makes a pod eligible to schedule on a tainted node; it doesn't require it. Without a matching
nodeSelectorornodeAffinity, the scheduler is free to place the pod on any untainted node instead, breaking the pool's intended isolation. - The real bug is the node's
/etc/resolv.confpointing back at the cluster's own DNS Service IP, so CoreDNS'sforward . /etc/resolv.confcreates a genuine infinite loop. Disabling theloopplugin would silence the crash-loop symptom while leaving the actual self-referencing query loop in place. - The four control-plane components, including the API server itself, are static pods read directly off disk by the control-plane node's own kubelet — so a broken API server means
kubectlhas nothing to talk to. The fix is SSH pluscrictlto find the error, then editing the manifest file directly; the kubelet's own file watcher restarts the pod once the file is valid, with nokubectl applyinvolved or possible. WaitForFirstConsumerdeliberately delays binding (and often provisioning) until a Pod that references the claim exists, so the provisioner can place the volume near where it'll actually be used. A Pending PVC in that state before any Pod exists is working exactly as designed, not broken — recreating it would change nothing.
That's Set 3. If your misses clustered in Troubleshooting, that's this paper doing its job — loop back to weeks 7–8 of the study plan and the drills linked above before your next sitting rather than re-reading this page's answer key on its own. For a paper weighted closer to the plain exam blueprint, sit Set 1 or Set 2; for shorter, single-topic reps instead of a full timed paper, see CKA Practice Tasks.