The Exam Blueprint · CKA · D5 · Troubleshooting · 30%

Troubleshooting

Troubleshooting is the single largest domain on the Certified Kubernetes Administrator exam — 30% of everything tested, bigger than Storage and Workloads and Scheduling combined. It's also the domain with the fewest new nouns to memorize, because it isn't really about facts at all: it's about a method. The CNCF's curriculum names five competencies here — troubleshooting clusters and nodes, troubleshooting cluster components, monitoring resource usage, reading container output streams, and troubleshooting services and networking — and every one of them rewards the same instinct: look at the cheapest, least invasive signal first, and only escalate to something more expensive once the cheap signal has told you where to look. This page teaches that sequence explicitly, then walks each of the five competencies with the real commands behind it, because on exam day the clock punishes anyone who guesses before they check.

☺ Explain it like I'm 10

Imagine your bike won't move. A bad way to start: immediately take the chain apart. A much better way: first look — is the tire flat? Then listen — does anything creak or grind when you pedal? Only then do you get out a wrench and start removing parts. And if the whole bike won't even balance upright, that's not a chain problem at all — something's wrong with the frame itself, and no amount of chain fiddling fixes that. Kubernetes troubleshooting works the same way: look first (describe what's broken), listen next (read the logs), and only then reach for a wrench (get inside the container or the machine). And if a whole machine seems wrong, stop blaming the app running on it — go check the machine.

🦉🦫Your hosts for this topic: Professor Owl & Benny the Beaver — Owl insists the method comes before the commands, and Benny is the one who actually gets his hands dirty running them on a live, deliberately-broken cluster.

The domain, and where it sits in the blueprint

☺ Like you're 10: The whole exam is cut into five slices, and this is by far the biggest one — bigger than the other two smallest slices put together.

The CNCF's CKA curriculum publishes five domains that sum to exactly 100%. This page covers the last and largest, Domain 5. The other four are covered on their own pages in this blueprint, and it's worth reading all five before you sit the exam — a stuck Pod is rarely only a troubleshooting question; it's usually a scheduling constraint, a storage mismatch, or a networking gap wearing a troubleshooting costume.

#DomainWeight
D1Cluster Architecture, Installation & Configuration25%
D2Workloads & Scheduling15%
D3Services & Networking20%
D4Storage10%
D5Troubleshooting — this page30%

(The curriculum PDF prints Domain 3 as "Servicing and Networking" in some published versions; this course and its blueprint page use the more common phrasing, "Services & Networking" — same domain, same 20%.) The five published competencies under Troubleshooting, verbatim from the curriculum, are: troubleshoot clusters and nodes, troubleshoot cluster components, monitor cluster and application resource usage, manage and evaluate container output streams, and troubleshoot services and networking. That's 27 competencies published across all five domains — this course's CKA study plan maps every one of them onto a page. For a companion treatment of this exact exam from adjacent angles, see Platform Engineering's CKA page and SRE's CKA page — both good second opinions on the same 30%.

The method before the facts: describe, then logs, then exec

☺ Like you're 10: Always check the cheap, fast thing before the slow, invasive thing — most of the time the cheap thing already tells you exactly what's wrong.

Every troubleshooting question on the exam — and in production — can be attacked with the same four-stage sequence, each stage strictly cheaper and less invasive than the one after it. Skip a stage and you either waste time re-deriving what the cheap stage would have told you for free, or you jump straight to guessing.

The triage sequence 1. Wide view kubectl get pods -A spot what isn't Running cheapest 2. Describe kubectl describe read Events at the bottom 3. Logs kubectl logs --previous if it crashed 4. Exec / node kubectl exec, or SSH + journalctl on the node most invasive Never skip a stage — the cheap stage often answers the question the expensive stage was about to. And if stage 4 means "the node itself," stop debugging the app — you're debugging the node now.
# the standard opening sequence — memorize this exact order
kubectl get pods -A -o wide | grep -Ev 'Running|Completed'   # stage 1: who's unhappy?
kubectl describe pod <pod> -n <ns>                            # stage 2: Events, at the bottom
kubectl logs <pod> -n <ns> -c <container>                    # stage 3: what did it say?
kubectl logs <pod> -n <ns> -c <container> --previous          # stage 3b: what did the LAST crash say?
kubectl exec -it <pod> -n <ns> -c <container> -- sh          # stage 4: only once 1-3 pointed here
◆ Key idea

Notice what "describe before logs" actually buys you: a Pod stuck in Pending or ImagePullBackOff has no logs to read at all — the container never started, so there's nothing to log. kubectl describe's Events section is the only place that failure is visible. Jumping straight to kubectl logs on a Pod that never scheduled just wastes a command and returns nothing useful.

Troubleshooting clusters and nodes

☺ Like you're 10: Before you blame anything running on a machine, check whether the machine itself is healthy — cold, out of breath, or full up.

A node reports its health as a set of ConditionsReady, MemoryPressure, DiskPressure, PIDPressure, and NetworkUnavailable — each independently True, False, or Unknown. A node stuck at Ready: Unknown almost always means the control plane has stopped hearing from that node's kubelet — check the node's own kubelet service before assuming the node is dead. NotReady with a known reason (disk full, memory pressure) is more useful: the kubelet is talking, and it's telling you exactly what's wrong.

kubectl get nodes -o wide
kubectl describe node <node> | grep -A6 Conditions           # Ready / MemoryPressure / DiskPressure / ...
kubectl describe node <node> | grep -A10 "Allocated resources"

# on the node itself — SSH in when kubectl's view of it stops making sense
systemctl status kubelet
journalctl -u kubelet -n 100 --no-pager -p err                # errors only, last 100 lines
crictl ps -a                                                   # containers the runtime actually has
df -h /var/lib/kubelet   /var/lib/containerd                  # DiskPressure's usual root cause

# taking a node out of rotation safely, and putting it back
kubectl cordon <node>                                          # stop new scheduling, don't evict anything yet
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
kubectl uncordon <node>                                        # forgetting this is the #1 self-inflicted outage

crictl — the CRI-level tool, not docker — is worth being fluent with specifically because it talks to the container runtime directly, one layer below the kubelet, which matters enormously in the next section. See control-plane internals for what the kubelet actually is on that node, and the Fix a Broken Cluster drill for hands-on practice with exactly this category of failure.

Troubleshooting cluster components: when the control plane itself is sick

☺ Like you're 10: If the thing that's broken IS the thing you'd normally ask for help — the API server — you can't ask it what's wrong with itself. You have to go around it.

The API server, etcd, the scheduler, and the controller-manager on a kubeadm-built control plane all run as static Pods — Pod manifests sitting in /etc/kubernetes/manifests/ on the control-plane node, which the kubelet watches directly and runs through the container runtime, with no API server involved in that specific loop at all. That's precisely what makes them recoverable: the mechanism that starts the API server can't itself depend on the API server being up.

Normal path — needs a healthy control plane kubectl API server etcd, scheduler, controller-manager The static pod path — bypasses the API entirely kubelet on the node watches /etc/kubernetes/manifests/ container runtime crictl / containerd, driven directly this is how the API server gets started in the first place If the top lane's API server won't come up, debug the bottom lane instead: journalctl on the kubelet · crictl ps -a · edit the manifest directly on the node
# the API server itself won't come up — kubectl is useless here, so don't reach for it
ssh <control-plane-node>
journalctl -u kubelet -n 60 --no-pager                         # kubelet logs WHY it won't run the manifest
crictl ps -a --name kube-apiserver                              # exited? crash-looping? check the exit reason
crictl logs <container-id>                                      # the apiserver's own stderr, one layer down
ls /etc/kubernetes/manifests/                                   # kube-apiserver.yaml, etcd.yaml, kube-scheduler.yaml, ...
cat /etc/kubernetes/manifests/kube-apiserver.yaml                # look for a bad flag, a typo, a wrong cert path

# fix it by editing the file in place — no "kubectl apply" exists for a static pod
vi /etc/kubernetes/manifests/kube-apiserver.yaml
# the kubelet notices the file change on its own and restarts the pod within seconds
⚠ Watch out

A static pod manifest with invalid YAML doesn't produce a Kubernetes error anywhere you'd normally look — there's no API server yet to report one. The kubelet just silently refuses to run it, and the only place that shows up is journalctl -u kubelet. If the exam scenario says "this control-plane component isn't coming up and kubectl shows nothing at all," that silence is the clue: go read the kubelet's journal on that node, not the API.

Monitoring resource usage

☺ Like you're 10: "It's slow" isn't a diagnosis — check whether it's actually short on CPU or memory before you guess at anything fancier.

kubectl top shows live CPU and memory usage for nodes or Pods, but it depends on the metrics-server add-on being installed — a fresh cluster has no built-in usage metrics, and kubectl top failing with "metrics not available" is itself a diagnosis, not a bug in the command. Once it's running, the real skill is comparing three different numbers that are easy to confuse: what the Pod requested, what it's limited to, and what it's actually using right now.

kubectl top nodes
kubectl top pods -n <ns> --sort-by=memory
kubectl describe node <node> | sed -n '/Allocated resources/,/Events/p'   # requests vs. capacity, per node
kubectl describe pod <pod> -n <ns> | grep -A4 -i "Limits:\|Requests:"

# a container that keeps disappearing is usually one of these two, and describe tells you which
kubectl get pod <pod> -n <ns> -o jsonpath='{.status.containerStatuses[*].lastState}'
SymptomLikely causeFirst command
Container repeatedly killed, exit code 137Hit its memory limit — OOMKilledkubectl describe pod, check Last State: Terminated, Reason: OOMKilled
Pod disappears, Status: EvictedThe node ran low on memory or disk and reclaimed itkubectl describe pod for the eviction message, then kubectl describe node
Pod stuck Pending, no node assignedNo node has enough requested capacity free to fit itkubectl describe pod Events: "Insufficient cpu/memory"
kubectl top returns an errormetrics-server isn't installed or isn't Readykubectl get deploy metrics-server -n kube-system

Requests drive scheduling — the scheduler only checks requests, never limits, when deciding whether a node has room. Limits drive eviction — a container that exceeds its memory limit gets OOMKilled by the kernel, not gently throttled. That asymmetry between the two numbers is a favorite exam trap, and it's covered in depth from the resource-management angle in scheduling & resource management and from the platform-observability angle in observability on Kubernetes.

Container output streams

☺ Like you're 10: A well-behaved container writes what happened straight to the screen, not to a hidden file — that's the whole reason kubectl logs works at all.

Kubernetes captures whatever a container writes to stdout and stderr and nothing else — a container that logs to an internal file instead is invisible to kubectl logs no matter how much it writes, which is exactly why the twelve-factor convention of logging to standard streams matters operationally, not just stylistically. Reading those streams correctly, especially for a Pod that already crashed, is its own small skill:

kubectl logs <pod> -n <ns>                          # the CURRENT container's stdout/stderr
kubectl logs <pod> -n <ns> --previous                 # the LAST container before it restarted — read this for CrashLoopBackOff
kubectl logs <pod> -n <ns> -c <container>             # multi-container pod: name it, or you get an error
kubectl logs <pod> -n <ns> -c <init-container>         # init containers have logs too — check these before the app container
kubectl logs <pod> -n <ns> --since=10m -f              # follow, last 10 minutes only
kubectl logs -n <ns> -l app=web --all-containers --prefix=true   # every pod matching a label, one merged stream

The single most common miss under exam pressure: reading kubectl logs with no flags on a Pod that's already restarted, seeing nothing useful, and concluding there's nothing to see — when --previous was the entire answer, because you're looking at the brand-new container that hasn't failed yet instead of the one that already did.

✎ Try it

Deploy any container with a command that exits non-zero after a few seconds (command: ["sh","-c","sleep 3; exit 1"]), watch it enter CrashLoopBackOff, then run kubectl logs with and without --previous back to back. Seeing the empty result from the current container next to the real one from --previous, side by side, is the fastest way to make this stick.

Troubleshooting services and networking

☺ Like you're 10: A Service is just a label with a list of addresses attached to it — if the list is empty, nothing you do to the Service itself will fix it, because the problem is upstream, at the labels.

A Service doesn't route traffic itself; it's a stable name and IP that kube-proxy programs into rules pointing at whatever Pods match the Service's selector, tracked live in an EndpointSlice. The single most common networking failure on the exam is a Service with a perfectly correct spec and an empty EndpointSlice — meaning no running Pod's labels actually match the selector, usually from a typo or a label that got removed in a later edit.

kubectl get svc <svc> -n <ns> -o wide
kubectl get endpointslices -n <ns> -l kubernetes.io/service-name=<svc>   # empty = selector matches nothing
kubectl get pods -n <ns> --show-labels | grep <expected-label>           # do any pods actually carry it?
kubectl describe svc <svc> -n <ns>                                        # confirm the selector itself

# is it DNS, or is it the Service? a throwaway debug pod answers both in under a minute
kubectl run tmp-shell --rm -it --restart=Never --image=busybox:1.36 -- sh
#   inside:
#   nslookup <svc>.<ns>.svc.cluster.local     # DNS resolves? if not, check CoreDNS next
#   wget -qO- http://<svc>.<ns>:<port>         # resolves but hangs/refuses? it's the endpoints or a NetworkPolicy

kubectl get pods -n kube-system -l k8s-app=kube-dns   # is CoreDNS itself even running?
kubectl get networkpolicy -n <ns>                      # a default-deny policy with no matching allow rule looks identical to a routing bug

Once DNS resolves and endpoints exist but traffic still doesn't arrive, a NetworkPolicy is the next suspect: the moment any policy selects a Pod, that Pod flips from allow-everything to deny-by-default in whichever direction the policy names, and a policy with an Egress section but no explicit rule allowing UDP/TCP port 53 silently breaks every DNS lookup that Pod makes — a failure that looks exactly like a broken application from the outside. This whole layer — Pod-to-Pod connectivity, EndpointSlices, CoreDNS, NetworkPolicy, Ingress, and the newer Gateway API — is the subject of the Services & Networking blueprint page and goes deeper still in networking & the CNI; the Diagnose a Networking Failure drill drills exactly this failure mode hands-on.

Common exam traps in this domain

☺ Like you're 10: The exam likes to hand you a symptom that looks like one thing and is actually caused by something else nearby.

🦫 Benny's-eye view

"New engineers always want to exec in first — it feels the most like 'doing something.' I make them run describe and logs out loud before I let them touch exec, every single time, until it's reflex. On the exam, that habit alone saves minutes per task. In production, it's the difference between a two-minute fix and accidentally poking around inside a container that was never the actual problem in the first place."

🎬 At the Pod Squad
🦫

Benny: Pod's crash-looping. I ran kubectl logs and it's empty — I'm stuck.

🦉

Professor Owl: Empty how? Did you check the container that's running right now, or the one that already died and got replaced?

🦫

Benny: ...the one that's running now. It hasn't failed yet.

🦉

Professor Owl: Add --previous. You're reading the wrong container's logs entirely.

👺

Gizmo: Or just kubectl exec in and poke around by hand — way more fun than reading logs. 🤑

🐢

Timmy: On a container that isn't even the one that crashed? You'd be debugging a completely different process than the one that failed.

🐦

Pip: And once you've fixed the crash — check whether anything downstream still can't reach it. A dead endpoint doesn't come back just because the Pod does; the EndpointSlice has to catch up too.

🦫

Benny: ...fine. Describe, then logs, then previous logs, then exec. In that order. I get it.

⚠ Verify officially before you book

This page is an independent, unofficial study resource — not affiliated with the CNCF or the Linux Foundation. The domain weight and competency wording above come from the published CKA curriculum, and the exam itself is performance-based: two hours, live clusters in a browser terminal, graded entirely on the end state you leave the cluster in, not on the commands you typed to get there. Curriculum versions, weights, and exam logistics (price, duration, pass mark, permitted documentation) change over time — confirm the current details on the official Linux Foundation and CNCF certification pages, and see Kubernetes Certifications and the CKA study plan for this course's full logistics table before you pay for anything.

🐢 Timmy's checkpoint

1. What are the four stages of the triage sequence taught on this page, in order, and why does that order matter? 2. A node reports Ready: Unknown. What does that specifically suggest, versus a node reporting NotReady with a known Condition? 3. Why can't a broken API server be diagnosed with kubectl, and what do you check instead? 4. A container keeps getting killed with exit code 137. Is that a requests problem or a limits problem, and what's the Kubernetes term for it? 5. A Service's spec looks correct but nothing can reach it. What's the single most common root cause, and which command exposes it directly? 6. Why does a NetworkPolicy with an Egress section often break DNS unless you're careful?

Check your answers
  1. Wide view (kubectl get pods -A) → describe (Events) → logs (current, then --previous) → exec or the node. The order matters because each stage is more expensive and invasive than the last, and an earlier stage frequently already answers the question — jumping ahead wastes time and can mean debugging the wrong thing.
  2. Ready: Unknown usually means the control plane has simply stopped hearing from that node's kubelet at all (a communication gap) — check whether the kubelet service is even running. NotReady with a known Condition like MemoryPressure or DiskPressure means the kubelet IS talking, and is actively reporting a specific resource problem.
  3. A broken API server can't answer kubectl requests, since kubectl talks to the API server itself. Instead, SSH to the control-plane node and check journalctl -u kubelet (why the kubelet won't run the static pod) and crictl ps -a / crictl logs (what the container itself is doing), then inspect the manifest file in /etc/kubernetes/manifests/ directly.
  4. Exit code 137 with repeated kills is a limits problem — the container exceeded its memory limit and the kernel OOM-killed it. Kubernetes reports this as OOMKilled in the container's last state. Requests, by contrast, only affect scheduling, not eviction.
  5. The Service's selector doesn't match any running Pod's actual labels, leaving its EndpointSlice empty. kubectl get endpointslices -n <ns> -l kubernetes.io/service-name=<svc> shows this directly — an empty result means no Pod qualifies, regardless of how correct the Service spec itself looks.
  6. Selecting a Pod with any NetworkPolicy flips that Pod to deny-by-default in the directions the policy names. If the policy has an Egress section with no explicit rule allowing UDP/TCP port 53, every outbound DNS lookup from that Pod is silently blocked, which looks like a broken application from the outside.