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插件。具体流程如下:
- kubelet通过CRI接口请求创建Pod沙箱。
- containerd创建Pod的网络命名空间,并生成一个CNI配置文件(通常位于
/etc/cni/net.d/)。 - containerd调用CNI插件二进制文件(如
/opt/cni/bin/bridge),传入ADD命令、容器ID、网络命名空间路径、接口名等参数。 - 插件根据配置创建veth pair,把一端放入Pod命名空间并重命名为
eth0,另一端挂到网桥,分配IP地址,设置路由。 - 插件返回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-local | Linux网桥 + iptables | 节点内二层转发,跨节点依赖路由或Overlay | 小规模集群、学习环境 |
| Calico | BGP + iptables/eBPF | 纯三层路由,无Overlay | 大规模、高性能、需要网络策略 |
| Flannel | VXLAN或host-gw | Overlay或三层路由 | 简单易用,中小规模 |
| Cilium | eBPF | 三层路由,可替代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网络工程师的核心能力。