2、Kubernetes网络基础:Pod网络模型、Service与ClusterIP、NetworkPolicy概念、CNI插件工作原理与调用链

Kubernetes网络是云原生基础设施中最复杂、也最容易被误解的领域之一。很多工程师在部署Calico或Cilium时,往往直接套用默认配置,遇到Pod IP不通、Service访问超时、策略不生效等问题时,才回头补网络基础。实际上,无论是Calico的BGP路由模式,还是Cilium的eBPF数据路径,它们都只是Kubernetes网络模型的实现者,而模型本身是独立于具体CNI插件的。本章将系统梳理Pod网络模型、Service与ClusterIP的转发逻辑、NetworkPolicy的管控语义,以及CNI插件的完整调用链,为后续深入Calico和Cilium的实战打下坚实的认知底座。

Kubernetes网络模型与CNI调用链全景 Pod网络模型 每个Pod拥有唯一Cluster内IP Pod间直接通信,无需NAT Node与Pod可互通(扁平网络) Service与ClusterIP ClusterIP: 虚拟IP,仅集群内可达 kube-proxy: iptables/IPVS/eBPF Endpoints/EndpointSlice: 后端Pod列表 NetworkPolicy 基于LabelSelector选择Pod Ingress/Egress规则 + 策略类型 由CNI数据路径强制执行 CNI插件调用链(kubelet → CNI → 数据路径) kubelet 创建Pod沙箱 CRI (containerd) 运行时调用 CNI Plugin calico/cilium 数据路径 veth/bridge/BGP/eBPF Pod网络命名空间 eth0@veth ADD: 创建网络 | DEL: 清理网络 | CHECK: 校验网络 | GC: 垃圾回收 CNI配置: /etc/cni/net.d/*.conf | CNI二进制: /opt/cni/bin/ 1 2 3 4

上图展示了Kubernetes网络体系的核心组件关系。左侧是Pod网络模型的基本语义,中间是Service抽象层,右侧是NetworkPolicy的管控范围,底部则是CNI插件从kubelet到数据路径的完整调用链。理解这张图,就掌握了Kubernetes网络的骨架。接下来,我们逐一深入每个模块。

2.1 Pod网络模型:扁平网络与IP-per-Pod

核心原理

Kubernetes对Pod网络提出了三个基本要求:任意两个Pod之间可以直接通信,无需NAT转换;Node与Pod之间可以直接通信;Pod看到的自身IP与其他实体看到的IP一致。这就是著名的“IP-per-Pod”模型。这个模型的设计初衷是消除传统容器网络中端口映射和NAT带来的复杂性,让Pod像虚拟机一样拥有独立的网络身份。

为了实现这个模型,每个Pod都会分配一个集群内唯一的IP地址(通常来自CNI配置的Pod CIDR)。Pod内的所有容器共享同一个网络命名空间,通过lo接口和eth0接口通信。eth0实际上是一个veth pair的一端,另一端连接在Node的根网络命名空间中(通常是一个Linux bridge或直接绑定到主网卡)。

网络层级作用范围典型实现关键特性
Pod内容器共享网络命名空间lo, eth0通过localhost通信,端口共享
同一Node上的PodNode本地二层网络Linux bridge (cni0/docker0)直接二层转发,无需路由
跨Node的Pod集群三层网络BGP (Calico), VXLAN (Flannel), eBPF (Cilium)需要路由协议或隧道封装
Node与PodNode路由表路由规则 + 转发Node可直接访问Pod IP

判断方法与操作流程

在实际排障中,判断Pod网络模型是否正常,可以按以下步骤操作:

  1. 检查Pod IP分配:kubectl get pod -o wide 查看Pod IP是否在预期CIDR范围内。
  2. 验证同Node通信:进入Pod执行 ping <同Node其他Pod IP>,观察是否通。
  3. 验证跨Node通信:从Pod ping 不同Node上的Pod IP,若不通则检查路由表或隧道状态。
  4. 检查Node到Pod:在Node上直接 curl <Pod IP>:<端口>,确认Node侧路由正确。
经验提醒:在排查Pod网络不通时,先确认Pod IP是否分配成功,再逐层检查veth、bridge、路由表。不要一上来就怀疑CNI插件配置,很多问题出在Node内核参数(如net.ipv4.ip_forward)或安全组规则上。

案例拆解:Pod IP冲突引发的“幽灵”故障

某生产集群中,部分Pod频繁出现网络间歇性中断。排查发现,两个不同Node上的Pod竟然分配了相同的IP地址。原因是集群使用了两个CNI配置并存(Flannel和Calico同时运行),导致IPAM(IP地址管理)冲突。kubelet在创建Pod时,会按照/etc/cni/net.d/目录下的配置文件顺序加载CNI插件,如果存在多个配置文件,就会导致IP分配混乱。

解决过程:首先通过 kubectl get pods -A -o wide | awk '{print $6}' | sort | uniq -d 找出重复IP。然后检查所有Node上的 /etc/cni/net.d/ 目录,删除多余的配置文件,只保留一个CNI插件。最后重启kubelet和容器运行时,IP分配恢复正常。

风险提示:在已有业务的集群中切换或删除CNI配置属于高风险操作,务必先在测试环境验证,并做好备份。同时,确保所有Node上的CNI配置完全一致,否则会出现部分Node网络正常、部分异常的情况。

常见误区与总结复盘

  • 误区一:认为Pod IP在集群外也可路由。实际上,Pod IP默认仅在集群内部可达,外部访问需要NodePort或Ingress。
  • 误区二:忽略Node上的路由表。Calico等BGP方案依赖Node路由表,如果路由被意外覆盖,跨Node通信就会中断。
  • 误区三:将Pod网络与Service网络混为一谈。Pod网络是真实的三层网络,而Service网络是虚拟的,两者转发机制完全不同。

总结:Pod网络模型的核心是“扁平、直连、无NAT”,所有CNI插件都是对这个模型的落地实现。理解了这个模型,后续学习Calico的BGP和Cilium的eBPF时,就能明白它们各自在解决什么问题。

2.2 Service与ClusterIP:虚拟IP的转发魔法

核心原理

Pod IP是动态的,Pod重建后IP就会变化。为了提供稳定的访问入口,Kubernetes引入了Service资源。Service定义了一组Pod的逻辑集合和访问策略,其核心是ClusterIP——一个虚拟的、仅集群内部可达的IP地址。ClusterIP本身没有对应的网络设备,它只是kube-proxy在Node上写入的转发规则。

kube-proxy支持三种转发模式:iptables(默认,基于随机规则匹配)、IPVS(基于内核态负载均衡,支持更多调度算法)、eBPF(Cilium专用,绕过iptables,性能最优)。无论哪种模式,其基本逻辑都是:当数据包的目的地址是ClusterIP时,将其DNAT(目的地址转换)为某个后端Pod IP,并记录连接跟踪信息,以便回程流量能正确转换回来。

判断方法与操作流程

当Service访问异常时,可以按以下流程排查:

  1. 检查Service定义:kubectl describe svc <service-name> 查看ClusterIP、Port、Selector是否正确。
  2. 检查Endpoints:kubectl get endpoints <service-name> 确认后端Pod IP列表是否非空。如果为空,说明Selector没有匹配到任何Pod。
  3. 检查kube-proxy规则:在Node上执行 iptables -t nat -L KUBE-SERVICES -n 或 ipvsadm -Ln,确认ClusterIP的转发规则存在。
  4. 验证转发:在集群内任意Pod中 curl <ClusterIP>:<Port>,观察是否通。
关键参数:Service的sessionAffinity字段可以设置会话保持(ClientIP),但要注意,在iptables模式下,会话保持是通过ipvs或iptables的recent模块实现的,对性能有一定影响。IPVS模式支持sh(源地址哈希)调度,更适合大规模场景。

案例拆解:ClusterIP不通,但Pod IP直连正常

某微服务应用反馈,通过Service访问后端API超时,但直接访问Pod IP却正常。初步判断是kube-proxy规则问题。登录Node检查iptables,发现KUBE-SERVICES链中确实存在该Service的规则,但规则中的目标端口与Pod实际监听端口不一致。

进一步排查发现,Service定义中targetPort写的是8080,而Pod容器实际监听的是9090。由于targetPort错误,DNAT将流量转发到了Pod的错误端口,导致连接被拒绝。修正targetPort后,Service访问恢复正常。

风险提示:修改Service定义会触发kube-proxy重新生成规则,在流量高峰期可能导致瞬时连接中断。建议在低峰期操作,并提前通知业务方。

常见误区与总结复盘

  • 误区一:认为ClusterIP可以在集群外访问。实际上,ClusterIP是虚拟的,外部流量无法直接路由到它,必须通过NodePort或LoadBalancer。
  • 误区二:忽略externalTrafficPolicy的影响。默认值为Cluster,流量可能被转发到非本节点的Pod,导致源IP丢失;设置为Local可以保留源IP,但可能导致负载不均。
  • 误区三:在iptables模式下,Service数量过多会导致规则膨胀,性能下降。此时应考虑切换到IPVS模式。

总结:Service是Kubernetes流量治理的基石,ClusterIP是虚拟IP,其转发逻辑完全由kube-proxy控制。理解iptables/IPVS/eBPF三种模式的差异,是后续优化网络性能的关键。

2.3 NetworkPolicy概念:零信任网络的第一道门槛

核心原理

默认情况下,Kubernetes集群中的Pod可以自由通信,这显然不符合生产环境的安全要求。NetworkPolicy是一种资源对象,它通过标签选择器(LabelSelector)来定义Pod之间的访问控制策略。NetworkPolicy是白名单机制:一旦某个Pod被NetworkPolicy选中,它默认拒绝所有未被显式允许的流量。

NetworkPolicy包含三个核心要素:podSelector(选择策略作用的Pod)、policyTypes(指定Ingress、Egress或两者)、ingress/egress规则(定义允许的流量来源和目的地)。需要注意的是,NetworkPolicy本身只是一个声明式API,真正的强制执行由CNI插件完成。如果CNI插件不支持NetworkPolicy(如Flannel的VXLAN模式),策略将不会生效。

判断方法与操作流程

验证NetworkPolicy是否生效,可以按以下步骤操作:

  1. 确认CNI支持:查看CNI插件文档,确认其支持NetworkPolicy(Calico、Cilium均支持)。
  2. 创建测试Pod:部署两个带有不同标签的测试Pod,并启动一个简单的HTTP服务。
  3. 应用NetworkPolicy:创建一个只允许特定来源访问的Ingress策略。
  4. 验证连通性:从允许的来源访问目标Pod,应该成功;从其他来源访问,应该超时或拒绝。
经验提醒:在调试NetworkPolicy时,可以使用 kubectl describe netpol <policy-name> 查看策略详情,但更有效的方法是直接查看CNI插件的数据路径状态。例如,Calico可以通过 calicoctl get networkpolicy 查看,Cilium可以通过 cilium policy get 查看。

案例拆解:误配置导致业务中断

某团队在命名空间prod中部署了一个数据库Pod,并创建了一条NetworkPolicy,意图只允许app=api的Pod访问数据库的3306端口。然而,策略生效后,所有应用都无法连接数据库。

排查发现,策略中podSelector写的是app: database,但数据库Pod的标签实际是role: db。由于标签不匹配,策略没有选中任何Pod,但policyTypes中包含了Ingress,导致该命名空间下所有Pod的入站流量被默认拒绝。修正标签后,策略正确匹配,业务恢复。

风险提示:NetworkPolicy的默认拒绝行为是“安全”的,但也是“危险”的。在创建策略前,务必确认标签选择器的准确性,并先在测试命名空间验证。建议使用kubectl run临时Pod进行连通性测试,避免影响生产业务。

常见误区与总结复盘

  • 误区一:认为NetworkPolicy可以跨命名空间生效。实际上,NetworkPolicy只能作用于其所在命名空间内的Pod。
  • 误区二:忽略Egress策略。很多团队只配置Ingress,导致Pod可以接收流量,但无法主动访问外部服务(如DNS解析失败)。
  • 误区三:认为NetworkPolicy能控制Service流量。实际上,NetworkPolicy作用于Pod IP层面,Service的ClusterIP转发不受NetworkPolicy直接管控,但最终流量会到达Pod,因此Ingress策略仍然有效。

总结:NetworkPolicy是Kubernetes安全体系的重要组成部分,但它依赖CNI插件的实现。在规划网络策略时,需要同时考虑Ingress和Egress,并确保标签设计规范。

2.4 CNI插件工作原理与调用链

核心原理

CNI(Container Network Interface)是Kubernetes与网络插件之间的标准接口协议。它定义了一组规范,包括插件二进制、配置文件格式和调用约定。当kubelet需要为Pod创建网络时,会按照以下顺序调用CNI插件:

  1. 读取配置:kubelet从 /etc/cni/net.d/ 目录读取配置文件(如 10-calico.conflist),确定要调用的插件类型和参数。
  2. 执行ADD命令:kubelet通过CRI(如containerd)调用CNI插件的 ADD 命令,传入容器ID、网络命名空间路径等参数。
  3. 插件执行网络配置:CNI插件根据配置创建veth pair、分配IP、设置路由,并将结果(IP、MAC等)返回给kubelet。
  4. 后续操作:Pod删除时,kubelet调用 DEL 命令清理网络资源;CHECK 命令用于校验网络状态。

CNI插件通常分为主插件(负责创建网络接口)和IPAM插件(负责分配IP地址)。例如,Calico的CNI插件会调用 calico-ipam 来分配IP,而Cilium则内置了IPAM功能。

判断方法与操作流程

当CNI插件调用失败时,可以从以下角度排查:

  1. 查看kubelet日志:journalctl -u kubelet -f 或 tail -f /var/log/kubelet.log,搜索“CNI”或“network”关键字。
  2. 检查CNI配置:确认 /etc/cni/net.d/ 目录下只有一个配置文件,且内容格式正确。
  3. 手动执行CNI命令:在Node上手动执行 CNI_COMMAND=ADD CNI_CONTAINERID=test CNI_NETNS=/var/run/netns/test CNI_IFNAME=eth0 /opt/cni/bin/calico,观察输出和错误信息。
  4. 检查IPAM状态:查看IP池是否耗尽,或IPAM数据是否损坏。
关键文件路径:CNI配置文件目录:/etc/cni/net.d/;CNI插件二进制目录:/opt/cni/bin/;容器运行时(containerd)配置文件:/etc/containerd/config.toml。

案例拆解:CNI插件版本不兼容导致Pod创建失败

某集群升级containerd后,新创建的Pod一直处于ContainerCreating状态。查看kubelet日志,发现报错:failed to find plugin "calico" in path /opt/cni/bin。检查发现,升级过程中,/opt/cni/bin目录下的Calico插件被覆盖为旧版本,而新版本的containerd要求CNI插件支持特定的接口版本。

解决过程:重新下载与集群版本匹配的Calico CNI插件,替换到 /opt/cni/bin 目录,并重启kubelet。同时,检查 /etc/cni/net.d/ 中的配置文件,确保cniVersion字段与插件支持的版本一致。

风险提示:在升级Kubernetes或容器运行时之前,务必先验证CNI插件的兼容性。建议在测试环境先行演练,并备份所有CNI配置和二进制文件。

常见误区与总结复盘

  • 误区一:认为CNI插件只需要在Master节点安装。实际上,CNI插件必须安装在所有Node节点上,因为Pod可能调度到任何Node。
  • 误区二:忽略CNI配置文件的优先级。kubelet会按文件名顺序加载配置,如果有多个配置文件,可能导致网络行为异常。
  • 误区三:在CNI插件调用链中,DEL命令失败不会导致Pod删除失败,但会遗留网络资源(如veth、IP地址),长期积累会耗尽资源。

总结:CNI插件是Kubernetes网络的实际执行者,理解其调用链和配置方式,是排障和优化网络的基础。Calico和Cilium作为主流的CNI插件,各自有不同的实现路径,后续章节将深入剖析。

本章核心要点回顾:
  • Pod网络模型要求扁平、直连、无NAT,所有CNI插件都是对这个模型的实现。
  • Service的ClusterIP是虚拟IP,转发逻辑由kube-proxy控制,支持iptables、IPVS、eBPF三种模式。
  • NetworkPolicy是白名单机制,依赖CNI插件强制执行,配置前务必确认标签选择器。
  • CNI调用链为:kubelet → CRI → CNI插件 → 数据路径,理解每个环节的职责是排障的关键。