为什么 Pod 创建要先启动 pause 容器?详解 CNI 网络配置链路
2026/9/9 23:08:40 网站建设 项目流程

在排查一个“Pod创建成功但网络不通”的问题时,我无意间翻到 kubelet 的日志,发现它的执行顺序非常有意思:先创建了 pause 容器,紧接着才去调用 CNI 插件配置网络。当时我就在想,为什么是这个顺序?pause 容器到底在里面扮演什么角色?在云原生环境里摸爬滚打久了,你会发现这个“先 pause、后 CNI”的顺序不仅是流程设计上的巧合,而是整个 Kubernetes 网络模型的地基。这篇文章就围绕这一点,把 Pod 创建过程中的网络链路彻底拆开讲透,包括 pause 容器的作用、CNI 的执行机制、常见坑和避坑经验,希望对你排查问题或者准备面试有帮助。

1. 为什么非得先有 pause,才能谈网络

1.1 pause 容器的真正使命不是“占位”

很多人第一次接触 pause 容器,都会觉得它是个“多余的占位符”——毕竟kubectl get pods里看不到它,docker ps里却总能看到一个pausesandbox字样的容器在运行。实际上,这个容器的存在,决定了整个 Pod 的网络命名空间能否成立。

先讲一个底层事实:在 Linux 中,网络命名空间(network namespace)是隔离网络栈的单位,每个命名空间拥有自己独立的网卡、路由表、iptables 规则等。一个 Pod 里的所有容器,之所以能用localhost互相访问,就是因为它们共享同一个网络命名空间。那么这个“共享的命名空间”是哪来的?不是凭空出现的,而是通过 pause 容器创建的。

pause 容器是 Pod 里最先启动的容器,它会在启动时创建一个全新的网络命名空间,并一直保持运行。其他业务容器启动时,通过Join方式加入到这个已经存在的命名空间里。换句话说,pause 就是那个“先占了坑、把地基打好”的角色,业务容器都是后来的住客,住进这个已经布置好的“房间”。

那为什么选 pause 而不让第一个业务容器来承担这个职责?原因很现实:业务容器会退出、会重启,如果网络命名空间随着业务容器的生死而销毁和重建,那整个 Pod 的网络身份就全乱套了。pause 容器的作用就是保证这个网络命名空间在 Pod 的整个生命周期内稳定存在,哪怕业务容器崩溃了、重启了,网络栈也纹丝不动。这也是为什么 pause 镜像极小、几乎不占用资源,它只需要活着,不需要干别的。

1.2 kubelet 创建 Pod 的固定动作:先 sandbox,后容器

在 Kubernetes 的视角里,Pod 不是一个“大容器”,而是一组共享资源的容器的集合。为了管理这些共享资源,kubelet 先把 Pod 抽象成一个“沙箱”,也就是 sandbox。这个沙箱的概念,在 CRI(Container Runtime Interface)里被定义成了RunPodSandbox,而沙箱对应的具体实现,就是创建并启动 pause 容器。

这个顺序在 kubelet 的代码里是被严格保证的:每次 Pod 同步(syncPod)时,kubelet 会先调 CRI 的RunPodSandbox创建沙箱,然后才开始拉取业务镜像、启动业务容器。这就意味着,在业务容器还没影的时候,pause 容器已经站在那儿了,它的网络命名空间已经就绪,设备文件、网络配置都等着 CNI 来填充。

这里有一个很关键的细节:RunPodSandbox返回成功后,kubelet 会拿到这个沙箱的网络命名空间路径(比如/var/run/netns/cni-xxxx),然后拿着这个路径去调用 CNI 插件。换句话说,pause 容器创建成功,网络配置的真正操作对象才存在。没有前面的沙箱,CNI 插件连“网卡插到哪”都不知道。

所以,整个链路是这样闭环的:kubelet 决定创建 Pod → 调用 CRI 创建 pause 容器(沙箱)→ 沙箱启动后暴露网络命名空间 → kubelet 调 CNI 插件为这个命名空间配置网络 → 配置完成,pause 容器拥有 Pod IP → 后续业务容器启动,直接加入这个现成的网络环境。这套序列不是哪个团队拍脑袋定的,而是从设计层面保证了“网络先于业务”这个硬性要求。

2. CNI 到底怎么和 pause 容器合作

2.1 从 kubelet 到 CNI:一次标准 Add 调用

CNI(Container Network Interface)本身不是 Kubernetes 的组件,它是一套规范,定义“如何把容器接入网络”。Kubernetes 通过 kubelet 内置的cni网络插件,在沙箱创建完成后,按照 CNI 规范去执行网络配置。

