1. Kubernetes 网络模型到底在解决什么问题
1.1 先理清四个逃不掉的需求
很多人一上来就纠结“选哪个 CNI”,结果把 Flannel 换成 Calico、Calico 换成 Cilium,折腾一圈也没搞明白自己到底要解决什么问题。实际上 Kubernetes 的网络需求可以拆成四件非常具体的事:容器与容器之间怎么通信、Pod 与 Pod 之间怎么通信、Pod 与 Service 之间怎么通信、集群与外部世界怎么通信。把这四件事摆清楚,选型就不是玄学,而是按需匹配。
先说容器与容器的通信。同一个 Pod 里的多个容器共享 Network Namespace,共享同一个 IP 和端口空间,所以它们之间走 localhost 就行,这部分基本不需要 CNI 参与,Pod 内的 pause 容器负责持有网络命名空间。这个机制很多人容易忽略,但它恰恰是后续理解 CNI“只负责 Pod 与 Pod、Pod 与外部”的分界线。
然后是 Pod 与 Pod 的通信。这是 CNI 的核心战场。Kubernetes 的网络模型要求:集群内每一个 Pod 都必须拥有一个集群内唯一的 IP 地址,并且任意两个 Pod 之间可以不经过 NAT 直接通信。这句话包含两层意思:第一,IP 必须唯一,不能冲突;第二,不管两个 Pod 在不在同一个节点上,通信路径都要是通的,而且不能做地址转换。这就是为什么我们需要地址分配机制、路由机制或封装机制的根因。
第三是 Pod 与 Service 的通信。Service 是一组 Pod 的抽象入口,它有自己的虚拟 IP(ClusterIP)。Pod 访问 Service 时,流量会被 kube-proxy 的 iptables 规则或 IPVS 规则转发到后端某个具体的 Pod 上。这里要注意一点,Service 的 ClusterIP 是 iptables/IPVS 层面的虚拟概念,它不属于 CNI 的职责范围,但 CNI 选择会影响 kube-proxy 的工作模式,比如 eBPF 模式下可以直接绕过 kube-proxy,由 Cilium 自己处理 Service 转发。
最后是集群对外通信。包括从 Pod 访问互联网(出站),以及从外部访问集群内的服务(入站)。出站流量通常依赖 NAT 完成,入站则依赖 NodePort、LoadBalancer 或 Ingress。这部分的路径设计也会受 CNI 影响,比如 Calico 在云环境下可以跟云厂商的 NAT 网关集成,Cilium 可以用 eBPF 实现更高效的入站转发。
1.2 每个 Pod 一个 IP 凭什么能做到
要理解 CNI 为什么能实现“每个 Pod 一个 IP”,得先搞清楚 IP 分配、网络接口创建、路由下发这三个环节是怎么配合的。
IP 分配方面,Kubernetes 集群在初始化时会给整集群划一个大网段(通常用--cluster-cidr指定,比如10.244.0.0/16)。每个节点再从这个大网段里分到一个子网段(比如10.244.1.0/24),节点上所有 Pod 的 IP 都会从这个子网段里出。这个“大网段切小网段、按节点分发”的过程,是通过 kube-controller-manager 的--allocate-node-cidrs=true参数触发的,各个 CNI 方案会通过监听 Kubernetes 的 Node 资源来感知每个节点被分配的子网。
网络接口创建方面,当调度器把 Pod 调度到某个节点后,容器运行时会调用 CNI 插件。CNI 插件要做的第一件事是在宿主机上创建一个虚拟网卡(通常是 veth pair),一头塞进 Pod 的网络命名空间,成为 Pod 里的eth0,另一头挂在宿主机的一个中间设备上。这个中间设备在不同 CNI 里不一样,Flannel 用的是 cni0 网桥,Calico 直接用宿主机的路由表,Cilium 则用名为 cilium_host 的虚拟设备。
路由下发方面,每个方案的路由策略差异很大。Flannel 的 VXLAN 模式会维护一张“节点 IP -> 宿主机 IP”的映射表,收到跨节点的包就封装成 VXLAN 隧道包送过去;Calico 则直接把每个节点当作一台路由器,通过 BGP 把路由信息广播出去;Cilium 在 eBPF 层面埋点,用 eBPF Map 维护节点和 Pod 的关联关系,数据路径上不做封装而是直接转发。
所以,选 CNI 的本质就是选“IP 分配方式 + 网络接口模型 + 路由/封装策略”的组合。把这个框架装进脑子里,后面看各个方案的对比就顺了。
2. 主流 CNI 方案横向对比与核心原理
2.1 Flannel:入门必经之路,但天花板明显
Flannel 是 CoreOS 出品的老牌 CNI,也是很多新手集群的“初恋”。它的设计哲学非常纯粹:只要做到“每个 Pod 唯一 IP、跨节点互通”就行,别的一概不管。Flannel 支持三种后端模式:VXLAN、host-gw、UDP。UDP 模式性能太差,现在基本没人用;VXLAN 是默认模式,适合所有环境;host-gw 性能最好,但要求所有节点二层直连。
VXLAN 模式的原理可以这样理解:每个节点上跑一个 flanneld 守护进程,这个进程会监听节点的子网分配情况,并在本地维护一张“Pod 网段 -> 宿主机 IP”的映射表。当一个 Pod 要访问另一个节点上的 Pod 时,cni0 网桥发现目的 IP 不在本节点子网内,就把包扔给 flannel.1 这个 VXLAN 隧道设备;flannel.1 根据映射表找到对端宿主机 IP,把原始的二层帧封装在一个 UDP 包里发过去;对端的 flannel.1 收到包后拆掉封装,再把包交给对端 cni0 网桥,最后送达到目标 Pod。整个过程对 Pod 里的应用完全透明,应用感知不到隧道存在。
Flannel 最大的优势是简单。二进制架构就一个 DaemonSet,不依赖 etcd、不依赖 BGP、不依赖 eBPF,部署上去几乎不会出什么问题。它把所有节点当做一个等于号的二层网络来看待,逻辑清晰。
但 Flannel 的短板也极其明显。第一,它没有实现 NetworkPolicy,这意味着你无法用 Kubernetes 的原生网络策略做东西向流量的访问控制,对大部分生产环境来说这是不可接受的。第二,VXLAN 模式把数据包多封装了一层 UDP 头,性能有损耗,尤其是在高吞吐场景下 CPU 开销会明显上升。第三,Flannel 的 VXLAN 转发是查表转发,节点规模大了之后,每台机器要维护全量节点的映射表,ARP 风暴和流表膨胀的问题会被放大。第四,不支持多网卡场景下的精细路由选择,绑定数据面网卡时得手动配置。
坦率说,如果你的集群规模在 50 节点以内、对网络策略没有硬性要求、主要用来学习和跑测试环境,Flannel 是性价比最高的入门选择。但一旦要考虑生产环境,我建议直接跨过 Flannel 去看下面两个方案。
2.2 Calico:三层路由的典型代表,生产环境的常青树
Calico 的底层思想与 Flannel 完全不同,它不做隧道封装,而是把整个集群当成一台“分布式路由器”。Calico 的每个节点上都运行着一个 BGP Agent(Felix + Bird),Felix 负责在宿主机上写入 iptables 规则和路由表,Bird 负责通过 BGP 协议与其他节点的 Agent 交换路由信息。
Calico 默认的 IPIP 模式会在跨节点通信时做一个简单的 IP 包外层封装。之所以保留这个封装,是为了解决“跨网段(三层隔离)环境下路由不可达”的问题——只要两个节点不在同一个二层网络里,IPIP 隧道就成了默认通道。如果你的集群所有节点都在一个大二层内,完全可以把 IPIP 模式关掉,切到 BGP 纯路由模式,这时候数据包不经过任何隧道,网络路径和物理网络几乎没有差别,性能和延迟都非常接近宿主机原生网络的水平。
Calico 在路由层面的表现只是基本功,它真正的护城河是丰富的数据面策略能力。Felix 会把 NetworkPolicy 翻译成 iptables 规则,逐个 Pod 地匹配生效,支持命名空间级、Label 级、IP 级乃至端口级的策略控制。在 Kubernetes 原生网络策略之外,Calico 还支持 GlobalNetworkPolicy、GlobalNetworkSet、Profile 等扩展对象,可以做跨命名空间、跨集群的安全策略编排。这一点在政企、金融类的合规性要求非常高的场景里尤其有分量。
不过 Calico 也不是没毛病。iptables 规则在高并发场景下会带来不可忽视的性能开销。每个新建连接都要遍历规则链,Pod 数量上来后规则数量会变得非常大,数据面的转发延迟和 CPU 消耗都会涨。如果你的集群形态是“少量大规格节点 + 海量 Pod”,Calico 的 iptables 瓶颈会更明显。
再有一个隐性问题:Calico 用 BGP 在全网广播路由,在节点数超过几百个时,BGP 会话的数量和路由表规模会快速膨胀。虽然 Calico 有 Route Reflector 的解决方案,但毕竟增加了架构复杂度,是需要投入精力去调的。
2.3 Cilium:eBPF 加持的新一代选手
Cilium 是近年社区热度上升最快的 CNI,核心卖点是基于 eBPF 技术重构数据面。传统 CNI 在转发路径上要经过“iptables -> 内核协议栈 -> 用户态 -> 内核协议栈”的多次拷贝和遍历,Cilium 则把 eBPF 程序直接挂载到网卡收包路径上,数据包在进入协议栈之前就被处理掉了。这意味着连接跟踪、负载均衡、网络策略、可观测性数据采集这些事情全部在 eBPF 这个内核虚拟机里完成,不需要反复在用户态和内核态之间切换。
性能方面 Cilium 的优势在延迟和吞吐两条曲线上都有体现。由于绕过了 iptables 和 kube-proxy 的用户态组件,Service 的负载均衡在 eBPF 层直接完成,新连接的处理时延可以压缩到微秒级,在重负载场景下 CPU 消耗明显低于传统 iptables 方案。我还记得有压测数据表明,Cilium 在高并发短连接的场景下,P99 延迟能比 iptables 模式低 30% 以上。这个差距在支付、交易、游戏对战类的高频服务里是非常有感知的。
Cilium 在功能丰富度上也走了完全不同的路线。它不只是做 Pod 互通,而是把 Service Mesh 的可观测性、零信任安全、多集群互联、透明加密全部纳入自己的生态。Cilium 支持 L3/L4 层网络策略,也支持 L7 层策略(比如只允许特定 HTTP 方法和路径通过)——这是绝大多数 CNI 做不到的。它还内置了 Hubble,可以直接可视化展示集群内的流量关系,排查问题的时候比对着 tcpdump 抓包要直观得多。
Cilium 的代价是学习曲线和运维复杂度明显上升。它引入的概念远比 Flannel、Calico 多:endpoint、identity、policy、CiliumNetworkPolicy、CiliumClusterwideNetworkPolicy、Hubble 等等。排查问题时,可能要同时打开 kubectl、cilium CLI、hubble CLI、各种 CRD 对象,对排障者的知识结构要求更高。此外,eBPF 对内核版本有硬性要求,一般建议至少 5.10 以上内核,且要确认节点环境中的内核编译选项是否打开了 CONFIG_BPF、CONFIG_BPF_JIT、CONFIG_BPF_SYSCALL 等相关配置。低内核版本的机器上就算安装了 Cilium,也会退回到 xfrm 隧道等兼容模式,性能打了折扣。
2.4 其他方案与差异化定位
在 Flannel、Calico、Cilium 之外,还有几个值得关注的方案,它们在特定场景下可能比“三巨头”更合适。
Weave Net:早期以“网状覆盖网络 + 内置 DNS 与加密”著称。它每个节点上会运行一个 Weave Router,所有 Router 之间建立 TCP 和 UDP 连接形成一个网格,数据包通过 mesh 转发。Weave 最大的优点是跨主机即使在三层网络环境中也能自动建立加密隧道,非常适合多数据中心的场景。缺点是 mesh 模式在节点多的时候连接数量按平方级增长,超过 100 节点就会明显吃力,现在用得越来越少了。
Antrea:VMware 开源的项目,基于 Open vSwitch 实现 OpenFlow 流表转发。它在数据面上继承了 OVS 的成熟度和高性能,同时天然兼容 VMware 的虚拟化体系,在混合负载(虚拟机和容器共存)的场景里有优势。如果你已经重度使用 vSphere,Antrea 会是值得评估的选项。
Kube-OVN:由国内社区主导的项目,把 OVN(Open Virtual Network)引入 Kubernetes,支持 VPC 网络隔离、QoS 限速、固定 IP、网络多租户等高级功能。在需要做云平台自研、多租户网络的场景下,Kube-OVN 的功能深度比 Calico 还要高一个档次,但社区的国际化程度和生态成熟度目前仍然有限。
下面一张表可以直观对比这些方案的关键差异:
| 方案 | 数据面模型 | 封装方式 | NetworkPolicy | 性能特征 | 典型场景 |
|---|---|---|---|---|---|
| Flannel | 网桥+veth | VXLAN / host-gw | 不支持 | 中低,VXLAN 有额外开销 | 学习、测试、小型集群 |
| Calico | 路由表+iptables | IPIP / BGP直连 | 支持,L3/L4 | 高(纯BGP模式),随规则变差 | 生产环境、合规安全 |
| Cilium | eBPF | 无/DSR/隧道 | 支持 L3-L7 | 极高,CPU 消耗低 | 高并发、微服务网格、可观测 |
| Weave Net | mesh路由器 | 自有封装 + 可选加密 | 支持(能 App 层) | 中低,mesh 规模受限 | 多数据中心小规模集群 |
| Antrea | OVS OpenFlow | Geneve / VXLAN | 支持 | 中高 | VMware 生态、混合负载 |
| Kube-OVN | OVN | Geneve / VXLAN | 支持 | 中高 | 多租户、VPC隔离 |
3. 选型决策框架与场景化推荐
3.1 选型不是选最好,是选最匹配
很多人的选型误区是“性能越高越好”,于是直接无脑上 Cilium。但如果你只是跑一个小型内部系统,Cilium 引入的复杂度反而让日常运维变得痛苦。我的建议是建立一个多维度的决策矩阵,把每个维度量化打分后再权衡,而不是凭感觉拍脑袋。
第一个维度是集群规模与节点形态。50 节点以下的集群,Flannel 或 Calico 的 BGP 模式都能轻松驾驭,感受不到明显瓶颈;200 节点以上的规模,Flannel 基本可以排除,Calico 需要认真配置 Route Reflector,Cilium 因为天生是分布式 eBPF Map 结构,在大规模下的表现要更平坦一些。另外还要看单个节点的 Pod 密度,Pod 密度高(比如单节点超过 100 个 Pod)时,Cilium 的内存占用和规则复杂度控制得更好。
第二个维度是功能需求清单。网络安全策略是不是刚需?如果是,Flannel 直接出局。是否需要 L7 层策略?如果需要,Calico 在这一点上并不完整,Cilium 的 Envoy 集成方案更成熟。是否需要透明的流量可观测性?Cilium + Hubble 的开箱体验是无敌的。有没有多租户隔离的需求?Kube-OVN 或 Cilium 的多集群特性更合适。
第三个维度是运维能力与团队技术栈。你团队里有没有懂 eBPF 或内核的人?如果出了问题能不能快速定位?对于大多数团队来说,Calico 的文档和社区方案最丰富,网络问题基本都能搜到答案;而 Cilium 的问题是当你真的遇到内核态的疑难杂症,社区能提供的帮助是有限的。运维能力和工具链的熟悉程度,往往会成为最后的决定性因素。
第四个维度是底层基础设施。如果跑在公有云上,要考虑云厂商的 VPC 是否支持 IP 直通(如阿里云的 Terway、AWS 的 VPC CNI)。如果跑在裸机机房,要考虑二层网络是否贯通。如果是混合云环境,则要评估隧道方案的兼容性。底层网络环境决定了你能不能用纯 BGP 直连,还是必须走 VXLAN 这类隧道模式。
3.2 场景化推荐:照着抄都行
我这里给几个偏“模板化”的推荐,谈不上绝对正确,但适合大多数读者作为起点。
场景一:学习环境、单机试验、小团队内部工具。推荐 Flannel VXLAN。一套命令就能装完,不依赖 BGP、不要求内核版本,即使配置出问题,把 flannel.1 和 cni0 删掉重新初始化也就十分钟。没什么好纠结的,用起来再说。
场景二:标准生产环境、中等规模(50-300节点)、有安全合规要求。推荐 Calico BGP 直连模式。这种场景下 Calico 的 iptables 规则数量是可控的,NetworkPolicy 能覆盖大部分需求,BGP 的成熟度和可排查性都很好。这是目前生产集群中占比最高的组合,各种云厂商托管集群也默认提供 Calico,算是全行业经验最丰富的组合。
场景三:大规模集群、高并发微服务、对延迟和性能有极致要求、且团队有内核排查经验。推荐 Cilium,开启 eBPF 主机路由和 DSR(Direct Server Return)模式。Cilium 会把 Service 负载均衡直接放进数据面,配合 Hubble 做可观测性,排障效率非常高。前提是你要能接受学习成本。
场景四:VMware 虚拟化平台、机房已有网络设备、希望做网络精细管理。推荐 Antrea 或 Kube-OVN。这两个方案与底层虚拟化生态的整合更紧密,而且对现有运维团队的技术栈更友好——OVS 和 OpenFlow 的思路与 VM 网络的运维经验是能对应上的。
4. 实操:从零部署一套多 CNI 可切换的集群
4.1 部署前的关键环境确认
我自己踩过不少坑之后,总结了几个部署前必须确认的点。先说内核版本,这是最容易被忽略的。很多人的 CentOS 7 服务器内核还在 3.10,这个版本对网络命名空间、veth、iptables 的支持没问题,但跑 Cilium 就费劲了。建议至少升级到 5.10 以上,几条命令就能搞定,这里不展开。
然后是 kubelet 和 kube-controller-manager 的启动参数。kubelet 上要确保--network-plugin=cni和--cni-conf-dir=/etc/cni/net.d、--cni-bin-dir=/opt/cni/bin这些参数正确。kube-controller-manager 上则必须设置--allocate-node-cidrs=true和--cluster-cidr=10.244.0.0/16。如果不设置,节点就分不到子网段,CNI 插件自然没法分配 Pod IP,这个问题表现成“Pod 启动后一直 ContainerCreating,事件里报 failed to get subnet”。
很多朋友用的是 kubeadm 部署,kubeadm 会为 kube-controller-manager 生成默认静态 Pod 清单,你可以在/etc/kubernetes/manifests/kube-controller-manager.yaml这个文件里看到默认的--cluster-cidr配置。kubeadm 默认的 Pod 网段是 10.244.0.0/16,但不同 CNI 建议的网段可能不同,Calico 建议 192.168.0.0/16 或 10.0.0.0/16,Cilium 则推荐 10.0.0.0/8 之外的网段以避免冲突。这里我强烈建议先确定 CNI 方案再初始化集群,否则初始化完之后发现网段不对,改起来就是整个集群推倒重建,别提多痛苦了。
4.2 Flannel 部署实操记录
初始化集群时,指定 Pod 网段为10.244.0.0/16,然后执行:
kubeadm init --pod-network-cidr=10.244.0.0/16完成之后,部署 Flannel:
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.ymlFlannel 的 DaemonSet 会在每个节点上启动一个 flanneld 容器和一个名为 kube-flannel 的 Pod。检查是否部署成功的核心命令是:
kubectl -n kube-flannel get pods -o wide然后验证集群内部 Pod 之间的通信。我习惯的验证方式是用 iperf3 压测两个跨节点的 Pod,先记录两个 Pod 的 IP,在一端启动服务端,另一端跑客户端。
# Pod A iperf3 -s # Pod B iperf3 -c <PodA_IP>如果输出显示带宽稳定且在预期范围内,说明 Flannel 的 VXLAN 通道是通的。这里有个很常见的坑:如果压测结果非常差(只有几百 Kbps),大概率是 MTU 问题。VXLAN 隧道会额外占用 50 字节的包头开销,所以物理网卡的 MTU 如果是 1500,VXLAN 的 MTU 一般要设置成 1450。Flannel 默认会检测物理网卡 MTU 并自动扣减,但如果你物理网卡启用了巨帧(MTU 9000),或者多层隧道叠加(比如 Kubernetes 跑在虚拟机里,虚拟机再套一层 VXLAN),就很容易出现 MTU 不匹配导致的分片问题,表现为大包不通、小包能通。可以使用如下命令测试:
kubectl exec -it <podA> -- ping -M do -s 1472 <podB_IP>如果丢包,逐步降低-s的大小,找到能通过的最大包大小,然后用这个值反推 MTU。
4.3 Calico 部署实操记录
Calico 推荐使用 Tigera Operator 方式部署。先安装 operator:
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml然后创建一个名为installation的自定义资源对象来触发 Calico 安装:
kubectl create -f - <<EOF apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: calicoNetwork: ipPools: - name: default-ipv4-ippool blockSize: 26 cidr: 10.244.0.0/16 encapsulation: VXLANCrossSubnet natOutgoing: true nodeAddressAutodetectionV4: interface: "eth0" EOF这里说明一下几个关键参数。blockSize是每个节点能拿到的子网掩码大小,26 表示每个节点分配/26网段(64 个 IP),如果单节点 Pod 密度高可以改成 24。encapsulation有三个选项:IPIP表示启用 IPIP 隧道,VXLANCrossSubnet表示只有跨子网时才用 VXLAN 封装,同子网走 BGP 直连,这个模式在国内多数机房环境中是体验和性能的平衡点。natOutgoing表示出站流量做 NAT,也就是 Pod 访问外部网络时,源 IP 会替换成节点 IP,一般建议打开,否则 Pod 的流量出去时可能被网络设备丢弃。
安装完成后查看运行状态:
kubectl get pods -n calico-systemCalico 的验证我习惯先确认 BGP 邻居状态。如果每个节点的 bird 进程显示 Established,说明 BGP 路由已经交换成功。然后跑到一个测试 Pod 里 ping 另一个节点的 Pod,能通就说明路由是通的。
kubectl exec -it <podA> -- ping <podB_IP>Calico 有个容易踩的坑是IP_AUTODETECTION_METHOD没设置对。节点上有多个网卡时,Calico 默认选择第一个网卡的 IP 作为 BGP 监听地址。如果你的默认路由网卡和实际业务网卡不是同一个,就会导致 Pod 流量能到宿主机但出不去,或者 BGP 邻居建不起来但实际上网络是通的这类诡异问题。解决办法是明确指定网卡:
kubectl set env daemonset/calico-node -n calico-system IP_AUTODETECTION_METHOD=interface=eth04.4 Cilium 部署实操记录
Cilium 推荐用 Helm 安装,也可以直接用 cilium CLI。先安装 CLI 工具,然后执行:
cilium install --version 1.15.0 \ --set kubeProxyReplacement=true \ --set ipam.mode=kuberneteskubeProxyReplacement=true表示让 Cilium 完全接管 Service 负载均衡,不再运行 kube-proxy。这个功能需要在节点上配置kubelet --network-plugin=cni之外,还要保证内核支持 eBPF 的bpf_host模式。如果条件不满足,Cilium 会自动降级到兼容模式,但这时一些高级特性不会生效,建议安装时留意安装日志。
安装完成后,Cilium 自带的连接测试功能是我用过的最省心的验证工具:
cilium connectivity test这个工具会创建一组测试 Pod,自动跑 DNS 解析、跨节点通信、Service 负载均衡、网络策略等测试,最后给出每个检查项的结果。跑完之后如果一切通过,基本可以确认数据面是健康的。Cilium 现场排障时,最有用的命令是:
cilium status cilium endpoint list cilium monitor -vcilium monitor可以实时打印数据面收到的每个数据包的信息,包括源 IP、目的 IP、源端口、目的端口、决策结果(允许/拒绝/转发)。遇到 Pod 访问不通的问题时,这个命令能很快定位是策略拒绝还是路由丢失。比如你看到包被 DROP 且 Reason 是 Policy denied,那问题就聚焦到 NetworkPolicy 上,直接去查 CiliumNetworkPolicy 的规则即可。
5. 常见问题与排查技巧实录
5.1 高频问题速查表
我根据这些年踩过的坑和平时答疑遇到过的问题,整理了一份高频问题速查表,按故障表现分类,方便大家直接索引:
| 故障表现 | 常见原因 | 定位命令 | 解决方案 |
|---|---|---|---|
| Pod 一直 ContainerCreating | CNI 插件未安装 / 节点未分配到子网 / CNI 配置文件损坏 | kubectl describe pod; journalctl -u kubelet | 检查 CNI DaemonSet 是否 Running;确认--allocate-node-cidrs; 重新执行 CNI 安装 |
| 跨节点 Pod 互 ping 不通 | BGP 会话未建立 / VXLAN 隧道 MTU 不匹配 / 宿主机 iptables 误拦截 | calicoctl node status; ping -M do -s 1472 | 调整 MTU;检查 BGP 邻居状态;检查 FORWARD 链 |
| Pod 能访问集群内 Service,但访问不了外网 | natOutgoing 未配置 / 宿主机 IP 转发未开启 / 云安全组拦截 | sysctl net.ipv4.ip_forward; iptables -t nat -L | 开启net.ipv4.ip_forward=1; 调整 CNI nat 开关;检查云安全组 |
| Service 访问不通,但 Pod IP 通 | kube-proxy 的 iptables/IPVS 规则损坏 / Cilium kubeProxyReplacement 与 kube-proxy 冲突 | iptables -t nat -L -n; ipvsadm -L -n | 清理 kube-proxy 残留规则;确认是否启用 kubeProxyReplacement |
| 集群节点数超过 50 后网络时延明显上升 | BGP 全网互联或 Flannel 映射表膨胀 | calicoctl get nodes; netstat -anp | 配置 Route Reflector 或切换 Cilium 方案 |
5.2 三个最容易被忽略的坑
第一个坑是iptables 规则与 Docker 的网络冲突。很多人部署完 Kubernetes 后,发现所有 Pod 都启动不了,kubelet 日志里报 CNI 失败。我排查过几次,最后定位到是 Docker 自身创建的 DOCKER 链和 Kubernetes 的 KUBE 链冲突。解决办法是在 kubelet 启动参数里加上--network-plugin=cni,同时确保 iptables 的 FORWARD 链默认策略是 ACCEPT。这个问题在新版本里出现得少了,但如果你用老版本 Docker + Kubernetes,还是要注意。
第二个坑是修改 CNI 后旧路由残留。有人从 Flannel 切换到 Calico,直接把 Flannel 的 DaemonSet 删了,然后套用 Calico 的 manifests 部署,结果发现部分节点上 Pod 能通、部分节点上 Pod 时好时坏。这时候多半是节点上还残留着 Flannel 的 cni0 网桥、flannel.1 隧道设备和旧的 VXLAN 路由。切换 CNI 时,务必先清掉所有旧 CNI 的网卡、路由和配置,再部署新方案。
# 在每台节点上 ip link delete cni0 ip link delete flannel.1 rm -rf /etc/cni/net.d/*清理完之后再让 kubelet 重新触发 CNI 调用,才能干净地切换到新方案。
第三个坑是多个 CNI 混用导致的 Pod 网络命名空间错乱。我见过有人为了“增强网络能力”同时部署了 Calico 和 Flannel,然后创建 Pod 的时候 kubelet 会从/etc/cni/net.d/目录里拿第一个配置文件,结果索引顺序不稳定,导致部分 Pod 用了 Calico 网络、部分 Pod 用了 Flannel 网络,集群里直接炸了。CNI 规范规定一个节点只能使用一个插件链,共存是绝对不行的。如果你非要试多个方案,建议用 kubeadm 重置集群再重新初始化:
kubeadm reset rm -rf /var/lib/cni/ rm -rf /etc/cni/net.d/5.3 排障时最有用的三板斧
结合我的排障经验,再分享三个最值得先试的定位方法,它们能覆盖大多数网络问题。
第一招,从 Pod 视角出发逐步拆解。用kubectl exec进入 Pod,执行ip addr和ip route确认 Pod 的 IP 和路由配置是否符合预期。然后从 Pod 里 ping 同节点 Pod、跨节点 Pod、Service VIP、外部 IP,逐步缩小故障范围。这一步能很快速地区分问题是出在“Pod 的网卡配置”还是“节点的路由/隧道”上。
第二招,看 kubelet 日志。网络问题大概率会在 kubelet 日志里留下线索。尤其在 Pod 创建失败的时候,kubelet 日志里的 CNI 调用输出信息非常详细,会直接告诉你插件名称、配置文件、错误原因。这类日志的位置在 systemd 环境里是journalctl -u kubelet -f,在非 systemd 环境里是/var/log/kubelet.log,根据你的发行版选择合适的查看方式。
第三招,在宿主机上抓包。如果 Pod 内 ping 不通,问题大概率在数据路径上。在源节点上tcpdump -i cni0看 Pod 流量是否到达网桥,在tcpdump -i eth0看流量是否出宿主机,在对端节点上再看流量是否进来。哪一段出现丢包,问题就在哪一段。这个方法虽然朴素,但往往比空想高效得多。
6. 最后再分享一点选型的心得
从 Flannel 到 Calico 再到 Cilium,我每个方案都在生产环境里经历过完整的上线和排障周期。回头再看这些走过的路,最深的体会是:网络方案没有绝对的好坏,只有是否匹配当前的规模、团队能力和业务需求。我不建议社区里出现“某某方案已经过时”这种论调,每个方案都有它适合存在的场景,关键是你要清楚自己的位置。
如果你现在的集群很小、团队只有两三个人,尽管用 Flannel 或者云厂商默认的 CNI,把精力放在业务代码上远比纠结网络方案重要。等集群规模上来、对安全和可观测性有需求了,再迁移到 Calico 或 Cilium。迁移其实是比较成熟的操作了,Cilium 官方提供了从 Calico 迁移的文档,实际做下来也不会太惊险。核心是先跑通业务,然后在压力点出现时再升级方案,这样每一步都走得稳。
另外多说一句关于 Cilium 的小技巧。如果你已经决定要上 Cilium,我强烈建议从部署第一天就把 Hubble 的 UI 组件打开。虽然它对集群资源有一点额外的占用,但在排查东西向流量问题时能省下大量时间。我见过太多人跑着 Cilium 却从不用cilium monitor,遇到问题还是一头扎进局域网里抓包,那就浪费了这个方案本身的强大能力了。