3. Pod间通信与同节点网络

在Kubernetes集群中,同一节点上的两个Pod之间通信,看似简单,实则涉及Linux网络栈中多个关键机制:虚拟网卡对、网桥转发、ARP解析、路由决策以及CNI插件的配置。理解这条路径,是网络工程师排查Pod连通性问题的基本功。本节从数据包的实际流向出发,逐步展开每个环节的工作原理与验证方法。

一、同一节点Pod通信的完整路径

假设节点上运行着两个Pod:Pod-A(IP 10.244.1.10)和Pod-B(IP 10.244.1.11),它们位于同一子网。当Pod-A内的进程向Pod-B发起请求时,数据包的旅程如下:

首先,Pod-A内部的eth0是一对veth pair的一端,另一端挂在宿主机的网桥上(通常名为cni0或docker0)。数据包从Pod-A的eth0发出,经过veth pair到达宿主机侧的veth接口,该接口被绑定到网桥。网桥收到帧后,根据目标MAC地址决定转发端口。由于Pod-A和Pod-B在同一子网,Pod-A会先通过ARP请求获取Pod-B的MAC地址。网桥在收到ARP请求后,会向所有端口泛洪(除接收端口外),Pod-B的veth接口响应ARP,返回自己的MAC地址。此后,Pod-A将数据帧直接发往Pod-B的MAC,网桥根据MAC地址表将帧转发到Pod-B对应的veth端口,最终到达Pod-B的eth0。

这条路径的核心是:veth pair + 网桥 + ARP + 同子网路由。整个过程不涉及宿主机的外部路由,也不需要NAT,因为源和目标在同一二层域内。

二、ARP与路由表的关键作用

在Pod内部,路由表决定了数据包从哪个接口发出。以Pod-A为例,执行ip route通常会看到:

default via 10.244.1.1 dev eth0
10.244.1.0/24 dev eth0 scope link src 10.244.1.10

这条路由表明:目标10.244.1.11匹配10.244.1.0/24网段,直接通过eth0发出,无需网关。Pod-A随后查询ARP缓存,若没有Pod-B的MAC,则发送ARP请求。ARP请求的源IP是10.244.1.10,目标IP是10.244.1.11,源MAC是Pod-A的eth0 MAC,目标MAC是广播地址ff:ff:ff:ff:ff:ff。

网桥收到广播帧后,向所有端口转发。Pod-B收到ARP请求,发现目标IP是自己,于是单播回复ARP响应,包含自己的MAC地址。Pod-A收到响应后,将映射写入ARP缓存,后续数据帧直接使用该MAC。

在宿主机上,网桥也维护自己的MAC地址表(FDB)。可以通过bridge fdb show dev cni0查看,输出中会列出每个veth接口对应的MAC地址。当网桥收到目标MAC为Pod-B的数据帧时,查表得知应从哪个veth端口发出,从而实现精准转发。

三、CNI配置示例与关键参数

上述网络行为由CNI插件配置决定。以常见的bridge插件为例,一个典型的CNI配置文件(/etc/cni/net.d/10-bridge.conflist)如下:

{
  "cniVersion": "0.4.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指定网桥名称;isGateway为true时,网桥接口会配置一个网关IP(通常是子网第一个地址,如10.244.1.1);ipMasq开启SNAT,用于Pod访问集群外;ipam中的subnet定义了Pod的IP范围,routes指定默认路由。当Pod创建时,CNI插件会创建veth pair,一端放入Pod的网络命名空间并重命名为eth0,另一端连接到cni0网桥,并从subnet中分配IP。

需要注意的是,同一节点上的Pod通信不经过ipMasq规则,因为源和目标都在同一子网,路由表直接匹配链路层,不会触发SNAT。

四、抓包验证方法与结果判读

要验证上述过程,最直接的方法是在Pod内和宿主机网桥上同时抓包。假设Pod-A的命名空间可通过crictl或nsenter进入,操作如下:

第一步,在Pod-A内启动tcpdump,监听eth0:

kubectl exec -it pod-a -- tcpdump -i eth0 -nn -e icmp

第二步,在宿主机上抓取cni0网桥的流量:

tcpdump -i cni0 -nn -e icmp

第三步,从Pod-A ping Pod-B:

kubectl exec -it pod-a -- ping -c 2 10.244.1.11

观察Pod-A内的tcpdump输出,应看到ICMP请求的源MAC为Pod-A的eth0 MAC,目标MAC为Pod-B的MAC(或广播地址,取决于ARP缓存是否已存在)。在cni0上的抓包应显示相同的帧,且源和目标MAC不变,说明网桥进行了二层转发。如果ARP缓存为空,第一次ping会先触发ARP请求和响应,随后才是ICMP。通过arp -n在Pod-A内查看ARP缓存,应能看到10.244.1.11对应的MAC地址。

若通信失败,排查顺序建议为:检查Pod内路由表是否正确;检查ARP缓存是否解析成功;检查网桥FDB是否包含目标MAC;检查CNI配置中subnet是否一致;检查iptables规则是否误拦截。常见问题包括:网桥未启用、veth pair未正确挂载、IPAM分配了不同子网的IP、或者宿主机防火墙丢弃了转发流量。

五、关键检查清单

检查项 命令示例 预期结果
Pod内路由表 ip route 同子网路由指向eth0,默认路由指向网关
Pod内ARP缓存 arp -n 目标Pod IP对应正确MAC
网桥FDB bridge fdb show dev cni0 包含两个Pod的veth MAC
veth pair状态 ip link show veth接口UP,且master为cni0
CNI配置文件 cat /etc/cni/net.d/*.conflist subnet与Pod IP段一致

掌握同一节点Pod通信的底层机制,不仅有助于快速定位连通性问题,也为理解跨节点通信、Overlay网络和NetworkPolicy打下坚实基础。在实际工作中,建议结合tcpdump、ip、bridge等工具,从二层到三层逐层验证,形成系统化的排查思路。