整个调用链可以简化为:

  1. kubelet 拿到 pause 容器的 ID 和网络命名空间路径。
  2. kubelet 根据 Pod 的注解和配置,生成 CNI 的ADD命令参数,包括容器 ID、网络命名空间路径、网络配置等。
  3. 按照 CNI 规范的顺序,先执行loopback插件,再执行主网络插件(如bridgeptpcalicoflannelcilium等)。
  4. 主网络插件在 pause 容器的网络命名空间里创建虚拟网卡、分配 IP、设置路由,并把结果返回给 kubelet。
  5. kubelet 收到插件返回的 IP 地址等信息,把它更新到 Pod 状态里,Pod 就此获得网络身份。

注意,这里的“容器 ID”是 pause 容器的 ID,不是业务容器的 ID。CNI 插件操作的是沙箱的网络命名空间,给这个命名空间挂网卡、给这个命名空间配 IP,而业务容器因为共享同一个命名空间,所以天然就继承了这个网络。

我在实际调试时,经常会用crictl inspect查看 pause 容器的状态,确认网络是否已经就绪。有一个非常直观的观察点:pause 容器的状态里会出现ipmacAddressinterface这些字段,一旦这些字段有值,说明 CNI 配置完成,Pod 的网络已经通了。如果在业务容器还没启动时,pause 容器已经有 IP,说明网络阶段是成功的——排查问题的时候,这个先后顺序能帮你快速定位是“网络配置没执行”还是“业务容器自身的问题”。

2.2 bridge、ptp、overlay:插件不同,动作不同

CNI 规范只是定了流程,具体怎么把容器接进网络,取决于你用的是哪个插件。这里我对比一下常用的三种模式,能帮你理解“同样的调用,不同的结果”。

插件类型代表实现网络动作Pod 之间通信方式典型误用场景
bridge官方 bridge 插件、flannel 的 host-gw创建 veth pair,一端接 Pod netns,一端接宿主机网桥通过宿主机网桥二层转发,或配合 host-gw 路由节点数多时路由表膨胀,排查路由丢包
ptp官方 ptp 插件创建 veth pair,一端在 Pod netns,另一端在宿主机,靠点对点路由逐跳三层路由,适合不需要网桥的场景和 Service 网段规划冲突时产生路由黑洞
overlaycalico VXLAN、flannel VXLAN、cilium在宿主机网络上封装一层隧道,Pod 流量通过 VTEP 设备转发跨节点走 VXLAN 隧道,解耦底层网络性能敏感场景下未开直路由,吞吐受损

选哪种插件,对“pause 容器先创建”这个顺序没有任何影响。但了解插件类型,对排障有巨大帮助。比如你用 bridge 插件,Pod 通了但跨节点不通,大概率是宿主机网桥的路由或 ARP 表出了问题;用 VXLAN,跨节点不通,可能要查 VTEP 的 MAC 地址学习是否正常。这些排查的起点,都在 pause 容器对应的那个网络命名空间里。

2.3 pause 容器网络就绪后,业务容器如何“加入”

CNI 配置完成后,pause 容器的网络环境是一套已经完整可用的栈:有网卡、有 IP、有路由、有 DNS 配置(如果配了)。业务容器启动时,CRI 会通过JoinNetworkNamespace方式,把业务容器放进 pause 容器所在的网络命名空间。用 Docker 的底层机制来说,就是--network container:xxx的那种模式。

这里有个容易被忽视的点:业务容器本身是不需要、也不会单独执行 CNI 的。你在 node 上手动执行crictl run起一个容器,它不会自动分配 Pod IP,原因就是没有经过 kubelet 的这整套逻辑。真正决定容器网络身份的,是它是否加入了某个 pause 容器创建的网络命名空间。

我在排查“容器内网络不通”问题时,第一件事就是在 node 上找到该 Pod 对应的 pause 容器,然后crictl exec进入这个 pause 容器的网络命名空间里ping一下网关和同节点 Pod IP。如果 pause 里能通,而业务容器里不通,那问题几乎都在业务容器自身的配置或镜像环境上,和 CNI 无关。如果 pause 里都不通,那才需要往 CNI 的插件执行过程去查。

3. 网络配置完整闭环:从 ADD 到 GC

3.1 ADD、CHECK、GC:每个阶段都不可跳过

提到 CNI,很多人的印象还停留在“创建时配网”这一步。其实一个完整的 CNI 生命周期,是被规范严格定义过的:ADD 负责添加网络接口,DEL 负责删除,CHECK 负责检查网络是否仍然正常,GC(Garbage Collection)负责回收异常残留的资源。

在 Pod 启动流程里,ADD 发生在 pause 容器创建后,这一步执行完,Pod 拿到 IP。CHECK 是 kubelet 在 Pod 同步时定期执行的,它会向 CNI 插件发起检查请求,看这个网络配置是否还有效。如果 CHECK 失败,kubelet 会考虑重启沙箱,也就是把 pause 容器销毁重建,重新走一遍 ADD。这个机制保证了网络异常时,Pod 能尽快被修复。

