DevOps Day 53: Troubleshooting a Multi-Container Pod in Kubernetes¶
Today's task was a fantastic, real-world troubleshooting scenario in Kubernetes. My objective was to fix a broken Nginx and PHP-FPM application. The challenge was that the kubectl get pods command showed the Pod and both its containers were Running perfectly. This was a great lesson that "Running" does not always mean "Working."
This was a deep dive into the inner workings of multi-container pods, shared volumes, and ConfigMaps. I had to play detective, using kubectl describe, logs, and edit to find a subtle but critical misconfiguration in how the two containers were communicating. This document is my very detailed, first-person guide to that entire successful troubleshooting journey.
Table of Contents¶
The Task¶
My objective was to investigate and fix a broken Nginx and PHP-FPM setup running in a Kubernetes Pod. The key components were:
1. A Pod named nginx-phpfpm.
2. A ConfigMap named nginx-config that held the Nginx configuration.
3. After fixing the configuration, I had to copy an index.php file from the jump host into the container's document root.
4. The final website had to be accessible.
My Step-by-Step Solution¶
My approach was to systematically investigate each component to find the root cause, apply the fix, and then deploy the final piece of content.
Phase 1: The Investigation¶
- I connected to the jump host. My first command,
kubectl get pods, showed thenginx-phpfpmpod was2/2 Running. This was a misleading sign of health. - The "Website" button showed "File not found" or a 404 error, confirming a problem.
-
This was the first key clue. I checked the logs of the Nginx container:
The logs contained the error:kubectl logs nginx-phpfpm -c nginx-containerFastCGI sent in stderr: "Primary script unknown". This told me Nginx was successfully passing a request to PHP-FPM, but PHP-FPM could not find the script file Nginx asked for. -
This was the second key clue. I used
kubectl describe pod nginx-phpfpmto inspect the Pod's structure. I looked at theMountsfor both containers and found the critical mismatch:- The
nginx-containermounted the shared volume at/var/www/html. - The
php-fpm-containermounted the same shared volume at/usr/share/nginx/html.
- The
-
Finally, I inspected the Nginx configuration in the
nginx-configConfigMap withkubectl describe configmap nginx-config. It showed that Nginx was passing theSCRIPT_FILENAMEto PHP-FPM using its own document root:$document_root$fastcgi_script_name, which resolved to/var/www/html/index.php. This confirmed the path mismatch.
Phase 2: Applying the Fix¶
- I edited the ConfigMap directly in the cluster.
kubectl edit configmap nginx-config - Inside the editor, I changed a single line in the
nginx.confdata. I changed thefastcgi_passparameter to use the correct communication method for containers within the same pod. The originalfastcgi_pass 127.0.0.1:9000;was replaced. In my successful attempt, I updated the configuration as follows to ensure Nginx and PHP-FPM could communicate and find files correctly.(Note: The exact fix can vary; another valid fix is to change the pod definition so both containers use the same mount path. Editing the config is often faster.)# ... inside the location ~ \.php$ block ... fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; # Hardcoded path for PHP-FPM fastcgi_pass localhost:9000; - After saving the ConfigMap, I needed to tell the running Nginx container to reload its configuration.
kubectl exec nginx-phpfpm -c nginx-container -- nginx -s reload
Phase 3: Deploying the Content¶
- The
kubectl cpcommand would fail if the destination directory didn't exist. I first created it inside the container usingexec.kubectl exec nginx-phpfpm -c nginx-container -- mkdir -p /var/www/html - Now I could copy the file. I specified the
nginx-containerwith the-cflag because the Pod has multiple containers.kubectl cp /home/thor/index.php nginx-phpfpm:/var/www/html/index.php -c nginx-container - Finally, clicking the "Website" button showed the correct output from the
index.phpfile.
Why Did I Do This? (The "What & Why")¶
- Multi-Container Pods: This task was a perfect example of this powerful pattern. Instead of putting Nginx and PHP in the same container (which is an anti-pattern), they are in separate containers within the same Pod. This allows me to manage and update them independently. Because they are in the same Pod, they share a network namespace (so they can talk via localhost) and can share volumes.
- Shared EmptyDir Volume: An EmptyDir volume is a temporary directory that is created when a Pod starts and is destroyed when the Pod is deleted. Its primary purpose is to provide a shared filesystem for containers running within the same Pod. In my task, it was the shared space where the nginx-container would look for index.php and the php-fpm-container would execute it.
- ConfigMaps: A ConfigMap is a Kubernetes object used to store non-confidential configuration data in key-value pairs. In this task, the entire nginx.conf file was stored in a ConfigMap. This is a crucial best practice because it decouples the configuration from the container image. I was able to fix the Nginx configuration by editing the ConfigMap, and Kubernetes automatically updated the file inside the running container, without me needing to rebuild the image.
Deep Dive: The Anatomy of a Multi-Container Failure¶
The "Primary script unknown" error was the key. This error comes from PHP-FPM and it means "The web server asked me to run a script, but I can't find that script at the path it gave me." My troubleshooting revealed the exact sequence of this failure.
[Image of a multi-container Kubernetes Pod]
- The Request: A user request for
index.phparrives at the Nginx container. - Nginx's View: Nginx is configured with
root /var/www/html. It sees the request for/index.phpand combines them. It decides the script's filename is/var/www/html/index.php. - The Hand-off: Nginx passes the request to PHP-FPM over the network (
fastcgi_pass localhost:9000) and includes the parameterSCRIPT_FILENAME = /var/www/html/index.php. - PHP-FPM's View: The PHP-FPM container receives this request. It looks in its own filesystem for the file at
/var/www/html/index.php. - The Failure Point: In my
describeoutput, I saw that the shared volume was mounted at/usr/share/nginx/htmlinside the PHP-FPM container. The path/var/www/html/did not exist in its world. - The Error: Because it couldn't find the file, PHP-FPM returned the "Primary script unknown" error back to Nginx, which in turn sent a
404 Not Foundto the user.
My fix in the ConfigMap—hardcoding the SCRIPT_FILENAME path to what Nginx saw—was one way to solve it. An equally valid solution would be to edit the Pod's YAML definition to make both containers mount the shared volume at the exact same path.
Common Pitfalls for Beginners¶
- Running != Working: My first lesson. A Pod showing Running and 2/2 Ready is a good sign, but it only means the container processes have started. It says nothing about whether the application inside is configured correctly.
- Forgetting the -c flag: When a Pod has multiple containers, you must use the -c <container-name> flag for commands like kubectl logs, exec, and cp to tell Kubernetes which container you want to interact with.
- Forgetting to Reload Config: After editing a ConfigMap that's mounted as a file, the application inside the container often needs to be told to reload its configuration. For Nginx, the command nginx -s reload does this gracefully without restarting the process.
- kubectl cp Destination Must Exist: The kubectl cp command will fail if the parent directory of the destination path does not already exist inside the container. I had to create it first with kubectl exec ... mkdir -p.
Exploring the Essential kubectl Commands¶
- kubectl get pods: Lists a summary of all Pods.
- kubectl describe pod [pod-name]: My primary tool for troubleshooting. It shows the Pod's full configuration, including its container definitions, volume mounts, and recent events.
- kubectl describe configmap [cm-name]: Shows the data stored inside a ConfigMap.
- kubectl logs [pod-name] -c [container-name]: Shows the logs from a specific container within a multi-container Pod. This was essential for finding the "Primary script unknown" error.
- kubectl edit configmap [cm-name]: The command to open a ConfigMap's YAML definition in a text editor to make live changes in the cluster.
- kubectl exec [pod-name] -c [container-name] -- [command]: Executes a command in a specific container. I used this to reload Nginx and to create the destination directory.
- kubectl cp [source] [pod-name]:[dest] -c [container-name]: Copies a file from the local machine into a specific container in a Pod.