生产环境排错利器:Kubernetes临时容器原理与实战

发布时间:2026/10/14 17:38:39
生产环境排错利器:Kubernetes临时容器原理与实战 生产环境里最让人紧张的不是告警本身而是“看得到异常却进不去现场”。前阵子一个后端服务CPU飙到99%日志里全是超时我第一反应是kubectl exec -it pod-xxx -- bash结果进去就傻眼了这个服务为了减少镜像体积用的是纯编译产物连ps、top、netstat都没有exec等于进了一个空壳什么都干不了。折腾半天最后只能紧急重启Pod故障倒是恢复了但根因始终没定位到。后来我用上了 Kubernetes 的临时容器Ephemeral Containers才彻底解决这个痛点。它相当于运维排错时的“瑞士军刀”不改变Pod的现有拓扑不需要重新调度直接往正在运行的Pod里塞进一个自带工具的调试容器让你在故障现场做进程分析、网络抓包、文件查看用完即丢。这篇就围绕临时容器的原理、实操、排错场景和注意事项把我自己踩过的坑和总结的经验完整写出来。1. 为什么说临时容器是排错的瑞士军刀1.1 传统排错方式的三个尴尬现状在没有临时容器之前想在运行中的Pod里做诊断基本只有三条路而每条路都有明显的坑。第一条路是依赖业务镜像自带调试工具。为了安全和体积现代的镜像越来越精简很多甚至直接用无需shell的镜像来部署容器里连/bin/sh都没有。就算有shell缺少像strace、tcpdump、dig、ss这类排错利器也很难做深度定位。你只能看日志但日志往往只告诉你现象不告诉你内核态发生了什么、连接断在哪里。第二条路是现做镜像、重新发布。改Dockerfile塞进一堆调试工具再走构建、推送、滚动更新流程。一个需要快速恢复的线上事故硬生生被拖成半个小时的交付过程。更要命的是滚动更新会改变Pod的IP和实例状态有些问题在实例被替换后可能就“消失”了你很难再抓到现场。第三条路是直接上节点用nsenter或者chroot进入容器对应的进程命名空间。这种方法权限要求高、操作繁琐而且现代运行时对容器进程的隔离方式各有差异一不小心就容易误操作到别的容器风险极大。1.2 临时容器到底解决了什么问题临时容器的设计初衷很朴素Pod还是那个Pod容器还是那些容器我只是往里面追加一个排查用的“外部工具进程”让它具备和Pod内其他容器一样的网络能力按需获取进程视图用完就走不污染业务容器。它在运行中的Pod上动态生效不会触发Pod重建不会影响现有容器的运行状态也不参与Service的负载均衡。对你来说它就是“现场勘查员”戴着的头灯——平时不占用空间需要的时候戴上看完摘掉。我建议这几类人重点了解它日常维护K8s集群的SRE和运维工程师负责微服务开发、需要经常排查接口异常和依赖问题的后端研发以及任何接触过无shell镜像、被“进不去容器”折磨过的同学。2. 临时容器的核心原理它到底是什么2.1 从Pod的组成模型说起Pod是Kubernetes中最小的调度和运行单元一个Pod里可以跑多个容器它们共享网络命名空间、IPC命名空间存储卷也可以共享。关键点在于Pod的spec.containers列表在Pod创建后基本是不可变的。这意味着你没办法用kubectl edit pod直接往Pod里新加一个常规容器常规容器的增删改只能通过重建Pod来完成。而生产环境的Pod往往由Deployment、StatefulSet等控制器管理你想“临时加一个容器”控制器会认为你篡改了期望状态直接帮你回滚或者重新调度。所以长期以来“往现有Pod里塞一个调试容器”这个诉求在K8s里一直得不到很好的满足。临时容器绕开了这个限制。它被放在一个新的字段spec.ephemeralContainers下这是一个独立的子资源。API Server授权更新这个子资源而不需要改动spec.containers主列表所以既能满足动态注入的需求又不会触发控制器的“期望状态修正”。2.2 与常规容器、Sidecar容器的本质区别很多初次接触的人会问这不就是Sidecar吗其实差异很大。Sidecar是常驻的、参与Pod生命周期管理的辅助容器它和业务容器一起启动、一起退出有探针、有资源配额。而临时容器更像“一次性的外部志愿者”不参与Pod的启动流程不参与探针和生命周期钩子也不能被反复重启。我把它们的关键差异整理成一张表方便对比对比维度常规容器Sidecar容器临时容器是否随Pod启动是是否按需动态注入是否触发Pod重建否本身就是Pod成员否否直接追加到运行中的Pod探针与生命周期钩子支持支持不支持重启策略支持支持不支持退出即终止是否参与Service后端是是否资源申请是否参与Pod调度是是不参与初始调度但可显式设限典型用途业务进程日志收集、流量代理等故障隔离、现场排错这里有个容易被忽略的点临时容器虽然被追加到Pod里但它不会被纳入Service的Endpoint也就是说即使你在临时容器里启动了一个监听端口的进程也不会有人把业务流量调度到它身上。它更适合做“观测者”而不是“替代者”。2.3 临时容器的命名空间共享规则理解临时容器能看什么、不能看什么关键看命名空间共享。首先网络命名空间一定是共享的。Kubernetes的Pod本身就让所有容器共享同一个网络命名空间临时容器作为Pod的一个追加项天然具有Pod IP、网卡、路由、DNS配置和已有的TCP连接表。所以你在临时容器里执行ss -tan看到的连接和业务容器看到的完全一致。这是排查网络问题最常用的能力。其次进程命名空间是否共享取决于Pod创建时是否设置了spec.shareProcessNamespace: true。如果设置了Pod内所有容器共享同一个PID命名空间临时容器里执行ps -ef可以直接看到业务进程如果没有设置每个容器默认有独立的PID命名空间临时容器只能看到自己的进程。最后存储卷是否可见取决于临时容器是否显式挂载了这些卷。注意它只能挂载Pod里已经定义过的Volume不能给运行中的Pod新增一个全新卷因为Volume是Pod级别属性同样受到不可变性约束。2.4 版本演进从特性门控到GA临时容器不是最近才出现的新功能。它在Kubernetes 1.16版本以alpha特性引入默认需要手动开启特性门控1.18版本开始kubectl内置了debug子命令不再需要自己拼JSON Patch1.23版本转为beta1.25版本正式GA主流集群已经可以直接使用。如果还在维护特别老的集群需要确认API Server是否支持EphemeralContainers特性否则任何临时容器的创建请求都会被拒绝。3. 实操用 kubectl debug 快速上手临时容器3.1 环境检查与命令入口实际操作时我建议先把kubectl升级到较新的版本确保kubectl debug子命令可用。然后在你的测试环境里跑一个不带调试工具的业务Pod例如直接跑一个纯脚本镜像再确认集群版本支持临时容器。判断方式也很简单直接看Pod的schema里有没有这个字段kubectl explain pod.spec.ephemeralContainers如果返回了字段说明说明你的kubectl和API Server都支持如果提示未知字段就要先处理版本兼容问题。3.2 最简单的方式往运行中的Pod注入一个调试容器假设我有一个Pod叫nginx-demo-7b7c5d8f9f-abcde运行正常但我想检查它的网络和文件系统。传统的kubectl exec只能进入业务容器本体如果业务镜像没有工具什么都做不了。现在直接用kubectl debugkubectl debug pod/nginx-demo-7b7c5d8f9f-abcde -it \ --imagenicolaka/netshoot \ -- /bin/bash这个命令会在指定Pod里创建一个名字带debug-前缀的临时容器拉取netshoot镜像并直接附着到临时容器的终端。netshoot镜像里预装了ss、tcpdump、dig、curl、nslookup、strace等一堆排错工具基本覆盖网络和系统诊断需求。创建完成后你会直接进入一个shell。这时可以执行ss -tan | head -20 curl -v http://service-name.namespace.svc.cluster.local nslookup db-service这些操作看到的网络视图和业务容器完全一致非常直观。需要注意一个细节kubectl debug默认不会挂载业务容器的卷也不会共享业务容器的进程命名空间。所以这个方案最适合快速验证“网络通不通”“端口通不通”“DNS解析正不正常”这类问题。3.3 想看业务进程先确认共享进程命名空间我踩过最大的坑就是兴冲冲地加了一个临时容器进去执行ps -ef结果只看到sleep和bash自己的进程还以为业务进程消失了。后来才明白这是Pod没有开启shareProcessNamespace导致的。判断Pod是否开启了共享进程命名空间一行命令就能看到kubectl get pod nginx-demo-7b7c5d8f9f-abcde -o yaml | grep -A2 shareProcessNamespace如果Pod创建时没有设置这个字段那临时容器默认看不到业务进程。这种情况下有两个选择。第一个选择是如果你能接受对Pod做一次滚动更新可以在Deployment的模板里加上spec: shareProcessNamespace: true然后重新滚动之后再创建临时容器就能看到所有进程了。但这种做法相当于发一次版本不太适合应急。第二个选择是使用--copy-to方案复制一个带共享进程命名空间的调试Pod这个我下面会讲。如果你处理的Pod本身已经开了shareProcessNamespace就可以使用--target参数明确指定想要观察的目标容器kubectl debug pod/nginx-demo-7b7c5d8f9f-abcde -it \ --imagenicolaka/netshoot \ --targetnginx \ -- /bin/bash进入后执行ps -ef | grep nginx ls -l /proc/1/cwd cat /proc/1/status | grep -E Threads|State这些信息对于判断进程状态、线程数量、当前工作目录非常有用。3.4 复制Pod排错更稳的 --copy-to 方案如果业务Pod是生产环境核心实例不想有任何染指或者Pod本身没有开启共享进程命名空间你又必须看进程又或者业务Pod正在CrashLoopBackOff根本没有稳定的运行时给你注入临时容器这时候我更推荐用--copy-to方案。它的原理是复制一个和原Pod配置几乎一致的新Pod名字由你指定然后在这个副本上做各种调试。副本不会接收线上流量不影响现网实例你可以大胆操作。kubectl debug pod/nginx-demo-7b7c5d8f9f-abcde \ --copy-tonginx-demo-debug \ --imagenicolaka/netshoot \ --containernginx \ -- /bin/bash这里--container指的是原Pod中要替换成调试镜像的容器名。创建完成后你需要单独进入这个副本Podkubectl exec -it nginx-demo-debug -c nginx -- /bin/bash副本Pod保留了原Pod的挂载卷、环境变量、网络策略等配置所以很多问题都能在副本里还原出来。而且你可以在副本里直接改文件、调配置、看进程完了把它删掉即可不会对生产环境产生任何痕迹。3.5 节点级排错语法一样权限不同除了针对Podkubectl debug还支持对节点进行调试。它会创建一个使用宿主命名空间的容器相当于让你在节点视角下执行命令kubectl debug node/worker-node-1 -it \ --imagenicolaka/netshoot \ -- /bin/bash这个场景适合排查kubelet状态、CNI插件问题、节点文件系统异常等。需要注意这个命令的权限远高于Pod级调试在RBAC上必须单独限制不建议开放给普通研发同学。4. 排错实战典型故障场景拆解4.1 场景一distroless 镜像完全无法 exec某天一个基于distroless镜像的服务突然出现大量502日志显示连接被重置。我想进容器看TCP连接状态结果常规exec直接报错OCI runtime exec failed: exec failed: unable to start container process: exec: sh: executable file not found in $PATH: unknown这几乎是无解的情况除非重建镜像。临时容器直接解决了这个死局kubectl debug pod/distroless-app-xxx -it \ --imagenicolaka/netshoot \ -- /bin/bash进入临时容器后我用ss -tan看到了大量TIME_WAIT和SYN_SENT的连接再用cat /proc/net/tcp进一步确认对端端口快速定位到是下游数据库连接池被占满导致连接被拒。整个过程没有重启Pod业务没有发生中断。4.2 场景二CrashLoopBackOff 反复重启有一种更棘手的情况业务容器启动后立刻崩溃进入CrashLoopBackOff每次存活时间只有几秒。这个时候你很难在崩溃间隙exec进去因为容器状态已经是Terminated或Waitingexec根本没有执行环境。面对这种情况我的做法是直接复制一个副本Pod进行调试kubectl debug pod/crash-app-xxx \ --copy-tocrash-app-debug \ --imagedebian:latest \ --containerapp \ -- /bin/sh由于副本Pod里的业务容器被替换成了debian镜像启动后不会立刻崩溃我可以在副本里检查原启动脚本、环境变量、挂载的配置甚至手动执行原容器的启动命令观察报错输出。这种“复制故障现场但不让故障继续发生”的思路比盯着原Pod的日志快得多。4.3 场景三网络丢包和DNS解析异常微服务架构里最常见的是Pod之间互相访问超时。业务容器是精简镜像没有dig和curl只能靠猜。临时容器里我一般先做这几步# 检查本机监听的端口 ss -lntp # 检查到目标服务的TCP连接 curl -v http://notification-service.default.svc:8080/health # 检查DNS解析结果 dig short notification-service.default.svc.cluster.local曾经遇到过一个问题服务间调用偶发超时常规排查无果。通过临时容器执行curl -v后发现请求在TLS握手阶段就卡住进一步用tcpdump抓包发现对端主动发RST最终定位到是Service的endpoint包含了已经被驱逐但未清理的Pod IP。这个结论在普通日志里根本看不到。4.4 场景四查看挂载卷里的现场数据临时容器也支持挂载Pod已经定义的Volume这对排查写入持久卷的数据非常有帮助。kubectl debug默认不提供直接挂载卷的参数所以我通常使用API方式创建临时容器。先生成一个调试容器的JSON例如{ spec: { ephemeralContainers: [ { name: debug, image: nicolaka/netshoot, stdin: true, tty: true, command: [/bin/bash], volumeMounts: [ { name: data-volume, mountPath: /data } ] } ] } }然后把它提交到Pod的ephemeralcontainers子资源kubectl replace --raw /api/v1/namespaces/default/pods/stateful-app-0/ephemeralcontainers \ -f debug-container.json创建完成后用kubectl exec进入这个临时容器kubectl exec -it stateful-app-0 -c debug -- /bin/bash ls -lah /data这种方式适合StatefulSet场景尤其当业务容器无法读某些文件但需要确认磁盘内容和权限归属时。挂载的卷必须是Pod已经定义过的不能额外新增一个“只给临时容器用”的新卷这一点在设计时就要注意。5. 常见问题与排查技巧实录5.1 临时容器创建成功但进入后命令立刻退出临时容器的生命周期规则和普通容器不一样当主命令执行完毕后容器直接进入Terminated状态没有重启机制。比如执行kubectl debug pod/xxx -it --imagebusybox -- ls /tmpls执行完临时容器立刻退出你想再在里面补一条命令都做不到。遇到需要持续操作的场景我习惯把命令替换成一个常驻进程比如kubectl debug pod/xxx -it \ --imagebusybox \ -- /bin/sh -c sleep 3600之后再通过kubectl exec -it xxx -c debug-xxx-xxxx -- /bin/sh进入。这样临时容器保持存活你随时可以进去执行多条命令用完让它自己退出即可。5.2 临时容器看不到业务进程这个问题在前面已经反复强调原因就是Pod没有设置shareProcessNamespace: true。临时容器“共享网络”是默认的但“共享进程”不是。如果确认Pod没有开启又不想重新滚动最快的解决方案是使用--copy-to创建一个调试副本并在副本上通过kubectl debug或直接调整yaml开启共享进程命名空间。如果你只是临时想看一眼/proc/pid/下的信息还可以考虑用节点级调试容器配合nsenter不过操作复杂度明显上升不推荐作为常规手段。5.3 Pod状态里出现Terminated的临时容器以为故障了临时容器退出后它在Pod status里会留下一个Terminated状态。很多同事第一次看到会误以为业务容器挂了实际业务容器和Pod的运行状态完全不受影响。查看容器状态时把containers和ephemeralContainers分开看就不会误会了kubectl get pod nginx-demo-xxx -o jsonpath{.status.containerStatuses[*].name}: {.status.containerStatuses[*].state} kubectl get pod nginx-demo-xxx -o jsonpath{.status.ephemeralContainerStatuses[*].name}: {.status.ephemeralContainerStatuses[*].state}5.4 镜像拉取失败导致临时容器一直Pending临时容器镜像拉取失败状态会卡在Waiting日志里看到ImagePullBackOff。原因可能是镜像名写错、私有仓库认证失败或者是集群有镜像白名单策略。调试镜像建议直接使用常用公共镜像比如busybox、alpine、nicolaka/netshoot。如果是私有镜像提前把imagePullSecrets配好否则临时容器只能持续等待。5.5 没有权限创建临时容器创建临时容器需要的权限不是普通的pods/exec或pods/get而是pods/ephemeralcontainers这个子资源的update权限。如果RBAC配置没有覆盖会看到类似下面这个报错Error from server (Forbidden): pods xxx is forbidden: cannot update pod ephemeralcontainers给排障人员分配权限时最小化原则是只允许在特定命名空间中操作apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: app-team name: pod-debugger rules: - apiGroups: [] resources: [pods] verbs: [get, list] - apiGroups: [] resources: [pods/ephemeralcontainers] verbs: [create, update]5.6 临时容器不能删除这是一个容易踩坑的设计点临时容器一旦提交给Kubelet创建就无法被单独删除。即使你去修改Pod的ephemeralContainers字段想把它移除API Server允许修改但Kubelet可能直接忽略这种删除请求。所以不要尝试“用完删掉”临时容器正确做法是让它自然退出。如果临时容器实在还占着一个名字需要清理的话只能等Pod整体删除后一起消失。6. 临时容器的安全边界与生产实践6.1 权限必须收敛审计必须开启临时容器本质上相当于在运行中的Pod里执行任意命令这一点比kubectl exec的权限影响更大。如果运维团队接管了一个集群又不小心把pods/ephemeralcontainers的写权限分配给了普通用户等于让他们能对任意Pod注入恶意调试工具读取环境变量、挂载卷内容甚至进程内存。因此建议严格控制创建临时容器的角色并开启Kubernetes审计日志定期检查是否有异常的ephemeralcontainers更新记录。生产环境我通常只用临时容器做“只读诊断”需要修改配置或文件的话优先通过副本Pod操作避免在线实例被误改。6.2 临时容器的资源限制容易被忽略临时容器在创建时不设置resources字段的话相当于以无限制状态运行在节点上。虽然它没有参与Pod的初始调度但如果里面跑了一个tcpdump或者长时间占满CPU的命令照样可能影响同节点其他Pod。使用API方式创建临时容器时建议显式加上resources限制。例如resources: { limits: { cpu: 500m, memory: 256Mi }, requests: { cpu: 100m, memory: 64Mi } }这样临时容器就像一把“带了保险销的瑞士军刀”既能干活又不会伤到周边环境。6.3 我的生产使用画像是这样的经过半年多的实践我已经形成了一套比较稳定的临时容器使用习惯轻量网络排查直接kubectl debug -it --imagenicolaka/netshoot需要看进程状态时先确认或创建开启shareProcessNamespace的实例遇到CrashLoopBackOff或不能碰的生产实例一律走--copy-to副本涉及挂载卷和精细化资源控制时再走API方式创建临时容器。还有一个提升效率的小技巧在调试之前先把以下三个信息准备好——业务容器的真正名称、Pod是否开了共享进程命名空间、目标网络服务域名。这三个信息决定了你会采用哪种调试路径也决定了你进去之后第一剑从哪落。临时容器不是万能的比如它没办法在容器崩溃的瞬间捕捉内核状态也不能替代metrics监控。但它确实把“进不去”这个老问题打开了一个口子。现在遇到精简镜像的服务我心里踏实很多因为我知道手里有一把能随时掏出来的“瑞士军刀”。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询