DEL 和 GC 则在 Pod 删除时执行。这里有一个细节需要注意:kubelet 在收到 Pod 删除请求后,会先停止业务容器,然后删除沙箱。沙箱删除之前,会调用 CNI 的 DEL 命令,把网络资源(IP、虚拟网卡、路由等)释放掉,最后才销毁 pause 容器的网络命名空间。顺序反过来会出大事:如果先销毁命名空间,再调 CNI 插件,插件会发现自己操作的对象已经不存在了,资源泄漏在所难免。

GC 则是回收那些异常残留的 IP 或网卡。比如节点宕机后,etcd 里的 Pod 还在,但节点上的 pause 容器和网络资源已经重建了,这时候旧资源需要被回收。CNI 规范在 1.1 版本里把 GC 机制明确化,主流插件也都实现了对应的 GC 逻辑。

3.2 为什么有的环境里 network 会“重复配置”

在实际运维中,我遇到过一种奇怪的现象:Pod 创建成功了,但容器里有两个网卡,一个 IP 是最初 CNI 分配的,另一个 IP 是后来冒出来的。排查到最后发现,根因是 kubelet 在 ADD 之后,没有收到预期的返回结果,于是重试了一次 ADD,而 CNI 插件没有做幂等处理,导致第二次 ADD 又新建了一对 veth 和新的 IP。

这种问题的根源在于“先 pause、后 CNI”这个机制中,如果 ADD 超时或返回异常,kubelet 会重试。正常的 CNI 插件应该能识别到同一容器 ID 的 ADD 请求,已经存在就返回现有配置,而不是重复创建。选型时,尽量选择成熟、维护活跃的 CNI 实现,不要自己造轮子,就是出于这类细节的考量。

另外,建议你在排障时也关注 CNI 插件的日志。用 Calico 的话,看 calico-node 的日志;用 Flannel 的话,看 flanneld 的日志;这些日志里通常会记录 ADD 的调用参数和返回结果。配合 kubelet 的日志,基本能拼出整个 Pod 网络配置的生命周期。

3.3 实际操作:手动复现 CNI 调用过程(核心调试方法)

如果想真正弄懂“pause 容器创建后,CNI 如何执行”,最好的方式就是在测试节点上手动跑一遍 CNI 命令。我自己的调试路径是这样的:

  1. 找一个正在运行的 Pod,拿到它的 pause 容器 ID。
  2. 通过crictl inspect <sandbox-id>拿到网络命名空间路径和容器 ID。
  3. 找到 CNI 插件的二进制目录和网络配置目录,通常在/opt/cni/bin/etc/cni/net.d
  4. 按 CNI 规范,手动设置CNI_COMMAND=ADDCNI_CONTAINERIDCNI_NETNSCNI_IFNAME等环境变量,直接执行主插件二进制。

举个实际的例子,用 bridge 插件来模拟:

export CNI_COMMAND=ADD export CNI_CONTAINERID=debug-pod-001 export CNI_NETNS=/var/run/netns/cni-debug-123 export CNI_IFNAME=eth0 export CNI_PATH=/opt/cni/bin # 需要先建一个 netns,模拟 pause 容器的网络命名空间 ip netns add cni-debug-123 # 执行 bridge 插件 cnio-bridge < /etc/cni/net.d/10-bridge.conflist

执行成功后,插件会返回一段 JSON,里面有ipmac等字段。这整个过程,用你系统里的 CNI 插件也可以完成。手动跑过一遍之后,你会对“CNI 到底做了什么”有一个非常具体的感知:它就是在网络命名空间里创建 veth、分配 IP、写路由的过程。

注意:在测试节点上手动创建 netns、执行 CNI 后,记得用ip netns del清理,不然残留的 netns 会影响后续排查。

4. 常见故障与排障技巧实录

4.1 故障速查表:遇到这些现象,先查哪里

现象可能原因排查起点
Pod 一直 ContainerCreating,卡在 sandbox 创建pause 镜像无法拉取、CRI 执行沙箱失败检查镜像仓库连通性,crictl images是否已有 pause 镜像
Pod 创建成功但无 IPCNI ADD 执行失败、插件二进制缺失、网络配置目录为空查看 kubelet 日志里的 CNI 错误信息
Pod 有 IP 但跨节点不通插件类型与路由策略不匹配、底层网络限制对比同一个 Pod 内的路由表,在 pause 容器 netns 里 tracepath 网关
删除 Pod 后 IP 长时间不释放DEL 未执行、GC 未触发查看 CNI 插件日志,确认 DEL 命令是否被调用
节点重启后 Pod 网络恢复慢CNI 配置与底层网络依赖存在顺序问题检查 kubelet 和 CNI 插件的启动依赖,是否需要等待底层网络就绪

