痛点:生产容器里没有调试工具
你一定遇到过这种场景:Pod 出了问题,kubectl exec 进去后发现——容器用的是 distroless 镜像,连 sh 都没有,更别说 curl、tcpdump、strace 了。
传统做法要么改 Deployment 加 sidecar 重新发布,要么临时换镜像重启 Pod。两种方式都会中断现场、破坏复现条件。在分秒必争的故障排查中,这就是致命的时间浪费。
Kubernetes 从 v1.25 起正式 GA 了 Ephemeral Containers(临时容器),让你无需重启 Pod,就能注入一个带完整调试工具的容器,共享目标容器的 PID/Network namespace,实现零侵入式生产调试。
方案:kubectl debug + Ephemeral Containers
核心原理:通过 Kubernetes API 向已运行的 Pod 动态注入一个新容器(不修改 Pod spec 的 containers 字段),新容器可以选择与目标容器共享 Process namespace 和 Network namespace,直接访问目标进程的网络栈、文件系统。
关键优势:
- 零重启:Pod 保持运行,不影响流量
- 零修改:不改 Deployment/StatefulSet,不触发滚动更新
- 工具自由:注入的镜像你说了算,busybox、nicolaka/netshoot、alpine 随便挑
- 安全可控:可通过 RBAC 限制谁能使用 kubectl debug
实操步骤
步骤 1:基础调试——注入 shell 到目标 Pod
# 向 Pod 注入一个临时调试容器,共享进程命名空间
kubectl debug -it pod/my-app-7b9f6d4c5-x2k8j \
--image=nicolaka/netshoot \
--target=my-app \
-- /bin/bash
参数说明:
- --image:调试容器使用的镜像,netshoot 内置 curl、tcpdump、dig、iftop、strace 等 40+ 网络/系统工具
- --target:指定要共享 PID namespace 的容器名,注入后可以 ps aux 看到目标容器的进程
进入后你可以直接:
# 查看目标容器进程
ps aux
# 抓包分析网络问题
tcpdump -i eth0 -nn port 8080 -c 100 -w /tmp/capture.pcap
# 检查 DNS 解析
dig kubernetes.default.svc.cluster.local
# curl 测试上游服务连通性
curl -v http://upstream-svc:8080/health
步骤 2:访问目标容器文件系统
由于共享了 PID namespace,目标容器的文件系统挂载在 /proc/1/root(假设目标主进程 PID 为 1):
# 查看目标容器的配置文件
cat /proc/1/root/etc/nginx/nginx.conf
# 检查目标容器的日志文件
tail -100 /proc/1/root/var/log/app/error.log
# 查看目标容器的环境变量
cat /proc/1/root/proc/1/environ | tr '\0' '\n'
步骤 3:使用 strace 追踪系统调用
当你需要分析目标进程为何 hang 住或行为异常时:
# 先找到目标进程 PID
ps aux | grep my-app
# strace 追踪(需要 SYS_PTRACE capability)
strace -p <PID> -f -e trace=network -o /tmp/trace.log
⚠️ 注意:strace 需要
SYS_PTRACEcapability。如果集群启用了 PSA (Pod Security Admission),需要在命名空间级别放行privileged或baselineprofile,或者使用如下方式显式声明:
kubectl debug -it pod/my-app-7b9f6d4c5-x2k8j \
--image=nicolaka/netshoot \
--target=my-app \
--profile=sysadmin \
-- /bin/bash
--profile=sysadmin 会为临时容器添加 SYS_PTRACE 等特权 capability。
步骤 4:节点级调试——当 Pod 无法调度时
如果问题出在节点层面(kubelet、容器运行时),可以直接调试节点:
# 创建一个以节点根文件系统为 host 的调试 Pod
kubectl debug node/worker-node-01 -it --image=ubuntu:22.04
# 进入后,节点文件系统挂载在 /host
chroot /host
# 查看 kubelet 日志
journalctl -u kubelet --since "10 minutes ago"
# 检查容器运行时状态
crictl ps -a | grep my-app
避坑指南
1. 临时容器不会自动清理
临时容器注入后会一直存在于 Pod spec 中(状态为 Terminated),直到 Pod 被删除。大量调试操作会让 Pod 的 ephemeralContainers 列表膨胀。
解决方案:建立团队规范,调试完成后及时记录并周期性重启长期运行的 Pod(配合 PDB 做滚动重启):
kubectl rollout restart deployment/my-app
2. 镜像拉取策略与私有仓库
临时容器继承 Pod 的 imagePullSecrets。如果调试镜像在私有仓库中,确保 Pod 所在 namespace 的 imagePullSecrets 包含对应凭据,或者使用公开镜像。
推荐做法:在集群内部署一个轻量 registry mirror,预缓存常用调试镜像:
# 提前在集群节点上拉取
crictl pull docker.io/nicolaka/netshoot:latest
3. RBAC 权限控制
生产环境必须限制谁能执行 kubectl debug。创建专用 ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-debugger
rules:
- apiGroups: [""]
resources: ["pods/ephemeralcontainers"]
verbs: ["patch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
绑定给 SRE 团队,普通开发者只给 get/list/log 权限,不给 debug 权限。
总结
| 场景 | 命令 | 适用情况 |
|---|---|---|
| 应用调试 | kubectl debug -it pod/xx --image=netshoot --target=app |
容器内无工具 |
| 网络抓包 | 进入后 tcpdump -i eth0 |
连接超时、DNS 异常 |
| 进程追踪 | strace -p <PID> (需 --profile=sysadmin) |
进程 hang/异常 |
| 节点排查 | kubectl debug node/xx --image=ubuntu |
kubelet/runtime 问题 |
Ephemeral Containers 是 Kubernetes 给 SRE 的一把瑞士军刀——不破坏现场、不中断服务、不需要提前埋点。建议将常用调试镜像和 RBAC 策略纳入集群标准化 baseline,让团队在下一次 P1 故障时能在 10 秒内进入调试状态,而不是花 10 分钟改 Deployment 重建 Pod。