调试 Kubernetes 最简单方法
本文介绍了 Kubectl debug 和临时容器等调试方法。
作者:Martin Heinz 翻译:Bach (K8sMeetup)
校对:星空下的文仔 来源:K8sMeetup社区
kubectl logs 或者 kubectl describe pod 便足以找到问题所在,但有时候,一些问题会特别难查。这种情况下,大家可能会尝试使用 kubectl exec,但有时候这样也还不行,因为 Distroless 等容器甚至不允许通过 SSH 进入 shell。那么,如果以上所有方法都失败了,我们要怎么办?kubectl debug。这是不久前添加的一个新命令(v1.18),允许调试正在运行的 pod。它会将名为 EphemeralContainer(临时容器)的特殊容器注入到问题 Pod 中,让我们查看并排除故障。kubectl debug 看起来非常不错,但要使用它需要临时容器,临时容器到底是什么?container。但与普通容器不同的是,临时容器不用于构建应用程序,而是用于检查。我们不会在创建 Pod 时定义它们,而使用特殊的 API 将其注入到运的行 Pod 中,来运行命令并检查 Pod 环境。除了这些不同,临时容器还缺少一些基本容器的字段,例如 ports、resources。kubectl debug,那么如何启用临时容器的功能门?这取决于集群设置。例如,现在使用kubeadm启动创建集群,那么可以使用以下集群配置来启用临时容器:apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.20.2
apiServer:
extraArgs:
feature-gates: EphemeralContainers=true
# File: config.yaml
# Run: kind create cluster --config ./config.yaml --name kind --image=kindest/node:v1.20.2
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
EphemeralContainers: true
nodes:
- role: control-plane
~ $ kubectl explain pod.spec.ephemeralContainers
KIND: Pod
VERSION: v1
RESOURCE: ephemeralContainers <[]Object>
DESCRIPTION:
List of ephemeral containers run in this pod....
...
kubectl debug。从简单的例子开始:~ $ kubectl run some-app --image=k8s.gcr.io/pause:3.1 --restart=Never
~ $ kubectl debug -it some-app --image=busybox --target=some-app
Defaulting debug container name to debugger-tfqvh.
If you don't see a command prompt, try pressing enter.
/ #
# From other terminal...
~ $ kubectl describe pod some-app
...
Containers:
some-app:
Container ID: containerd://60cc537eee843cb38a1ba295baaa172db8344eea59de4d75311400436d4a5083
Image: k8s.gcr.io/pause:3.1
Image ID: k8s.gcr.io/pause@sha256:f78411e19d84a252e53bff71a4407a5686c46983a2c2eeed83929b888179acea
...
Ephemeral Containers:
debugger-tfqvh:
Container ID: containerd://12efbbf2e46bb523ae0546b2369801b51a61e1367dda839ce0e02f0e5c1a49d6
Image: busybox
Image ID: docker.io/library/busybox@sha256:ce2360d5189a033012fbad1635e037be86f23b65cfd676b436d0931af390a2ac
Port: <none>
Host Port: <none>
State: Running
Started: Mon, 15 Mar 2021 20:33:51 +0100
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>
some-app 的 Pod 来进行“调试”。然后针对这个 Pod 运行 kubectl debug,指定 busybox 为临时容器的镜像,并作为原始容器的目标。此外,还需要包括 -it 参数,以便我们立即附加到容器获得 shell 会话。kubectl debug 是非常强大的工具,但有时向 Pod 添加一个容器还不足以获取 Pod 的另一个容器中运行的应用程序相关信息。当故障容器不包括必要的调试工具甚至 shell 时,可能就是这种情况。在这种情况下,我们可以使用 Process Sharing(进程共享)来使用注入的临时容器检查 Pod 的原有容器。spec.shareProcessNamespace 设置为 true,并将一个临时容器注入其中。这样有点麻烦,尤其是需要调试多个 Pod 或容器,亦或者需要重复执行该操作时。幸运的是,kubectl debug 可以使用 --share-processes 做到:~ $ kubectl run some-app --image=nginx --restart=Never
~ $ kubectl debug -it some-app --image=busybox --share-processes --copy-to=some-app-debug
Defaulting debug container name to debugger-tkwst.
If you don't see a command prompt, try pressing enter.
/ # ps ax
PID USER TIME COMMAND
1 root 0:00 /pause
8 root 0:00 nginx: master process nginx -g daemon off;
38 101 0:00 nginx: worker process
39 root 0:00 sh
46 root 0:00 ps ax
~ $ cat /proc/8/root/etc/nginx/conf.d/default.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
...
--share-processes 还包括了 --copy-to=new-pod-name,这是因为我们需要创建一个新的 Pod,其名称由该 flag 指定。如果我们从另一个终端列出正在运行的 Pod,我们将看到以下内容:# From other terminal:
~ $ kubectl get pods
NAME READY STATUS RESTARTS AGE
some-app 1/1 Running 0 23h
some-app-debug 2/2 Running 0 20s
~ $ kubectl get pod some-app-debug -o json | jq .spec.shareProcessNamespace
true
kubectl debug:~ $ kubectl run distroless-python --image=martinheinz/distroless-python --restart=Never
~ $ kubectl exec -it distroless-python -- /bin/sh
# id
/bin/sh: 1: id: not found
# ls
/bin/sh: 2: ls: not found
# env
/bin/sh: 3: env: not found
#
...
kubectl debug -it distroless-python --image=praqma/network-multitool --target=distroless-python -- sh
Defaulting debug container name to debugger-rvtd4.
If you don't see a command prompt, try pressing enter.
/ # ping localhost
PING localhost(localhost (::1)) 56 data bytes
64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.025 ms
64 bytes from localhost (::1): icmp_seq=2 ttl=64 time=0.044 ms
64 bytes from localhost (::1): icmp_seq=3 ttl=64 time=0.027 ms
praqma/network-multitool 将临时容器注入到 Pod 中,该镜像包含了 curl、ping、telnet 等工具,现在我们可以进行所有必要的故障排除。~ $ kubectl run distroless-python --image=martinheinz/distroless-python --restart=Never
~ $ kubectl debug -it distroless-python --image=busybox --share-processes --copy-to=distroless-python-debug
Defaulting debug container name to debugger-l692h.
If you don't see a command prompt, try pressing enter.
/ # ps ax
PID USER TIME COMMAND
1 root 0:00 /pause
8 root 0:00 /usr/bin/python3.5 sleep.py # Original container is just sleeping forever
14 root 0:00 sh
20 root 0:00 ps ax
/ # cat /proc/8/root/app/sleep.py
import time
print("sleeping for 1 hour")
time.sleep(3600)
kubectl debug 以及 --share-processes --copy-to=...,它创建了一个新的 Pod,带有额外的临时容器,可以访问所有进程。当我们列出正在运行的进程时,能看到应用程序容器的进程有 PID 8,可以用它来探索文件和环境。为此,我们需要通过 /proc/<PID>/... 目录,这个例子中是 /proc/8/root/app/...。~ $ kubectl get pods
NAME READY STATUS RESTARTS AGE
crashing-app 0/1 CrashLoopBackOff 1 8s
~ $ kubectl debug crashing-app -it --copy-to=crashing-app-debug --container=crashing-app -- sh
If you don't see a command prompt, try pressing enter.
# id
uid=0(root) gid=0(root) groups=0(root)
#
...
# From another terminal
~ $ kubectl get pods
NAME READY STATUS RESTARTS AGE
crashing-app 0/1 CrashLoopBackOff 3 2m7s
crashing-app-debug 1/1 Running 0 16s
kubectl debug 允许通过创建 Pod 来调试节点,该 Pod 将在指定节点上运行,节点的根文件系统安装在 /root 目录中。我们甚至可以用 chroot 访问主机二进制文件,这本质上充当了节点的 SSH 连接:~ $ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane,master 25h v1.20.2
~ $ kubectl debug node/kind-control-plane -it --image=ubuntu
Creating debugging pod node-debugger-kind-control-plane-hvljt with container debugger on node kind-control-plane.
If you don't see a command prompt, try pressing enter.
root@kind-control-plane:/# chroot /host
# head kind/kubeadm.conf
apiServer:
certSANs:
- localhost
- 127.0.0.1
extraArgs:
feature-gates: EphemeralContainers=true
runtime-config: ""
apiVersion: kubeadm.k8s.io/v1beta2
clusterName: kind
controlPlaneEndpoint: kind-control-plane:6443
node/... 作为参数显式运行 kubectl debug 以访问我们集群的节点。在那之后,当连接到Pod后,我们使用 chroot /host 突破 chroot,并完全进入主机。最后,为了验证是否真的可以看到主机上的所有内容,我们了查看一部分的 kubeadm.conf,最终看到我们在文章开头配置的内容 feature-gates: EphemeralContainers=true。推荐阅读:
评论
