CKA — Troubleshooting Lab
Lab Overview
This lab simulates broken cluster scenarios similar to what you’ll face on the CKA exam. Each task presents a broken component — your job is to diagnose the issue and fix it.
Troubleshooting is worth 30% of the CKA exam. This lab is the most important practice you can do.
Lab Rules
- Read the task description carefully
- Follow the same debugging flow you’d use on the exam: observe → diagnose → fix → verify
- Time yourself — in the real exam, you’ll have roughly 5–8 minutes per question
- Try to solve each task on your own before looking at the answer
Prerequisites
- A working Kubernetes cluster (kubeadm, minikube, or kind)
kubectlaccess with admin privileges- SSH access to nodes (for node and control plane tasks)
Task 1: Fix a CrashLoopBackOff Pod
Setup
Deploy a Pod with a broken command:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: crash-app
labels:
app: crash-app
spec:
containers:
- name: app
image: nginx:1.25
command: ["/bin/start-app"]
EOF
The Problem
kubectl get pods
NAME READY STATUS RESTARTS AGE
crash-app 0/1 CrashLoopBackOff 3 (20s ago) 1m
Your Task
Diagnose why the Pod is crashing and fix it so it transitions to Running.
Show Answer
# Step 1: Check the logs
kubectl logs crash-app
# Output: exec /bin/start-app: no such file or directory
kubectl logs crash-app --previous
# Same output — the command doesn't exist in the nginx image
# Step 2: The fix — remove the wrong command so nginx uses its default entrypoint
# Export the Pod spec
kubectl get pod crash-app -o yaml > crash-app.yaml
# Delete the broken Pod
kubectl delete pod crash-app
# Edit the YAML — remove the "command" line
# The file should look like:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: crash-app
labels:
app: crash-app
spec:
containers:
- name: app
image: nginx:1.25
EOF
# Step 3: Verify
kubectl get pod crash-app
Expected final state:
NAME READY STATUS RESTARTS AGE
crash-app 1/1 Running 0 10s
Task 2: Fix an ImagePullBackOff Pod
Setup
Deploy a Pod with a wrong image name:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pull-app
labels:
app: pull-app
spec:
containers:
- name: app
image: ngingx:1.25
EOF
The Problem
kubectl get pods
NAME READY STATUS RESTARTS AGE
pull-app 0/1 ImagePullBackOff 0 1m
Your Task
Diagnose the issue and fix the Pod so it runs successfully.
Show Answer
# Step 1: Describe the Pod to see the error
kubectl describe pod pull-app
# Events show:
# Warning Failed kubelet Failed to pull image "ngingx:1.25":
# rpc error: ... manifest unknown ...
# The image name is "ngingx" — should be "nginx"
# Step 2: Fix — you can't edit the image of an existing Pod directly
# Export, fix, delete, and recreate
kubectl get pod pull-app -o yaml > pull-app.yaml
kubectl delete pod pull-app
# Edit the YAML: change "ngingx" to "nginx"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pull-app
labels:
app: pull-app
spec:
containers:
- name: app
image: nginx:1.25
EOF
# Step 3: Verify
kubectl get pod pull-app
Expected final state:
NAME READY STATUS RESTARTS AGE
pull-app 1/1 Running 0 15s
Task 3: Troubleshoot a Pending Pod
Setup
Deploy a Pod with resource requests that exceed available resources:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pending-app
labels:
app: pending-app
spec:
containers:
- name: app
image: nginx:1.25
resources:
requests:
cpu: "100"
memory: "1Ti"
nodeSelector:
gpu: "true"
EOF
The Problem
kubectl get pods
NAME READY STATUS RESTARTS AGE
pending-app 0/1 Pending 0 5m
Your Task
Diagnose why the Pod won’t schedule, and fix it.
Show Answer
# Step 1: Check why it's pending
kubectl describe pod pending-app
# Events show:
# Warning FailedScheduling default-scheduler
# 0/3 nodes are available: 3 Insufficient cpu, 3 Insufficient memory,
# 3 node(s) didn't match Pod's node selector
# Two problems:
# 1. CPU request of "100" (100 cores) and memory of "1Ti" (1 tebibyte) is way too high
# 2. nodeSelector gpu=true doesn't match any node
# Step 2: Fix — reduce resources to something reasonable and remove/fix nodeSelector
kubectl delete pod pending-app
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pending-app
labels:
app: pending-app
spec:
containers:
- name: app
image: nginx:1.25
resources:
requests:
cpu: "100m"
memory: "128Mi"
EOF
# Step 3: Verify
kubectl get pod pending-app
Expected final state:
NAME READY STATUS RESTARTS AGE
pending-app 1/1 Running 0 10s
Task 4: Fix a NotReady Node
Setup
Note: This task requires SSH access to a worker node. If you’re using minikube, you can simulate this with
minikube ssh.
Simulate a node failure by stopping the kubelet:
# SSH into the worker node
ssh node01
# Stop kubelet (simulates failure)
sudo systemctl stop kubelet
# Exit back to the control plane
exit
The Problem
kubectl get nodes
NAME STATUS ROLES AGE VERSION
controlplane Ready control-plane 10d v1.29.0
node01 NotReady <none> 10d v1.29.0
Your Task
Diagnose why the node is NotReady and bring it back to Ready status.
Show Answer
# Step 1: Check the node
kubectl describe node node01
# Conditions show Ready=False, and the message mentions "Kubelet stopped posting node status"
# Step 2: SSH to the node
ssh node01
# Step 3: Check kubelet status
systemctl status kubelet
# Output: inactive (dead) — kubelet is stopped
# Step 4: Check kubelet logs for any underlying issues
journalctl -u kubelet | tail -20
# Step 5: Start and enable kubelet
sudo systemctl start kubelet
sudo systemctl enable kubelet
# Step 6: Verify kubelet is running
systemctl status kubelet
# Output: active (running)
# Step 7: Exit and verify from the control plane
exit
kubectl get nodes
Expected final state (may take 30-60 seconds):
NAME STATUS ROLES AGE VERSION
controlplane Ready control-plane 10d v1.29.0
node01 Ready <none> 10d v1.29.0
Task 5: Fix a Broken Control Plane Component
Setup
Warning: This task modifies a control plane manifest. Be ready to fix it quickly!
Introduce a typo in the kube-scheduler manifest:
# On the control plane node:
# Backup the scheduler manifest first!
sudo cp /etc/kubernetes/manifests/kube-scheduler.yaml /tmp/kube-scheduler.yaml.bak
# Introduce a typo in the kubeconfig path
sudo sed -i 's|--kubeconfig=/etc/kubernetes/scheduler.conf|--kubeconfig=/etc/kubernetes/schedulerr.conf|' \
/etc/kubernetes/manifests/kube-scheduler.yaml
The Problem
After 30 seconds, try creating a Deployment:
kubectl create deployment test-deploy --image=nginx --replicas=3
kubectl get pods
NAME READY STATUS RESTARTS AGE
test-deploy-6d9f4b7c8-xxxxx 0/1 Pending 0 2m
test-deploy-6d9f4b7c8-yyyyy 0/1 Pending 0 2m
test-deploy-6d9f4b7c8-zzzzz 0/1 Pending 0 2m
All Pods are Pending and not being scheduled.
Your Task
Diagnose why Pods are stuck in Pending and fix the issue.
Show Answer
# Step 1: Check why Pods are pending
kubectl describe pod test-deploy-6d9f4b7c8-xxxxx
# Events section is empty — no scheduler is attempting to schedule!
# Step 2: Check the scheduler
kubectl get pods -n kube-system | grep scheduler
# The scheduler Pod is either missing or in CrashLoopBackOff
kubectl logs -n kube-system kube-scheduler-controlplane
# Error: "failed to read kubeconfig" or similar
# Step 3: Check the scheduler manifest
cat /etc/kubernetes/manifests/kube-scheduler.yaml
# Notice: --kubeconfig=/etc/kubernetes/schedulerr.conf (extra 'r')
# Step 4: Fix the typo
sudo vi /etc/kubernetes/manifests/kube-scheduler.yaml
# Change: schedulerr.conf → scheduler.conf
# Or restore from backup:
sudo cp /tmp/kube-scheduler.yaml.bak /etc/kubernetes/manifests/kube-scheduler.yaml
# Step 5: Wait 15-30 seconds for the kubelet to restart the scheduler
# Step 6: Verify
kubectl get pods -n kube-system | grep scheduler
# Should be Running
kubectl get pods
# test-deploy Pods should now be Running (or at least Scheduling)
Expected final state:
NAME READY STATUS RESTARTS AGE
test-deploy-6d9f4b7c8-xxxxx 1/1 Running 0 5m
test-deploy-6d9f4b7c8-yyyyy 1/1 Running 0 5m
test-deploy-6d9f4b7c8-zzzzz 1/1 Running 0 5m
Task 6: Fix DNS Resolution
Setup
Scale down CoreDNS to simulate a DNS failure:
kubectl scale deployment -n kube-system coredns --replicas=0
The Problem
# Try to resolve a Service name from a Pod
kubectl run dnstest --image=busybox:1.36 --restart=Never --rm -it -- nslookup kubernetes.default
;; connection timed out; no servers could be reached
DNS resolution fails for all Pods in the cluster.
Your Task
Diagnose the DNS failure and restore DNS functionality.
Show Answer
# Step 1: Check CoreDNS Pods
kubectl get pods -n kube-system -l k8s-app=kube-dns
# No resources found — CoreDNS has 0 replicas!
# Step 2: Check the CoreDNS Deployment
kubectl get deployment -n kube-system coredns
# DESIRED: 0, CURRENT: 0
# Step 3: Scale CoreDNS back up
kubectl scale deployment -n kube-system coredns --replicas=2
# Step 4: Wait for CoreDNS Pods to be ready
kubectl get pods -n kube-system -l k8s-app=kube-dns -w
# Wait until both Pods show READY 1/1
# Step 5: Verify DNS works
kubectl run dnstest --image=busybox:1.36 --restart=Never --rm -it -- nslookup kubernetes.default
Expected final output:
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: kubernetes.default.svc.cluster.local
Address: 10.96.0.1
Task 7: Fix NetworkPolicy Issue
Setup
Create a namespace with a default-deny NetworkPolicy:
# Create namespace and Pods
kubectl create namespace np-lab
kubectl run web --image=nginx:1.25 -n np-lab --labels="app=web" --port=80
kubectl run client --image=busybox:1.36 -n np-lab --labels="app=client" --command -- sleep 3600
# Wait for Pods to be ready
kubectl wait --for=condition=ready pod/web -n np-lab --timeout=60s
kubectl wait --for=condition=ready pod/client -n np-lab --timeout=60s
# Apply a default-deny ingress policy
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: np-lab
spec:
podSelector: {}
policyTypes:
- Ingress
EOF
The Problem
# Try to reach the web Pod from the client Pod
WEB_IP=$(kubectl get pod web -n np-lab -o jsonpath='{.status.podIP}')
kubectl exec -n np-lab client -- wget -qO- --timeout=3 http://$WEB_IP
wget: download timed out
command terminated with exit code 1
The client Pod can’t reach the web Pod because of the default-deny NetworkPolicy.
Your Task
Create a NetworkPolicy that allows the client Pod (label app=client) to access the web Pod (label app=web) on port 80, while keeping the default-deny for all other traffic.
Show Answer
# Step 1: Check the existing NetworkPolicies
kubectl get networkpolicies -n np-lab
kubectl describe networkpolicy default-deny-ingress -n np-lab
# This policy denies all ingress traffic to all Pods in the namespace
# Step 2: Create an allow policy for client → web on port 80
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-client-to-web
namespace: np-lab
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: client
ports:
- protocol: TCP
port: 80
EOF
# Step 3: Verify connectivity
WEB_IP=$(kubectl get pod web -n np-lab -o jsonpath='{.status.podIP}')
kubectl exec -n np-lab client -- wget -qO- --timeout=5 http://$WEB_IP
Expected final output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
The client can now reach the web Pod on port 80, while all other traffic remains denied by the default-deny policy.
Cleanup
Remove all resources created during this lab:
# Delete individual Pods
kubectl delete pod crash-app pull-app pending-app --ignore-not-found
# Delete the test Deployment
kubectl delete deployment test-deploy --ignore-not-found
# Delete the NetworkPolicy lab namespace (removes everything in it)
kubectl delete namespace np-lab --ignore-not-found
# Restore CoreDNS replicas (if you changed them)
kubectl scale deployment -n kube-system coredns --replicas=2
# Restore the scheduler manifest (if you backed it up)
# sudo cp /tmp/kube-scheduler.yaml.bak /etc/kubernetes/manifests/kube-scheduler.yaml
echo "Cleanup complete!"
Lab Summary
| Task | Scenario | Key Skill |
|---|---|---|
| 1 | CrashLoopBackOff Pod | kubectl logs --previous, fix command |
| 2 | ImagePullBackOff Pod | kubectl describe pod, fix image name |
| 3 | Pending Pod | kubectl describe pod, fix resources/nodeSelector |
| 4 | NotReady Node | systemctl status/start kubelet |
| 5 | Broken Scheduler | Check static Pod manifest, fix typo |
| 6 | DNS Failure | Check CoreDNS, scale up |
| 7 | NetworkPolicy Blocking | Create allow NetworkPolicy |
Pro Tip: Run through this entire lab under timed conditions. Set a timer for 45 minutes and try to complete all 7 tasks. On the real CKA exam, you’ll face similar scenarios with the same time pressure.
What’s Next
Congratulations — you’ve completed the Troubleshooting chapter! This is the single most important chapter for the CKA exam (30% of the total score).
Key takeaways:
- Always follow the systematic debugging flow: observe → diagnose → fix → verify
- Know your tools:
kubectl,journalctl,crictl - Pod issues are the most common — master the status meanings
- Node issues require SSH and
systemctl/journalctl - Control plane issues involve static Pod manifests in
/etc/kubernetes/manifests/ - Networking issues require understanding DNS, Services, and NetworkPolicies