饮墨

子安饮墨馀三斗,留与卿儿作赋来

Kubernetes Ephemeral Containers 实战:不重启 Pod 的生产调试术

痛点:生产容器里没有调试工具

你一定遇到过这种场景:Pod 出了问题,kubectl exec 进去后发现——容器用的是 distroless 镜像,连 sh 都没有,更别说 curltcpdumpstrace 了。

传统做法要么改 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 内置 curltcpdumpdigiftopstrace 等 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_PTRACE capability。如果集群启用了 PSA (Pod Security Admission),需要在命名空间级别放行 privilegedbaseline profile,或者使用如下方式显式声明:

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。

您还没有登录,请登录后发表评论。