Exam Prep · Triage Playbook

The Triage Playbook — diagnose & remediate fast

Almost every task on a performance-based platform exam has something broken in it — sometimes that is the task, and sometimes it is a quiet trap inside a task that looks like plain configuration. The people who pass are not the ones who have memorised the most YAML; they are the ones who run the same diagnostic sequence every single time, so that all their thinking is left over for the actual fix. This page is the entry point: first that universal sequence, then a symptom index that takes whatever you are staring at and sends you to the page covering it in depth. Drill the sequence until the first six commands are muscle memory, and use the index as a lookup table, not a reading list.

☺ Explain it like I’m 10

Imagine the light in your bedroom won’t turn on. A panicky person immediately starts unscrewing the ceiling. A calm person always does the same four checks first: is the switch on? is the bulb dead? do the other lights work? did someone flip the big switch in the hallway box? Kubernetes has exactly the same kind of checks — and one of them, called Events, is a little note the cluster leaves you saying literally what went wrong. Most people forget to read the note. This page is about reading the note, then knowing which drawer to open next.

🐘🐢Your hosts for this topic: Ellie the Elephant & Timmy the Turtle — Ellie never forgets a symptom she has seen before and knows exactly which signal to look at next, and Timmy refuses to be rushed, checking the boring things (namespace, context, spelling) that turn out to be the answer more often than anyone admits.

The universal triage order

☺ Like you’re 10: Before you guess what’s wrong, always look in the same six places, in the same order. Guessing first is how you waste ten minutes.

The single biggest time sink under exam pressure is forming a hypothesis before gathering evidence. You see CrashLoopBackOff, you decide it must be the config map, you spend six minutes on the config map, and the answer was in the Events the whole time. So: do not think. Run the sequence. The sequence takes ninety seconds and it very frequently hands you the literal answer in plain English.

The six steps, in order

Steps 1–4 are the general Kubernetes ladder; step 5 is the platform-engineering addition that matters enormously on this exam, because so much of the platform is expressed as custom resources whose status.conditions block is the real error message. Only at step 6 do you get to have an opinion.

#StepCommandWhat you are looking for
1Wide listkubectl get <kind> -o widePhase, restarts, age, node, IP. Restarts > 0 and a recent age tell you a lot before you read anything else.
2Describekubectl describe <kind> <name>The Events block at the bottom. Scroll straight there first. Then Conditions, then the container spec.
3Namespace eventskubectl get events --sort-by=.lastTimestampFailures on objects you did not think to describe — the ReplicaSet, the PVC, the webhook, the quota.
4Logskubectl logs <pod> --previousWhy the app itself died. --previous is essential in a crash loop — the current container has no logs yet.
5Controller / CR statuskubectl get <cr> -o yamlstatus.conditionsWhat the operator thinks. Argo CD, Flux, Crossplane and cert-manager all write the true error here.
6HypothesisOnly now. And prefer the cause that explains all the evidence, not just the loudest bit.
# The 90-second opener. Run this on every broken thing, every time.
kubectl config current-context                     # am I even on the right cluster?
kubectl get pods -n "$NS" -o wide                  # 1. wide list
kubectl describe pod -n "$NS" "$POD" | tail -30    # 2. Events live at the BOTTOM
kubectl get events -n "$NS" --sort-by=.lastTimestamp | tail -25   # 3. everything else that failed
kubectl logs -n "$NS" "$POD" --previous --tail=50  # 4. why the process exited
kubectl logs -n "$NS" "$POD" -c "$INIT" --previous # 4b. init containers need -c

# Cluster-wide sweep when you do not yet know where the damage is:
kubectl get pods -A --field-selector=status.phase!=Running
kubectl get events -A --sort-by=.lastTimestamp | grep -i -E 'warn|fail|error' | tail -30
◆ Key idea

Events are the answer key. Kubernetes tells you, in near-English, things like 0/3 nodes are available: 3 Insufficient cpu, couldn't find key DB_HOST in ConfigMap app/config, or admission webhook "validate.kyverno.svc" denied the request: require-labels. Every one of those is the whole diagnosis. Train yourself to read the Events block before you read the spec — it is the difference between a two-minute task and a twelve-minute one.

The decision flow

The flow below is what the six steps look like as a shape. Notice that almost every branch terminates in “read Events / read Conditions” — because in a well-built control plane, every controller is contractually obliged to explain itself in one of those two places. Notice too that the three branches map exactly onto the three detailed playbooks this page routes you to.

