一、背景
- 容器及其周围的生态系统改变了工程师部署、维护和排查工作负载故障的方式。但是,在 Kubernetes 集群上调试应用程序有时可能会很困难,因为可能在容器中找不到所需的调试工具。许多工程师使用基于精简、发行版构建无发行版的基础镜像,其中甚至没有包管理器或shell,甚至一些团队使用 scratch 作为基础镜像,并且只添加应用程序运行所需的文件。
- 这种常见做法的一些原因是:
-
- 具有较小的攻击区域。
-
- 为了获得更快的扫描性能。
-
- 减小了镜像大小。
-
- 为了有更快的构建和更短 CD/CI 周期。
-
- 减少依赖关系。
- 这些精简的基础镜像不包括用于对应用程序或其依赖项进行故障排查的工具,这是 Kubernetes 临时容器功能最大用途。临时容器允许创建包含可能需要的所有调试工具的容器镜像。一旦需要调试,就可以将临时容器部署到所选的正在运行的 Pod 中。
- 不能将容器添加到已部署的容器,需要更新 spec,并重新创建资源。但是,可以将临时容器添加到现有 Pod 中,以便对线上问题进行故障排查。
二、 临时容器的配置
- 临时容器与常规容器共享相同的 spec。但是,某些字段被禁用,并且某些行为被更改。
- 如下列出了一些重大变化,检查临时容器规范以获取完整列表:
-
- 它们不会重新启动。
-
- 不允许定义资源。
-
- 不允许使用端口。
-
- 不允许使用启动、活动和就绪探测。
三、启动临时容器
- 首先,检查是否启用了临时容器功能:
rust
kubectl debug -it <POD_NAME> --image=busybox
- 如果未启用该功能,将看到类似下面的消息:
rust
Defaulting debug container name to debugger-wg54p.
error: ephemeral containers are disabled for this cluster (error from server: "the server could not find the requested resource").
- 将 EphemeralContainers=true 附加到 kubelet、kube-apiserver、kube-controller-manager、kube-proxy、kube-scheduler 参数中的--feature-gates=后,例如:
rust
...
--feature-gates=RemoveSelfLink=false,EphemeralContainers=true
...
四、使用临时容器
- 现在,集群支持临时容器功能,要创建临时容器,使用 kubectl 命令行工具的 debug 子命令。首先,创建一个 Deployment:
rust
kubectl create deployment nginx-deployment --image=nginx
- 获取需要 debug 的 Pod 的名称:
rust
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-66b6c48dd5-frsv9 1/1 Running 6 62d
- 以下命令将在 pod nginx-deployment-66b6c48dd5-frsv9 中创建一个新的临时容器,它的镜像是 busybox,-i 和 -t 参数允许我们附加到新创建的容器:
rust
$ kubectl debug -it pods/nginx-deployment-66b6c48dd5-frsv9 --image=busybox
- 现在就可以 debug 了:
rust
/ # ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=112 time=9.797 ms
64 bytes from 8.8.8.8: seq=1 ttl=112 time=9.809 ms
^C
/ # nc --help
BusyBox v1.34.1 (2021-11-11 01:55:05 UTC) multi-call binary.
Usage: nc [OPTIONS] HOST PORT - connect
nc [OPTIONS] -l -p PORT [HOST] [PORT] - listen
...
- 当使用 kubectl describe pod <POD_NAME> 命令时,可以看到一个新字段 "Ephemeral Containers",此部分包含临时容器及其属性:
rust
$ kubectl describe pods <POD_NAME>
...
...
Ephemeral Containers:
debugger-thwrn:
Container ID: containerd://eec23aa9ee63d96b82970bb947b29cbacc30685bbc3418ba840dee109f871bf0
Image: busybox
Image ID: docker.io/library/busybox@sha256:e7157b6d7ebbe2cce5eaa8cfe8aa4fa82d173999b9f90a9ec42e57323546c353
Port: <none>
Host Port: <none>
五、与临时容器共享进程命名空间
- 进程命名空间共享一直是一个很好的故障排查选项,此功能可用于临时容器。进程命名空间共享不能应用于现有容器,因此必须创建目标容器的副本。--share-processesflag 在与 --copy-to 一起使用时,可实现进程命名空间共享。这些标志将现有的 Pod spec定义复制到新定义中,并在 spec 中启用了进程命名空间共享:
rust
$ kubectl debug -it <POD_NAME> --image=busybox --share-processes --copy-to=debug-pod
- 运行 ps 命令以查看正在运行的进程,正如所期望的那样,可以从 busybox 容器中看到 /pause,从 nginx-deployment 容器中看到 nginx 进程:
rust
# ps aux
PID USER TIME COMMAND
1 root 0:00 /pause
6 root 0:00 nginx: master process nginx -g daemon off;
11 101 0:00 nginx: worker process
12 root 0:00 sh
17 root 0:00 ps aux
- 使用进程命名空间,共享容器文件系统也是可访问的,这对于调试非常有用,可以使用 /proc//root 链接访问容器。从上面的输出中,可以知道 nginx 的 PID 为 6。
rust
# ls /proc/6/root/etc/nginx
conf.d koi-utf mime.types nginx.conf uwsgi_params fastcgi_params koi-win modules scgi_params win-utf
- 在这里,可以看到目标容器上的 Nginx 目录结构和配置文件。
六、结论
- 临时容器功能无疑给调试排查问题带来了很大便利,而进程命名空间共享允许高级调试功能。
- 如果也使用在 Kubernetes 集群中运行的应用程序,那么值得花时间尝试这些功能。不难想象,一些团队甚至使用这些工具自动执行工作流,例如在 readiness probes 探针失败时自动修复其他容器。