Overview
- Chapter 12 of the Kubernetes Cookbook: kubectl autocompletion, parsing status, debugging Pods, cluster snapshots, adding nodes, drain/cordon, and etcd. (Recipes 12.2 and 12.3 are service tricks and live in Services & Ingress.)
- Plus the modern debugging toolkit the book predates:
kubectl debug, ephemeral containers andkubectl events. - Back to the hub: Kubernetes Cookbook · Related: Monitoring & Logging, Learning kubectl
Recipes
Recipe 12.1 — kubectl Autocompletion
- Problem: Typing
kubectl get deployment my-long-nameis tedious. - Solution (zsh, the macOS default):
# ~/.zshrc
source <(kubectl completion zsh)
alias k=kubectl
compdef k=kubectl- Gotchas: Bash needs the
bash-completionpackage first. Completion also completes resource names from the live cluster.
Source: K8s Cookbook, Recipe 12.1
Recipe 12.4 — Parse Resource Status (for scripts and CI)
- Problem: React to an object’s state in a script or pipeline.
- Solution:
kubectl get pod jump -o jsonpath='{.status.qosClass}' # Guaranteed | Burstable | BestEffort
kubectl get pod jump -o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\n"}{end}'
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}'
kubectl get deploy web -o json | jq '.status'
# Better than polling: block until a condition is true
kubectl wait --for=condition=Available deploy/web --timeout=120s
kubectl wait --for=jsonpath='{.status.phase}'=Running pod/jump
kubectl rollout status deploy/web --timeout=2m # non-zero exit if it fails → fails the CI step- Discussion: Built-in JSONPath or go-template output means you don’t need
jq, butjqis nicer for complex queries. - QoS classes decide who gets evicted first under memory pressure:
- BestEffort: no requests or limits. Evicted first.
- Burstable: some requests or limits.
- Guaranteed: requests = limits for CPU and memory. Evicted last.
Source: K8s Cookbook, Recipe 12.4
Recipe 12.5 — Debug Pods
- Problem: A Pod won’t start, or keeps dying.
- Solution: Run an OODA loop. Observe, Orient (list hypotheses), Decide (pick one), Act (test it), repeat. Observe in this order:
kubectl get pod web-abc -o wide # STATUS, RESTARTS, NODE
kubectl describe pod web-abc # Events at the bottom ← start here
kubectl events --for pod/web-abc # (or) kubectl get events --sort-by=.lastTimestamp
kubectl logs web-abc --previous # why did the last container die?
kubectl get pod web-abc -o jsonpath='{.status.containerStatuses[*].lastState}'- Read the status:
| Status | Usual cause | Look at |
|---|---|---|
Pending | No node fits (requests too big, taints, PVC unbound, quota) | describe → Events (FailedScheduling) |
ImagePullBackOff / ErrImagePull | Typo in image/tag, private registry without imagePullSecrets, rate limit | describe → Events |
CrashLoopBackOff | App exits on start: bad config, missing env/Secret, liveness too aggressive | logs --previous, exit code |
OOMKilled (exit 137) | Memory limit too low or a leak | lastState.terminated.reason, kubectl top |
CreateContainerConfigError | Referenced ConfigMap/Secret or key missing | describe |
Running but 0/1 Ready | Readiness probe failing | describe → probe events |
Terminating forever | Finalizers, or node unreachable | get -o yaml → metadata.finalizers |
- Get inside: use
kubectl debug. It attaches an ephemeral debug container that works even for distroless images with no shell:
kubectl exec -it web-abc -- sh # if the image has a shell
kubectl debug -it web-abc --image=busybox:1.36 --target=web # share the app container's process namespace
kubectl debug web-abc -it --copy-to=web-debug --container=web -- sh # debug a *copy* (e.g. CrashLoop) with a shell
kubectl debug node/worker-1 -it --image=ubuntu # a shell on the node (host fs at /host)
kubectl run tmp --rm -it --image=nicolaka/netshoot --restart=Never -- bash # network toolbox Pod- Pre-flight checklist (from the book, still true):
- Is the manifest valid? (
kubectl apply --dry-run=server) - Does the image run locally with
docker run? - Can nodes pull from the registry?
- Is DNS working (see Services & Ingress)?
- Are there enough resources, and are your requests and limits sane?
- Is the manifest valid? (
Changed since the book:
kubectl debugand ephemeral containers (GA 1.25) replace “install curl into the running container”.kubectl events(GA 1.28) gives sorted, filterable events. Validate against your real cluster with--dry-run=server. The book pointed to a JSON schema site.
Source: K8s Cookbook, Recipe 12.5
Recipe 12.6 — Snapshot the Whole Cluster State
- Problem: Capture everything for an audit, a post-mortem, or a support ticket.
- Solution:
kubectl cluster-info dump --all-namespaces --output-directory=./cluster-state-$(date +%F)- Discussion: You get objects as JSON plus Pod logs, per namespace. For restorable backups (objects + volumes) use Velero. For “what’s in Git vs. live”, GitOps tools show drift.
- Gotchas: The dump contains Secrets and logs, so treat it as sensitive.
Source: K8s Cookbook, Recipe 12.6
Recipe 12.7 — Add Worker Nodes
-
Problem: The cluster needs more capacity.
-
Solution: Provision a machine and install the three node components:
- kubelet: the node agent. It registers the node, runs and probes Pods, mounts volumes, and reports status.
- A container runtime speaking CRI: containerd or CRI-O.
- kube-proxy: programs Service routing. It’s optional if your CNI replaces it, e.g. Cilium.
Then run
kubeadm join …(see Getting Started & Local Clusters). On managed clouds, just grow the node pool, or let the autoscaler do it (Scaling). -
Discussion: A Node object is only a record. The node counts once its kubelet reports healthy (
kubectl get nodes→Ready). Debug runtimes withcrictlon the node, sincedocker pswon’t show Kubernetes containers anymore.
Changed since the book:
The Docker Engine runtime was removed in 1.24 (dockershim), so containerd or CRI-O it is.
Source: K8s Cookbook, Recipe 12.7
Recipe 12.8 — Drain a Node for Maintenance
- Problem: Patch, reboot or upgrade a node without dropping traffic.
- Solution:
kubectl cordon worker-1 # no new Pods land here
kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data --timeout=5m
# … do the maintenance …
kubectl uncordon worker-1 # schedulable again- Protect apps during drains with a PodDisruptionBudget:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2 # or maxUnavailable: 1
selector:
matchLabels:
app: web- Discussion:
drain= cordon + evict every Pod, respecting PDBs and graceful termination. DaemonSet Pods stay. Bare Pods (no controller) need--forceand are gone forever. - Gotchas: A PDB with
minAvailableequal to the replica count (e.g. 1 of 1) blocks drains forever. Run ≥ 2 replicas for anything you drain around.
Changed since the book:
--delete-local-data→--delete-emptydir-data. PDBs arepolicy/v1(GA 1.21).
Source: K8s Cookbook, Recipe 12.8 · Best Practices book, Ch. 8
Recipe 12.9 — Manage etcd
- Problem: Back up (or inspect) the cluster’s source of truth.
- Solution (self-managed / kubeadm control plane):
ETCDCTL_API=3 etcdctl \
--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 \
snapshot save /backup/etcd-$(date +%F).db
etcdutl snapshot status /backup/etcd-2026-01-01.db -w table- Discussion: Only the API server talks to etcd. Everything else goes through the API. Keys look like
/registry/<resource>/<ns>/<name>, stored as protobuf (not readable JSON). Back up regularly and test restores (etcdutl snapshot restore). - Gotchas: On managed services (GKE, EKS, AKS) etcd is hidden and backed up for you. Back up your objects with Velero or Git instead.
Changed since the book:
The book’s
curl …:2379/v2/keysuses the etcd v2 API, which is gone. Kubernetes uses etcd v3 only, over TLS. Snapshot status/restore moved frometcdctltoetcdutl.
Source: K8s Cookbook, Recipe 12.9
Summary
- Debug order:
get -o wide→describe(Events!) →logs --previous→kubectl debug. - Know the statuses: Pending (scheduling), ImagePullBackOff (image), CrashLoopBackOff (app/config/probe), OOMKilled (memory).
- Script with
jsonpath,kubectl waitandrollout status, notsleep 30. - Maintenance:
cordon→drain --ignore-daemonsets --delete-emptydir-data→uncordon, with PDBs and ≥ 2 replicas. - Nodes run kubelet + containerd + kube-proxy, so use
crictlon the node. Back up etcd withetcdctl snapshot save(v3 only).