context + namespace are you in the right place? kubectl get -o wide phase · restarts · node pod not Running describe → Events logs --previous Running, traffic fails endpoints → DNS NetworkPolicy → mesh CR not Ready status.conditions controller logs now form the hypothesis it must explain every symptom fix → re-run step 1 to verify over 5–7 min with no progress? flag it, move on
🦆 Dot’s-eye view

“What made me fast wasn’t learning more Kubernetes — it was deleting a habit. I used to open the Deployment YAML first, because that felt like ‘real’ debugging. Now I read the last ten lines of Events before anything else, and two thirds of the time the fix is right there in a sentence.”

Symptom index

☺ Like you’re 10: Find the words you can actually see on your screen in the left column. The right column says which page to open.

Once the sequence has produced evidence, this is the lookup. Scan the left column for the string in front of you, read the middle column for the cause to test first, and follow the link for the full symptom → cause → command → fix treatment.

You see…Most likelyWhere to look
Pending / UnschedulableInsufficient CPU/memory, taint or selector mismatch, unbound PVC, quotaWorkloads
ImagePullBackOff / ErrImagePullImage name or tag typo; private registry, no imagePullSecretsWorkloads
CrashLoopBackOffApp exits non-zero; wrong command; missing runtime configWorkloads
OOMKilled (exit 137)Memory limit below real usage, or a runtime heap above itWorkloads
CreateContainerConfigErrorConfigMap/Secret missing — or the key inside it isWorkloads
Init:Error / Init:CrashLoopBackOffInit container failed: migration, dependency wait, or permissionsWorkloads
Running but 0/1 READY, no restartsReadiness probe failing — wrong path, wrong port, slow startWorkloads
Restarts climbing, app looks healthyLiveness probe too aggressive for a slow containerWorkloads
EvictedNode memory/disk pressure or ephemeral-storage overrunWorkloads
Stuck TerminatingFinalizer never removed, long grace period, or node goneWorkloads
PVC stuck PendingNo matching or default StorageClass; no provisioner runningWorkloads
Multi-attach error on a volumeA ReadWriteOnce volume wanted by a second nodeWorkloads
StatefulSet pod never binds its volumevolumeClaimTemplates mismatch, or a zone-bound PVWorkloads
endpoints returns <none>Selector matches no pods — or matching pods are not ReadyNetworking
DNS lookups fail inside a podCoreDNS down, wrong FQDN, or DNS egress blockedNetworking
Pod-to-pod connection times outA NetworkPolicy: default-deny with no matching allowNetworking
Ingress or Gateway returns 404 / 503Wrong host/path rule, missing class, or backend without endpointsNetworking
mTLS handshake failure in the meshSTRICT mode with a sidecar missing, or an authorization policyNetworking
Error from server (Forbidden)No Role grants that verb, or the binding names the wrong subjectNetworking
admission webhook … denied the requestA Kyverno/Gatekeeper rule, or a webhook failing closedNetworking
Argo CD app OutOfSyncDrift, auto-sync off, or a path that renders nothingDelivery
Argo CD app Synced but DegradedGit applied cleanly; the manifests themselves are wrongDelivery
Sync fails or a hook never completesFailing hook Job, wrong sync wave, or an RBAC gapDelivery
Argo Rollout stuck mid-canaryPaused awaiting promotion, or the new ReplicaSet never availableDelivery
Rollout AnalysisRun failedMetric query returns nothing — bad provider address or selectorDelivery
Flux Kustomization not reconcilingSource not fetched, object suspended, or a build errorDelivery
Tekton PipelineRun / TaskRun failedFailing step, missing workspace, or ServiceAccount lacking registry accessDelivery
Prometheus target down or missingServiceMonitor labels, port name mismatch, or CR not selecting itDelivery
Alert never firesRule not loaded, expression never true, or routing swallows itDelivery
Traces missing or fragmentedWrong collector endpoint, heavy sampling, or context not propagatedDelivery
Crossplane claim never ReadyNo matching Composition, or missing provider credentialsDelivery
Custom resource accepted but ignoredNo controller watching that kind; CRD version not servedDelivery

The three playbooks

☺ Like you’re 10: The detail lives in three pages: things that won’t start, things that can’t talk, and things that won’t deliver.

Each page recaps the six-step order, then goes deep with matrices, commands and worked fixes. Between them they cover every row of the index above.

