3. Pod网络命名空间与veth pair
在 Kubernetes 中,Pod 是最小的调度单元,但它并不是“一个容器”的同义词。一个 Pod 可以包含多个容器,而这些容器之所以能像运行在同一台主机上一样通过 localhost 互相访问,根本原因在于它们共享同一个网络命名空间(network namespace)。理解这一点,是理解整个 Pod 网络模型、CNI 插件行为以及后续 Service、NetworkPolicy 实现的基础。
一、Pod 的网络命名空间从何而来
Linux 的网络命名空间是一套独立的网络协议栈,包含独立的网卡、路由表、ARP 表、iptables 规则、端口空间等。默认情况下,每个进程继承宿主机的网络命名空间;而容器运行时(如 containerd、CRI-O)在创建容器时,会为容器新建一个独立的网络命名空间,使其拥有隔离的网络视图。
Pod 的特殊之处在于:Pod 内所有容器共享同一个网络命名空间。实现方式是引入一个“基础设施容器”(infra container,也叫 pause 容器)。kubelet 在启动 Pod 时,先创建 pause 容器,由它持有该 Pod 的网络命名空间;随后启动的业务容器通过 --net=container:<pause容器ID> 的方式加入这个已存在的命名空间,而不是各自新建。因此,Pod 内所有容器看到的是同一块网卡、同一张路由表、同一个端口空间——两个容器不能同时监听 80 端口,但可以通过 localhost:80 直接通信。
二、veth pair:连接命名空间与宿主机的“虚拟网线”
网络命名空间是隔离的,但隔离的命名空间必须与外界通信。Linux 提供的 veth pair(虚拟以太网设备对) 就是解决这个问题的核心机制。veth pair 总是成对出现,可以把它想象成一根两端都是网卡的虚拟网线:从一端发出的数据帧,会直接从另一端出来。
在 Pod 网络中,这对设备的典型部署方式是:
- 一端(例如
eth0)放入 Pod 的网络命名空间,作为 Pod 的默认网卡,并分配 Pod IP; - 另一端(例如
vethXXXX)留在宿主机的根命名空间中,连接到网桥(如cni0或docker0)。
这样,Pod 发出的数据包从 eth0 进入 veth pair,从宿主机侧的 vethXXXX 出来,再交给网桥转发,从而进入宿主机网络或跨节点网络。
三、网桥的协作方式
网桥(bridge)是 Linux 内核实现的二层交换机。宿主机上的 cni0 网桥会把所有 Pod 的宿主机侧 veth 接口作为端口“插”上去。当网桥收到来自某个 veth 的数据帧时,会根据目标 MAC 地址决定从哪个端口转发出去:
- 如果目标 Pod 在同一宿主机上,网桥直接根据 MAC 地址表转发到对应的 veth 端口,数据帧进入目标 Pod 的命名空间;
- 如果目标不在本宿主机,网桥把帧交给宿主机的路由/转发逻辑,由节点网络(如 VXLAN、BGP、路由)继续转发。
网桥本身通常会被分配一个网关 IP(例如 10.244.0.1/24),Pod 的默认路由指向这个网关。因此 Pod 访问外部网络时,第一步就是把包发给网桥网关,再由宿主机做 SNAT 或路由。
四、一次 Pod 内到 Pod 外的数据包旅程
假设 Pod A(10.244.1.5)要访问集群外的 8.8.8.8,数据包的路径如下:
- 应用在容器 A 中发出数据包,目标 IP 为 8.8.8.8。由于不在本地子网,查路由表命中默认路由,下一跳为 10.244.1.1(网桥网关)。
- 数据包从
eth0发出,进入 veth pair,从宿主机侧vethXXXX出来。 - 网桥
cni0收到帧,发现目标 MAC 是网关自身,于是交给宿主机协议栈处理。 - 宿主机开启
ip_forward,根据自身路由表将包转发到物理网卡,并通常执行 SNAT(源地址伪装),把源 IP 改为节点 IP。 - 回包按相反路径返回,经网桥和 veth pair 回到 Pod A 的
eth0。
如果目标 Pod B 在同一宿主机上(例如 10.244.1.6),则第 3 步中网桥会直接查 MAC 地址表,把帧从 Pod B 对应的 veth 端口转发出去,不经过宿主机三层路由,效率更高。
五、关键参数与观察方法
理解机制之后,实际排查时可以通过以下方式观察这些对象。以下命令在节点上执行,属于教学示例,具体接口名以实际环境为准:
# 查看 Pod 的网络命名空间(在节点上)
crictl pods
crictl inspectp <pod-id> | grep -i namespace
# 进入 Pod 网络命名空间查看网卡
nsenter -t <pause-pid> -n ip addr show
nsenter -t <pause-pid> -n ip route show
# 查看宿主机侧 veth 与网桥
ip link show type veth
bridge link show
ip -d link show cni0
典型输出中,Pod 内 eth0 的 @if 编号会与宿主机侧某个 vethXXXX 的 ifindex 对应,这正是 veth pair 两端关系的直接证据。网桥的 bridge link show 会列出所有挂载的 veth 端口及其状态。
address already in use。这不是 Kubernetes 的 bug,而是共享网络栈的必然结果。六、小结与工程意义
Pod 网络命名空间 + pause 容器 + veth pair + 网桥,构成了 Kubernetes 单节点 Pod 网络的基础四件套。CNI 插件(Calico、Flannel、Cilium 等)本质上就是在完成“创建 veth pair、分配 IP、配置路由、接入网桥或 eBPF 程序”这一套动作,只是不同插件在跨节点转发、策略执行、数据面加速上采用了不同实现。掌握本节内容后,再去看 CNI 配置文件和插件行为,就能清楚每一步在操作哪个对象、影响哪条路径。