4.2 排障实例:从 kubelet 日志定位 CNI 挂点

一次客户现场反馈:新加的节点上,所有 Pod 都创建不成功,报错信息是network plugin is not ready: cni config uninitialized。看到这行日志,第一反应就是/etc/cni/net.d下没有配置文件,或者 kubelet 启动时读不到有效配置。

登上去一看,目录里确实是空的。再查安装记录,发现节点的 kubelet 启动比 CNI 配置下发早了半分钟,kubelet 启动时认为 CNI 未就绪,就一直挂在那里。这种情况下,不是 CNI 坏了,而是启动顺序问题。重启 kubelet 服务即可恢复,但更优雅的做法是让 CNI 配置先就绪,再启动 kubelet,或者在 kubelet 配置中延长 CNI 就绪等待时间。

另一个案例:Pod 一直处于ContainerCreatingcrictl ps -a里能看到 pause 容器已经创建,但kubectl describe pod里写着Failed to create pod sandbox: rpc error: ... network plugin could not be found。这种情况是 CNI 二进制文件的路径和 kubelet 配置里的cni-bin-dir不一致。很多发行版会把 CNI 插件装在/usr/lib/cni,而 kubelet 默认找/opt/cni/bin,不匹配就会挂。处理方式也很简单,要么在 kubelet 启动参数里显式指定--cni-bin-dir,要么建一个软链指向实际目录。

4.3 三个容易“想当然”的盲区

第一,不要以为 Pod 有 IP 就代表网络配置全部成功。IP 只是 ADD 的结果之一,route、iptables、ARP、DNS 这些配置,任何一个失败都会让 Pod “有 IP 但不可用”。我建议在验证 CNI 时,除了看 IP,还要看路由表和/etc/resolv.conf是否正确挂载。

第二,不要盲目相信docker inspect的结论。现在很多环境是 containerd 接管了运行时,docker ps看到的网络信息不一定是 kubelet 最终使用的网络命名空间。用crictl工具来查,才是和 CRI 一致的视角。

第三,不要忽略了 pause 容器本身的资源占用上限。pause 容器虽然极小,但它作为 Pod 生命周期最长的容器,如果被设置了过小的内存限额,在极端情况下也会影响网络命名空间的稳定性。我在生产环境里习惯把 pause 容器的资源都设为 0(不设 limit),避免这类无谓的干扰。

4.4 面试向:怎么把“先 pause、后 CNI”讲成亮点

这个问题在 Kubernetes 面试里其实是个非常经典的知识点。如果面试官问你“Pod 的网络是怎么建立起来的”,不要只答“CNI 配置的”,而是可以按这个逻辑展开:

  • Pod 不是容器,是一组共享资源的容器的集合,这个共享的“资源池”就是一个网络命名空间。
  • 网络命名空间由 pause 容器(沙箱)创建,它是 Pod 生命周期里第一个被启动的容器。
  • kubelet 在同步 Pod 时,先调用 CRI 的RunPodSandbox创建 pause 容器,然后由 kubelet 发起 CNI ADD 调用,为这个命名空间配置网络接口和 IP。
  • 业务容器启动时,通过加入 pause 容器的命名空间,直接继承网络配置。
  • 如果网络配置失败,kubelet 不会启动业务容器,Pod 卡在 ContainerCreating,等待修复后重试。

这样回答,把机制讲清楚了,也展示了你对底层链路有完整的认知。面试官如果再接着问“如果某个网络插件坏了怎么办”,你可以顺势讲 DEL、GC 和 kubelet 的重试机制,这就属于加分项了。

5. 写在最后:一个运维老手的体会

在刚开始接触 Kubernetes 的时候,我也觉得 pause 容器是个“玄学”,不知道为什么非得有个看不见的东西占着位置。后来真正开始做网络排障,才明白这个设计的精妙之处:它把“网络生命周期”和“业务容器生命周期”彻底解耦了。业务容器的生死,不影响网络身份;网络配置变更,也只需要操作一次沙箱,而不是反复对每个容器操作。这种解耦思想,贯穿了整个 Kubernetes 网络模型的设计。

如果你正在学习或者排查 Pod 网络问题,我的建议是:多花一点时间在 pause 容器和 CNI 的调用细节上,不要一上来就盯着业务容器的网络配置看。crictlip netnsethtooltcpdump,这些工具用好了,很多疑难网络问题都能迎刃而解。下次再遇到 Pod 网络不通,先从那个“不起眼”的 pause 容器查起吧。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询