kubectl debug, ephemeral containers and crictl

How to debug when the image has no shell: ephemeral containers, debug copies, node debugging Pods and crictl.

35 min 2 hands-on tasksTroubleshooting · 30% of the exam

Key ideas in this lesson · tap one for a simple explanation

What you will be able to do

  • Add an ephemeral debug container to a running Pod and see the app's processes, without restarting the Pod.
  • Debug a crashing Pod through a changed copy, and delete the copy afterwards.
  • Start a node debugging Pod, read the node's files through it, and delete it when you are done.
  • Find a Pod's containers on the node with crictl and run a command in one.

Many production images are distroless: they hold the app and nothing else. No shell, no ps, no wget. So kubectl exec into them fails with executable file not found. kubectl debug fixes that: it brings a second image that has the tools. The exam expects you to use it quickly, and to clean up what it leaves behind.

When the itself is broken, kubectl cannot help at all. Then crictl, run on the , is the fallback.

Three ways to use kubectl debug

Ephemeral container

joins a running Pod, which is not restarted

kubectl debug POD --target=app

Debug copy

a new Pod you are free to break

kubectl debug POD --copy-to=NEW

Node debugger

a Pod on the node, with the node's files at /host

kubectl debug node/​NODE

All three bring their own image, such as busybox:1.36, with the tools the app image lacks.

Which tool, when

ToolWhat you getUse it when
kubectl execA command inside an existing , with that image's toolsThe image has a shell and the container is running
kubectl debug POD --image=… --target=CAn in the same , which sees container C's processesThe image has no shell, and the Pod must not restart
kubectl debug POD --copy-to=NEW …A new Pod made from the original, with a changed image or commandThe container crashes at start, or you need a different image
kubectl debug node/NODE --image=…A Pod on that node that shares the node's processes and network, with the node's files at /hostYou need the node's files, processes or network
crictl on the nodeThe 's own view: Pods, containers, logs, execThe or the API server is broken

Ephemeral containers

An ephemeral container is a temporary container you add to a Pod that is already running. The Pod is not restarted. Use one to look inside an app whose image has no tools.

  • It is limited. It has no ports, or resource settings, and it is never restarted.
  • It stays. You cannot change or remove it. It stays in spec.ephemeralContainers until the Pod is deleted.
  • --target=app puts it in the same process list as container app. Then ps shows the app's processes, and /proc/1/root is the app's filesystem.
  • Pass --profile=general, as the examples do. It gives the debug container the rights it needs to look at the app's processes.
Attach, use and inspect an ephemeral container
# Interactive, the usual way: you land in a busybox shell next to the app's processes
kubectl -n shop debug -it api-0 --image=busybox:1.36 --target=app -c dbg --profile=general -- sh
#   ps                      # the app's processes, PID 1 is its entrypoint
#   ls /proc/1/root/etc     # the app container's filesystem
#   wget -qO- -T 3 http://localhost:8080/healthz

# Non-interactive: start it with a long-running command, then exec into it
kubectl -n shop debug api-0 --image=busybox:1.36 --target=app -c dbg --profile=general -- sleep 3600
kubectl -n shop exec api-0 -c dbg -- ps

# What it added to the Pod
kubectl -n shop get pod api-0 -o jsonpath='{.spec.ephemeralContainers[*].name}{"\n"}'
kubectl -n shop get pod api-0 -o jsonpath='{.status.ephemeralContainerStatuses[?(@.name=="dbg")].state}{"\n"}'

Debugging a copy

Some containers crash before you can look inside. For those, make a copy of the Pod with one change, for example a command that only sleeps. The original Pod is not touched.

--copy-to: a new Pod you are free to break
# Same Pod plus a busybox container, sharing one process namespace
kubectl -n shop debug api-0 --copy-to=api-debug --image=busybox:1.36 --share-processes -- sleep 3600

# Same Pod, but container app gets a different image
kubectl -n shop debug api-0 --copy-to=api-debug --set-image=app=busybox:1.36

# Same Pod, but container app runs a different command (it crashed on start)
kubectl -n shop debug api-0 --copy-to=api-debug --container=app -- sh -c 'sleep 3600'

kubectl -n shop get pod api-debug
kubectl -n shop delete pod api-debug        # the copy is yours to clean up

The copy is a plain Pod with a new name. No controller manages it, so nothing deletes it for you. It also drops the original's and probes, so no sends it traffic. To find why a container crashes, start a copy that only sleeps. Then kubectl exec into it and run the real program by hand, to see its error.

Debugging a node

kubectl debug node/cka-worker --image=busybox:1.36 starts a Pod on that node. Through it you can look at the node as if you had logged in:

  • It shares the node's processes and network, so ps shows the kubelet.
  • The node's whole filesystem is mounted at /host. chroot /host makes the shell use the node's own programs.
  • The Pod is named node-debugger-cka-worker-<suffix> and lands in the current . Pass -n so you know where it went.
  • Exiting the shell does not delete it. It stays as Completed until you delete it.
  • Add --profile=sysadmin only when you need a privileged container.
A node debugging Pod
kubectl debug node/cka-worker -n shop -it --image=busybox:1.36 --profile=general -- sh
#   cat /host/etc/hostname
#   ps | grep kubelet                   # you see the node's processes
#   ls /host/var/log/pods
#   chroot /host                        # from here on, the node's own programs
kubectl -n shop get pods | grep node-debugger
kubectl -n shop delete pod node-debugger-cka-worker-x7k2p

# Non-interactive variant
kubectl debug node/cka-worker -n shop --image=busybox:1.36 --profile=general -- sleep 600
kubectl -n shop exec node-debugger-cka-worker-x7k2p -- cat /host/etc/hostname

crictl on the node

crictl talks to the container runtime directly, without the API server. You run it in a shell on the node. These three commands find a Pod's containers and run a command in one:

crictl on the node
# exam: ssh <node>, then sudo -i    lab: docker exec -it cka-worker bash
crictl pods --name api-0                      # Pod sandbox ID and state
crictl ps --pod <pod-id>                      # running containers of that Pod
crictl exec <container-id> cat /etc/hostname  # only works if the image has the binary

crictl can also list stopped containers and read their logs. Control plane failures covers those commands.

In the official docs· open on exam day too

Hands-on task 1 of 2ts-debug-1
~6 min5% weightTroubleshooting

Debug a Pod that has no shell

Pod payroll in namespace tsx-debug runs an image without a shell, so kubectl exec into it fails. Without restarting or recreating the Pod, add a debug container named dbg using image busybox:1.36 that shares the process namespace of container app and keeps running with the command sleep 3600. Then, from dbg, write the command line of process ID 1 to /tmp/cka/payroll-pid1.txt.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-debug-1/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-debug-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-debug-1/cleanup | bash
Hands-on task 2 of 2ts-debug-2
~5 min4% weightTroubleshooting

Read a node file through a node debugging Pod

Using kubectl debug, start a node debugging Pod on node cka-worker in namespace tsx-node with image busybox:1.36. Through that Pod, read the node's file /etc/hostname and write its content to /tmp/cka/worker-hostname.txt. Delete the debugging Pod when you are done.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-debug-2/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-debug-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-debug-2/cleanup | bash

Finished this lesson?

0 of 2 tasks passed. Your readiness counts tasks, so run them before you move on.