2. Pod网络基础与CNI模型

在Kubernetes集群里,Pod是最小的调度单元,但它并不是一个简单的进程容器。每个Pod都拥有独立的网络命名空间,这意味着Pod内部的容器共享同一个IP地址、同一张路由表和同一套端口空间。理解这一点,是理解Kubernetes网络模型的第一块基石。面试中经常被问到:“为什么同一个Pod里的两个容器可以用localhost互相访问?”答案就在于它们共享网络命名空间,而不是各自拥有独立的网络栈。

Pod网络命名空间与veth pair

Linux网络命名空间(network namespace)是对网络协议栈的隔离,包括网卡、路由表、iptables规则、socket等。创建Pod时,容器运行时(如containerd)会为Pod创建一个网络命名空间,通常挂载在/var/run/netns/下,或者通过/proc/<pid>/ns/net引用。这个命名空间里最初只有一个回环接口lo,没有任何外部连通能力。

要让这个命名空间与宿主机通信,最常用的手段是veth pair。veth pair是一对虚拟以太网接口,像一根虚拟网线:一端放在Pod的网络命名空间里(通常命名为eth0),另一端留在宿主机的根命名空间里(通常命名为vethXXXX)。从Pod发出的数据包从eth0进入,从宿主机的vethXXXX出来,反之亦然。这样,Pod就拥有了一个可以对外通信的接口。

但仅有veth pair还不够。宿主机上通常运行着多个Pod,每个Pod都有自己的veth接口。如果让这些veth接口直接暴露在宿主机网络栈中,会带来路由复杂性和安全隔离问题。因此,Kubernetes网络方案普遍采用网桥(bridge)来汇聚这些veth接口。最常见的做法是创建一个名为cni0或docker0的Linux网桥,把每个Pod的宿主机侧veth接口都挂到这座桥上。网桥工作在二层,负责在同一节点内的Pod之间转发数据帧。这样,同一节点上的Pod可以通过网桥直接通信,不需要经过宿主机的外部网卡。

下面是一个典型的节点内Pod网络结构示意(以cni0网桥为例):

Pod A netns: eth0 (10.244.1.2/24)
      |
   veth pair
      |
宿主机: vethA --- cni0 (10.244.1.1/24) --- vethB --- Pod B netns: eth0 (10.244.1.3/24)
      |
   物理网卡 eth0 (192.168.1.10/24)

在这个示例中,Pod A和Pod B的IP都属于10.244.1.0/24网段,网关指向cni0的地址10.244.1.1。当Pod A ping Pod B时,数据包从Pod A的eth0发出,经过veth pair到达宿主机侧的vethA,进入cni0网桥,网桥根据MAC地址表转发到vethB,最终进入Pod B。整个过程不涉及宿主机的外部路由,延迟极低。

CNI插件规范与调用流程

Kubernetes本身并不实现Pod网络,而是通过CNI(Container Network Interface)规范把网络配置委托给插件。CNI定义了一组标准接口:插件是一个可执行文件,运行时通过环境变量和标准输入接收配置,执行网络操作,并以JSON格式返回结果。核心操作包括ADD、DEL、CHECK和VERSION。

当kubelet创建一个Pod时,它会调用容器运行时(如containerd)创建沙箱容器,然后由运行时调用CNI插件。具体流程如下:

  1. kubelet通过CRI接口请求创建Pod沙箱。
  2. containerd创建Pod的网络命名空间,并生成一个CNI配置文件(通常位于/etc/cni/net.d/)。
  3. containerd调用CNI插件二进制文件(如/opt/cni/bin/bridge),传入ADD命令、容器ID、网络命名空间路径、接口名等参数。
  4. 插件根据配置创建veth pair,把一端放入Pod命名空间并重命名为eth0,另一端挂到网桥,分配IP地址,设置路由。
  5. 插件返回JSON结果,包含分配的IP、网关、DNS等信息。containerd把这些信息写入Pod的沙箱状态。

当Pod被删除时,运行时调用DEL命令,插件清理veth pair、释放IP、删除路由。如果插件支持CHECK,kubelet还可以定期检查网络状态。

一个典型的CNI配置文件(/etc/cni/net.d/10-bridge.conflist)如下所示:

{
  "cniVersion": "1.0.0",
  "name": "mynet",
  "plugins": [
    {
      "type": "bridge",
      "bridge": "cni0",
      "isGateway": true,
      "ipMasq": true,
      "ipam": {
        "type": "host-local",
        "subnet": "10.244.1.0/24",
        "routes": [
          { "dst": "0.0.0.0/0" }
        ]
      }
    },
    {
      "type": "portmap",
      "capabilities": { "portMappings": true }
    }
  ]
}

这个配置使用了bridge插件和host-local IPAM插件。bridge负责创建网桥和veth pair,host-local负责从子网中分配IP。portmap插件则支持hostPort映射。注意ipMasq: true会为出节点流量添加SNAT规则,使Pod能访问集群外网络。

常见CNI插件对比

面试中常要求对比不同CNI插件。以下从数据平面、网络模型、适用场景三个维度进行说明:

插件数据平面网络模型适用场景
Bridge + host-localLinux网桥 + iptables节点内二层转发,跨节点依赖路由或Overlay小规模集群、学习环境
CalicoBGP + iptables/eBPF纯三层路由,无Overlay大规模、高性能、需要网络策略
FlannelVXLAN或host-gwOverlay或三层路由简单易用,中小规模
CiliumeBPF三层路由,可替代kube-proxy高性能、可观测性、安全策略

以Calico为例,它默认不使用网桥,而是为每个Pod创建veth pair,并在宿主机上添加指向Pod IP的路由。跨节点通信通过BGP协议交换路由信息,每个节点知道其他节点上Pod网段的下一跳。这种方案没有Overlay封装,性能接近原生网络,但要求底层网络支持BGP或使用IPIP模式。Flannel的VXLAN模式则把Pod数据包封装在UDP中,通过宿主机网络传输,对底层网络无要求,但有一定性能开销。

实践观察与判断依据

在节点上,可以用以下命令观察Pod网络的实际状态:

# 查看网桥和veth接口
ip link show type bridge
ip link show type veth

# 查看Pod网络命名空间中的接口
ip netns list
ip netns exec <netns> ip addr show eth0

# 查看路由表
ip route show

# 查看iptables NAT规则
iptables -t nat -L -n

判断Pod网络是否正常,可以依次检查:Pod内eth0是否有IP且状态为UP;宿主机上对应的veth是否挂到网桥;网桥是否UP;路由表中是否有Pod网段的路由;跨节点通信时,对端节点是否有回程路由。如果Pod能ping通同节点其他Pod但无法跨节点,通常问题出在跨节点路由或Overlay隧道上。如果Pod无法访问外部网络,检查ipMasq是否生效,即iptables -t nat -L POSTROUTING中是否有MASQUERADE规则。

理解Pod网络命名空间、veth pair、网桥和CNI调用流程,不仅是为了回答面试题,更是排查实际网络故障的基础。当你能在节点上手动追踪一个数据包从Pod发出到离开宿主机的完整路径时,就已经掌握了Kubernetes网络工程师的核心能力。