Yet another way to troubleshoot K8S applications
There are plenty of articles explains how to debug K8S applications, for example
- Troubleshoot Applications
- Connect with SSH to Azure Kubernetes Service (AKS) cluster nodes for maintenance or troubleshooting
Due to the nature of container's isolation, the application running from container uses its own namespaces and cgroups, the container image will also keep itself as small as possible. Thus the task to debug application running from container will be challenging. The container itself usually lacks of debugging tools, for example, capture a network trace requires tcpdump, but most of time, the container image won't include it.
This article explains how to use nsenter to debug applications running from K8S cluster. nsenter basically
run program with namespaces of other processes
With nsenter, you can access container's namespace and use host avaiable commands.
To use nsenter, we need to get the process ID of the application running under container, here are the steps
1.List containers and container IDs
Running below command from K8S master node(Linux) will output all containers and their corresponding docker id, since Pod can have 1+ containers, so for some Pods, we can see there will be multiple contains and docker ids from output.
kubectl get pods --all-namespaces -o=custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,NODE:.spec.nodeName,CONTAINERS:.spec.containers[*].name,CONTAINERIDS:.status.containerStatuses[*].containerID | sed -e 's/docker:\/\/\(.\{12\}\).\{52\}/\1/g'
Below is the sample output from my K8S cluster
NAMESPACE NAME NODE CONTAINERS
...
NAMESPACE NAME NODE CONTAINERS CONTAINERIDS
...
ghost arracs-ghost-754d44cf5c-pg4bp k8s-agentpool1-30506800-0 arracs-ghost 555d5bc1f3de
ghost arracs-ghost-mariadb-0 k8s-agentpool1-30506800-1 mariadb 2af387235184
...
kube-system kube-dns-8446b8bd4c-9ssgl k8s-agentpool1-30506800-1 kubedns,dnsmasq,sidecar 3b2fd4125a81,a6ecc2a6517f,353afb14b350
...
For example, container arracs-ghost is running from agent node k8s-agentpool1-30506800-0 and its container id is 555d5bc1f3de, in following steps, we will use this container id for demostration purpose.
Note: Running above command from Windows won't work as sed
is not available from Windows. So in Windows, just remove | sed -e 's/docker:\/\/\(.\{12\}\).\{52\}/\1/g'
of above command, then from output, find string similar like docker://3b2fd4125a81ffc061ee938b5fd3e4286b801187318ea3f3a3e93fcef9381015, the highlighted 12 characters is container ID.
2. Map container ID to PID
To map container id to PID, we need to SSH into agent node k8s-agentpool1-30506800-0, then run docker inspect --format '{{ .State.Pid }}' 555d5bc1f3de
, it will output PID, in our case, the PID is 15599
.
To SSH into agent node, refer to Connect with SSH to Azure Kubernetes Service (AKS) cluster nodes for maintenance or troubleshooting, if it is a Azure VM, assign a public IP address to its NIC then follow article SSH troubleshooting to add a user to login.
3. Enter network namespace
Now with PID, we are able to enter application's network namespace, sample command is sudo nsenter -t 15599 -n
, from that namespace, you will be able to run any agent node avaiable commands inside of container's network namespace. For example, before enter container's network namespace, ifconfig -a
on agent node looks like below
ifconfig
azure0 Link encap:Ethernet HWaddr 00:0d:3a:a0:64:b0
inet addr:10.240.0.35 Bcast:0.0.0.0 Mask:255.240.0.0
inet6 addr: fe80::20d:3aff:fea0:64b0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1920464 errors:0 dropped:0 overruns:0 frame:0
TX packets:2445984 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2739355202 (2.7 GB) TX bytes:606845859 (606.8 MB)
azv06977889865 Link encap:Ethernet HWaddr 32:ec:84:52:84:e8
inet6 addr: fe80::30ec:84ff:fe52:84e8/64 Scope:Link
UP BROADCAST RUNNING MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:45905 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:1930882 (1.9 MB)
...
docker0 Link encap:Ethernet HWaddr 02:42:89:1c:aa:6a
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
eth0 Link encap:Ethernet HWaddr 00:0d:3a:a0:64:b0
inet6 addr: fe80::20d:3aff:fea0:64b0/64 Scope:Link
UP BROADCAST RUNNING MTU:1500 Metric:1
RX packets:7484022 errors:0 dropped:0 overruns:0 frame:0
TX packets:3124398 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:8152611809 (8.1 GB) TX bytes:885562888 (885.5 MB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:22171 errors:0 dropped:0 overruns:0 frame:0
TX packets:22171 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:18088794 (18.0 MB) TX bytes:18088794 (18.0 MB)
After enter container's network namespace, ifconfig -a
shows below which indeed is container's network configuration.
eth0 Link encap:Ethernet HWaddr 4a:18:c7:56:ab:ab
inet addr:10.240.0.45 Bcast:0.0.0.0 Mask:255.240.0.0
UP BROADCAST RUNNING MTU:1500 Metric:1
RX packets:186316 errors:0 dropped:0 overruns:0 frame:0
TX packets:153705 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:3333925122 (3.3 GB) TX bytes:62593512 (62.5 MB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
If we run tcpdump -i eth0
, it will capture all the traffics on container's eth0 interface.
4. Enter mount namespace
To enter mount namespace, run sudo nsenter -t 15599 -m
. This command is useful in accessing mounted volumes inside of a container.
5. Quit from nsenter
To quite from container's namespace, simple type exit
, it will quit to agent node's shell.
6. Run command directly with nsenter
We can also add a sub-command directly to the end of nsenter's command, for example, sudo nsenter -t 15599 -n ip addr
, it will just run ip addr
inside of container's network namespace and output the result.