DEV Community

Akash for MechCloud Academy

Posted on

Part 12: Sherlock Holmes in the Cluster: A Practical Guide to Debugging

So far in our journey, we have built a robust, stateful, and well-behaved application. But even in the most well-managed cluster, things go wrong. An image tag has a typo. A configuration change breaks the application's startup logic. A pod runs out of memory.

Being a successful Kubernetes practitioner isn't about avoiding errors; it's about being able to efficiently diagnose and fix them. It's time to put on our detective hats and learn how to investigate when things go awry.

Your Detective Toolkit

When a Pod is misbehaving, you have three primary kubectl commands at your disposal. Knowing which one to use is the key to a speedy investigation.

  1. kubectl describe pod <pod-name>

    • The Case File: This is the most important command to start with. It gives you the full "biography" of a Pod, including its configuration, status, and IP address. Crucially, at the very bottom, it has an Events section. These events are the log of what Kubernetes itself has tried to do with your Pod. It's the first place to look for infrastructure-level issues.
  2. kubectl logs <pod-name>

    • The Witness Testimony: This command streams the standard output (stdout) from the container running inside your Pod. It tells you what the application is saying. If the Pod is running but the app is throwing errors, this is where you'll find them.
  3. kubectl exec

    • Going Undercover: This command lets you open a shell directly inside a running container. It's the ultimate tool for hands-on investigation. You can check for configuration files, test network connectivity from within the Pod, or run diagnostic tools.

The Investigation: A Case of a Broken App

Let's investigate a crime scene. We'll deploy an application that is deliberately broken in two different ways.

Create a file named broken-app.yaml:

# broken-app.yaml apiVersion: apps/v1 kind: Deployment metadata: name: broken-app spec: replicas: 1 selector: matchLabels: app: broken-app template: metadata: labels: app: broken-app spec: containers: - name: main-app image: nginxx:1.21-alpine # Clue #1: A typo command: ["sh", "-c", "echo 'Starting...' && sleep 5 && exit 1"] # Clue #2: A faulty command 
Enter fullscreen mode Exit fullscreen mode

Now, apply this broken configuration:

kubectl apply -f broken-app.yaml 
Enter fullscreen mode Exit fullscreen mode

Let the investigation begin!

Step 1: Survey the Scene
Check the status of your Pods.

kubectl get pods 
Enter fullscreen mode Exit fullscreen mode

You'll immediately see something is wrong.

NAME READY STATUS RESTARTS AGE broken-app-5b5f76f6b4-xyz12 0/1 ImagePullBackOff 0 20s 
Enter fullscreen mode Exit fullscreen mode

The status is ImagePullBackOff. This tells us Kubernetes is trying to pull the container image but is failing repeatedly.

Step 2: Examine the Case File (describe)
Let's use describe to find out why. (Remember to use your specific Pod name).

kubectl describe pod broken-app-5b5f76f6b4-xyz12 
Enter fullscreen mode Exit fullscreen mode

Scroll down to the Events section at the bottom. You will find the smoking gun.

Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 1m default-scheduler Successfully assigned default/broken-app... Normal Pulling 25s (x2 over 1m) kubelet Pulling image "nginxx:1.21-alpine" Warning Failed 23s (x2 over 1m) kubelet Failed to pull image "nginxx:1.21-alpine": rpc error... Warning Failed 23s (x2 over 1m) kubelet Error: ErrImagePull 
Enter fullscreen mode Exit fullscreen mode

The event log is crystal clear: Failed to pull image "nginxx:1.21-alpine". We have a typo!

Step 3: Correct Clue #1 and Re-apply
Fix the image name in broken-app.yaml from nginxx to nginx and apply the change.

# In broken-app.yaml # ... image: nginx:1.21-alpine # Corrected # ... 
Enter fullscreen mode Exit fullscreen mode
kubectl apply -f broken-app.yaml 
Enter fullscreen mode Exit fullscreen mode

Step 4: A New Problem Arises
The old Pod will be terminated, and a new one will be created. Let's check the status again.

kubectl get pods 
Enter fullscreen mode Exit fullscreen mode
NAME READY STATUS RESTARTS AGE broken-app-7dcfc75c8d-abc45 0/1 CrashLoopBackOff 2 30s 
Enter fullscreen mode Exit fullscreen mode

A new error! CrashLoopBackOff means the container is starting, but the application inside is exiting with an error code almost immediately. Kubernetes tries to restart it, it crashes again, and the loop continues.

Step 5: Question the Witness (logs)
The image is fine, so the problem must be inside the container. Let's check the application logs.

kubectl logs broken-app-7dcfc75c8d-abc45 
Enter fullscreen mode Exit fullscreen mode

The output is simply: Starting...

This tells us the command we specified is running, but it doesn't tell us why it's crashing. This is because the container crashes so fast. Let's ask for the logs of the previous attempt.

kubectl logs broken-app-7dcfc75c8d-abc45 --previous 
Enter fullscreen mode Exit fullscreen mode

The result is the same. The exit 1 command in our manifest is causing the container to stop with an error code, which Kubernetes interprets as a crash.

Step 6: Correct Clue #2 and Close the Case
Remove the entire command section from broken-app.yaml to let the nginx image use its default startup command.

# In broken-app.yaml - REMOVE THE FOLLOWING LINES # # command: ["sh", "-c", "echo 'Starting...' && sleep 5 && exit 1"] 
Enter fullscreen mode Exit fullscreen mode

Apply the final fix:

kubectl apply -f broken-app.yaml 
Enter fullscreen mode Exit fullscreen mode

Check the status one last time:

kubectl get pods 
Enter fullscreen mode Exit fullscreen mode
NAME READY STATUS RESTARTS AGE broken-app-6447d96c4d-qrst6 1/1 Running 0 15s 
Enter fullscreen mode Exit fullscreen mode

Success! Our Pod is Running. By systematically using describe for cluster-level issues and logs for application-level issues, we solved the case.

What's Next

We now have the fundamental skills to diagnose and fix the most common problems in a Kubernetes cluster.

As our applications have grown more complex, so have our manifests. We now have YAML files for Deployments, Services, ConfigMaps, PVCs, and Ingress rules. Managing all these related files for a single application is becoming cumbersome. What if we want to share our application so someone else can deploy it with one command?

In the next part, we will solve this problem of YAML sprawl by introducing Helm, the package manager for Kubernetes.

Top comments (0)