If the symptom spans two of them — a rollout stuck because the new pods fail a readiness probe, say — start at the layer closest to the evidence you already have, and follow the signals outward.

When you are stuck

☺ Like you’re 10: If you’ve been stuck a while, stop and check the boring things: are you in the right room, and did you read the question properly?

Every experienced operator has lost twenty minutes to a problem that did not exist. The checks below cost fifteen seconds and resolve a surprising share of “impossible” failures — and being in the wrong namespace is one of the most common ways to fail a task you knew how to do.

# 1. Am I where I think I am?
kubectl config current-context
kubectl config view --minify -o jsonpath='{..namespace}{"\n"}'
kubectl config use-context cluster-prod
kubectl config set-context --current --namespace=app

# 2. Does the object exist, spelled that way?
kubectl get <kind> -A | grep -i <name>

# 3. Is the control plane healthy? (rules out a class of ghosts)
kubectl get nodes
kubectl get apiservices | grep -v True      # a broken aggregated API breaks unrelated commands
  1. Re-read the task wording. Verbatim. People solve the problem they remember rather than the one on screen — fixing staging when the task named prod, or creating a ClusterRoleBinding where namespace-scoped access was asked for. The wording names the exact resource, namespace and context you are meant to use.
  2. Verify cluster and namespace. Multi-cluster environments switch context between tasks, so a task beginning “on cluster staging” means the previous task’s context is now wrong.
  3. Time-box hard. Past five to seven minutes with no visible progress, flag it and move on — partial credit is real, and three finished easy tasks beat one heroically half-finished hard one. Drill that against Practice Tasks and the Exam Guide, with the Command Reference within reach.
⚠ Verify exam specifics yourself

Details about the CNPE such as task counts, the pass mark, time limits and which resources you may open can and do change, and community write-ups describe one person’s sitting rather than a specification. The authoritative sources are the official CNCF certification page and the Linux Foundation handbook. Use this page for technique; confirm the rules before you book. Our reading of the published accounts is in Field Notes.

🐘 Ellie’s workshop · 15 min

Practise routing, not fixing. On a kind or minikube cluster, deploy a small app and break it five ways, one at a time: point the Service selector at a label no pod has; set resources.limits.memory: 16Mi on a JVM app; reference a ConfigMap key that does not exist; apply a default-deny-egress NetworkPolicy with no DNS exception; point an Argo CD Application at a path that isn’t there. Run only the six-step opener each time, then name the symptom-index row and the playbook you would open. If you can name the row before the fix, the index is doing its job.

🎬 At the Platform Guild
🦆

Dot: My deployment’s been rolling for ten minutes and the pods just say Pending. I think the cluster’s broken.

🐘

Ellie: Maybe! But did you describe the pod and read the bottom?

🦆

Dot: …it says 0/3 nodes are available: 3 Insufficient cpu. Oh. It literally just told me.

🐘

Ellie: Events almost always do — and now you know which page to open, because “Pending” is a workloads row.

👺

Gizmo: Easy fix — strip the resource requests off entirely! No requests, no “insufficient”, no problem. 😈

🐢

Timmy: And then the scheduler packs six noisy pods onto one node and evicts your database at 3am. Also, Dot: which cluster are you on?

🦆

Dot: …staging. The task said prod. I’ll see myself out.

Triage is a habit, not a talent. The order never changes and the commands never change; all that changes is which drawer you open once the evidence is in.

🐢 Timmy’s checkpoint

1. Name the first four steps of the universal triage order, in order. 2. Why is --previous essential when reading logs from a crash-looping pod? 3. A pod is Running with 0/1 READY and zero restarts — which probe is failing, and how do you know it isn’t the other one? 4. Which playbook covers an admission webhook denied the request error, and which covers a Crossplane claim with no status at all?

Check your answers
  1. kubectl get -o widekubectl describe (read the Events at the bottom) → kubectl get events --sort-by=.lastTimestampkubectl logs --previous. Then the controller/CR status.conditions, and only then a hypothesis.
  2. The container that failed has already been replaced, so the current one may have no logs yet. --previous reads the dead container’s output, where the real error is.
  3. The readiness probe. A failing liveness probe restarts the container, so restarts would be climbing; zero restarts rules it out. The pod runs fine but has been dropped from the Service’s endpoints.
  4. Networking, RBAC & Admission for the webhook denial; Delivery, Observability & Platform APIs for the claim — and no status at all implies no controller is watching that kind.