系列导航
本系列从 Pod 网络连通入手,逐步展开到 K8s 网络全景。
① 概念 → ② Flannel → ③ Calico → ④ 流量路径 → ⑤ Cilium → ⑥ 对比 → ⑦ 排障 ‖ ⑧ 开发 → ⑨ 多网卡 → ⑩ AI 演进
| 顺序 | 文章 | 定位 |
|---|---|---|
| ① | 概念与入门 | 基础——主机网络、Docker 网络、CNI 标准、方案分类树 |
| ② | 本篇 - Flannel 详解 | CNI 实现——Overlay 封装(UDP/VXLAN/HostGW)与抓包 |
| ③ | Calico 详解 | CNI 实现——三层路由(BGP/IPIP)+ NetworkPolicy |
| ④ | 流量路径全解析 | 全貌——Pod/Service/Ingress/Egress,揭示 CNI 的边界 |
| ⑤ | Cilium 详解 | 超越 CNI——eBPF 统一 Pod + Service + L7 + Hubble |
| ⑥ | 插件对比与选型 | 选型——5 插件横向对比 + 决策树 |
| ⑦ | 排障思路与常用命令 | 运维——工具链 + 场景排查 + 性能 |
| — | 核心路径 ↑ | 扩展展望 ↓ |
| ⑧ | CNI 插件开发指南 | 扩展——基于 CNI 规范开发自定义插件 |
| ⑨ | 多网卡方案详解 | 进阶——Multus + SR-IOV/ipvlan 多网口实战 |
| ⑩ | AI 时代网络演进 | 展望——GPU 网络、eBPF 加速、未来方向 |
Flannel 由 CoreOS 开发,是 k8s 最成熟的开源 CNI 插件之一,通过一个三层的 IPv4 Overlay 网络运行——跨集群每个节点的大型内部网络,每个节点分配一个子网。
1. Flannel架构
▸ 组件
flanneld —— 每个节点上的核心进程,作为 DaemonSet 部署,负责子网划分、子网租赁管理,使用 Etcd 或 K8S API 存储状态。
cni0 —— Linux 网桥,取代 docker0。每个 Pod 的 veth pair 对端插入 cni0,降级为网桥端口。同节点 Pod 间通信走 CAM 表二层转发。
后端(Backend) —— 负责跨节点报文的具体转发方式。Flannel 提供三种后端:UDP(用户态 Overlay)、VXLAN(内核态 Overlay)、Host-GW(路由模式)。
▸ 原理
每个节点从全局子网池中租赁一个子网段(如 10.244.1.0/24),节点内 Pod 从该段分配 IP。跨节点 Pod 通信时,数据路径为:
Pod eth0 → veth pair → cni0 网桥 → 宿主机路由 → 后端封装/转发 → 对端宿主机 → cni0 → veth pair → 目标 Pod
与 Calico 的关键区别:Flannel 的 veth pair 对端必须插入 cni0 网桥,数据先经过二层 CAM 转发后才能进入宿主机协议栈。这种"网桥+路由"双层结构在跨主机时引入了额外的封装/解封复杂度。
2. Flannel后端模式
Flannel 解决的核心问题:跨节点 Pod 发送 IP 包时,原始 Pod 报文如何到达对端节点? 三种后端给出三种答案:
| 模式 | 分类 | 通路 | 封装位置 | 性能 | 前提条件 |
|---|---|---|---|---|---|
| UDP | Overlay(L3) | flanneld 进程间 UDP 通信 + TUN 设备 flannel0 | 用户态 | 最差(3次状态切换) | 无 |
| VXLAN | Overlay(L2) | 内核态 VTEP (flannel.1) + VXLAN 隧道 + FDB 转发 | 内核态 | 良好(~50字节开销) | 内核支持 VXLAN |
| Host-GW | Underlay(路由) | 静态路由,flanneld 直接修改宿主机路由表 | 无封装 | 最高(裸机性能) | 所有节点 L2 互通 |
Flannel 的三种后端按数据路径分为两类:Overlay 封装(UDP、VXLAN)和 Underlay 路由(Host-GW)。
2.1 Overlay: UDP 模式
通路:flanneld 进程间 UDP 通信 + TUN 设备 flannel0——各节点 flanneld 组成 Overlay 网桥,用户态封包解包,实现最简单,性能最差。
封装结构:|HostMAC|HostIP|UDP|内层IP|Data|,IP 头(20B)+ UDP 头(8B)= 28B 开销。
2.1.1 组件概念
flannel0:TUN 设备
flannel0 是一个 TUN 设备(三层虚拟网络设备),在内核和应用程序(flanneld 进程)之间传递 IP 包:
| 方向 | 流向 | 行为 |
|---|---|---|
| 宿主机 → flannel0 | 内核态 → 用户态 | 内核态 IP 包交给创建该设备的应用程序(flanneld 进程) |
| flanneld → flannel0 | 用户态 → 内核态 | flanneld 进程写入的 IP 包,出现在宿主机网络栈,按路由表流转 |
2.1.2 封装流程
五段流程
| |
1. S.容器 → S.docker0
1.1 容器内,默认路由,即 docker0 的 IP == 网关 IP,通过容器 eth0 发出,目标 IP = 目的容器 IP,目标 MAC = docker0.MAC;
1.2 容器 eth0 → veth-pair → docker0 端口 vethxx;
1.3 docker0 发现目标 MAC == 自身 MAC,发往 S.宿主机网络栈。
2. S.docker0 → S.flanneld 进程
2.1 到达 S.宿主机网络栈后,根据路由(flanneld 维护),目标 IP(目的容器 IP) ∈ Overlay 网段,发往 flannel0 设备(TUN);
2.2 根据 flannel0 设备的 TUN 特性,IP 包进入 flanneld 进程(注意,内核此时会剥离原始报文的 L2 头,将 IP 报文发给 flannel0)。
3. S.flanneld → D.flanneld
3.1 S.flanneld 进程,根据目的容器 IP 结合 Etcd 中节点-子网对应关系,获得目的宿主机 IP,目的端口 = 8285;
3.2 经宿主机网络,到达目的宿主机 UDP 进程——D.flanneld 进程。(注意:进入目标主机内核后,会重新 ARP 缓存或 ARP 请求,根据目标 IP 取得目标 MAC,再封装二层头。此行为属于步骤 4.3。)
4. D.flanneld → D.docker0
4.1 D.flanneld 进程收到数据时,D.宿主机网络栈已剥离 L1-L4 协议头,得到原始报文(L3 层报文,目标 IP = 目的容器 IP。跟 S.flanneld 收到的一模一样);
4.2 D.flanneld 进程,将 IP 包发往 D.flannel0,IP 报文进入 D.宿主机网络栈;
4.3 D.宿主机网络栈,根据路由(flanneld 维护),目标 IP(目的容器 IP) ∈ 当前节点子网,出设备 = D.docker0。
docker0 是一个网桥(二层设备),要通过它发送,必须封装以太网帧头。D.宿主机网络栈封装 L2 帧头,通过 ARP 请求或 ARP 缓存获取目的容器 MAC,并封装。
5. D.docker0 → D.容器
5.1 docker0,根据目的容器 MAC,查 CAM 表,转发至指定端口 veth;
5.2 端口 veth → veth-pair → 进入目的容器 eth0。
2.1.3 性能分析
为什么性能差?三次内核态↔用户态切换
发送端:
第一次:容器进程 → 容器 eth0 用户态 → 内核态
之后 eth0→vethxx→docker0→flannel0 一直在内核态
第二次:flannel0 → flanneld 进程 内核态 → 用户态 ← TUN 特性
第三次:flanneld 进程 → 宿主机 eth0 用户态 → 内核态2.2 Overlay: VXLAN 模式
通路:内核态 VTEP 设备 flannel.1 + VXLAN 隧道 + FDB 转发表,封装/解包全在内核中完成。
封包格式:|HostMAC|HostIP|UDP|VXLAN|内层MAC|内层IP|Data|,总计 ~50B 开销(VTEP 帧头 14 + VXLAN 头 8 + UDP 头 8 + IP 头 20)。
VXLAN (Virtual Extensible LAN) 是 Linux 内核原生支持的 Overlay 协议,采用内核态标准协议,所有封装/解包在内核中完成,实际传输速度比 UDP 模式快许多。flanneld 服务在每个节点监听端口 8472(VXLAN),其他节点用随机端口连接。
VNI (VXLAN Network Identifier): Flannel 默认使用 VNI 1。同一 VNI 内的节点属于同一个 L2 广播域。
2.2.1 组件概念
flannel.1 = VTEP
flannel.1 是一个 VTEP(VXLAN Tunnel End Point)设备,工作在内核态,负责 VXLAN 封装和解封。
与普通网桥(cni0 / docker0)的区别:
| 特性 | 普通网桥(cni0) | VTEP(flannel.1) |
|---|---|---|
| 转发表 | CAM 表(端口 ↔ MAC) | FDB 表(远端 VTEP MAC ↔ 远端宿主机 IP) |
| 是否记录 IP | 否 | 是,FDB 记录远端宿主机 IP |
| 封装能力 | 无封装,纯二层转发 | 内核态 VXLAN 封装/解封 |
三层表
flanneld 进程在每个节点预置三类表项,构成完整的数据平面:
| 表 | 内容 | 作用 | 查看命令 |
|---|---|---|---|
| 路由表 | 远端子网 → flannel.1,网关 = 远端 VTEP IP | 将跨节点 Pod 流量引向 VTEP 设备 | ip route |
| ARP 表 | 远端 VTEP IP → 远端 VTEP MAC | VTEP 间邻居发现,回答"对端 VTEP 的 MAC 是什么" | ip neigh show dev flannel.1 |
| FDB 表 | 远端 VTEP MAC → 远端宿主机物理 IP | 回答"对端 VTEP 在哪台宿主机上" | bridge fdb show dev flannel.1 |
| |
flannel.1 虚拟网卡),无需像 UDP 模式那样经过用户态 flanneld 进程,性能比 UDP 方案提升约 50%。2.2.2 封装流程
完整封装流程
以源容器 → 目的容器为例:
步骤1:源容器发出 IP 包 S.容器 eth0 → veth pair → S.宿主机 cni0 网桥 → 路由表匹配:D.容器IP ∈ 远端子网 步骤2:路由引向 VTEP 路由规则(flanneld 预置): 10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink → IP 包发往 S.flannel.1,下一跳 = 远端 VTEP IP 步骤3:VTEP 内层二层封装 flannel.1 查 ARP 表:远端 VTEP IP → 远端 VTEP MAC 封装内层以太网帧头:S.VTEP.MAC + D.VTEP.MAC 步骤4:VTEP 封 VXLAN 头 添加 VNI = 1,形成 VXLAN 报文 步骤5:VTEP 封外层 UDP+IP 头 查 FDB 表:D.VTEP.MAC → D.宿主机.IP 源 IP = S.宿主机.IP, 目标 IP = D.宿主机.IP 目标端口 = 8472 步骤6:宿主机二层发出 查路由表/ARP,封装外层以太网帧(D.宿主机.MAC),从 eth0 发出 步骤7:目的宿主机解封装 D.宿主机 eth0 → 内核依次解外层 MAC/IP/UDP → 识别 VXLAN 端口 8472 → 根据 VNI=1 交给 flannel.1 步骤8:VTEP 剥离内层头 flannel.1 剥离 VTEP 内层 MAC 头、VXLAN 头,还原原始 IP 包 步骤9:路由到目标容器 查路由表,目标 IP ∈ 本机 Pod 子网 → 走 cni0 → ARP 获取容器 MAC → 封装二层头 → cni0 CAM 转发 → veth pair → D.容器 eth0
2.2.3 抓包验证
抓包验证
如下面一条抓包数据所示——源 192.168.1.1 节点的 pod [10.42.1.2] ping 目的 192.168.1.2 节点的 pod [10.42.2.6],抓源主机 enp0s3 设备的 UDP 包:
| |
192.168.1.1.39338 > 192.168.1.2.8472:外层 UDP 包,源端口 39338,目标端口 8472(VXLAN 协议端口)f2:60:ca:96:7f:1f > 4a:02:36:26:f9:c1:外层以太网帧,源/目标 MAC 为两台宿主机enp0s3网卡的 MAC10.42.1.2.10384 > 10.42.2.6.443:内层 TCP 包,这是 Pod 之间真实的通信——源 IP10.42.1.2,目标 IP10.42.2.6
2.3 Underlay: Host-GW 模式
flanneld 直接将 Pod 子网路由写入宿主机路由表,无任何封装,性能等于裸机网络。2.3.1 组件概念
与前两种 Overlay 模式不同,Host-GW 采用主机路由方案——既然跨节点不通是因为节点间没有路由信息,而 Flannel 知道这些信息,就直接把这个信息告诉网络上的节点。
Flannel 通过在各个节点上的 Agent 进程,将容器网络的路由信息刷到主机路由表上,所有主机关联整个容器网络的路由数据。
Host-Gateway 没有像 Overlay 中额外的装包解包操作,完全是普通的网络路由机制,效率与虚拟机直连通信相差无几。
然而,由于 Flannel 只能修改各个主机的路由表,一旦主机隔了其他路由设备(如三层路由器),数据包会在路由设备上被丢弃。因此 Host-Gateway 只能用于二层直接可达的网络。
2.3.2 主要流程
工作原理: 1. 每个节点的 Pod 子网在 Etcd 注册 2. flanneld 在每个节点的主机路由表中增加到其他节点子网的静态路由 3. 路由的下一跳就是目标节点的主机 IP 4. Pod 跨节点流量直接按主机路由转发,无任何封装
2.3.3 路由验证
假设两个节点:
| |
flanneld 在节点1 的路由表中写入:
| |
flanneld 在节点2 的路由表中写入:
| |
节点1 的 Pod(10.244.1.5)访问节点2 的 Pod(10.244.2.8)时:匹配 10.244.2.0/24 via 192.168.1.2 → 下一跳 = 192.168.1.2 → 查 ARP 获取节点2 的 MAC → 封装 L2 帧从 eth0 发出。无任何隧道封装,数据包 = 原始 Pod IP 包。
3. 流量加密
默认情况下,封装的流量不加密。Flannel 提供两种加密方案:
- IPSec: 使用 strongSwan 在节点间建立加密 IPSec 隧道,实验性后端
- WireGuard: 比 strongSwan 更快的替代方案
4. 总结
| 维度 | UDP | VXLAN | Host-GW |
|---|---|---|---|
| 封装开销 | 28B | ~50B | 0 |
| 封装位置 | 用户态 | 内核态 | 无封装 |
| 性能 | 最差 | 良好 | 最高 |
| 部署要求 | 无特殊要求 | 内核支持 VXLAN | 节点 L2 互通 |
| 适用场景 | 仅演示原理 | 公有云、虚拟化环境,生产通用 | 同 L2 广播域的自建机房 |
| 控制方式 | flanneld 进程转发 | VTEP 内核隧道 | 路由表 |
VXLAN 是 Flannel 的默认和推荐模式。UDP 模式仅作演示理解原理,Host-GW 在不跨网段的场景下性能最优。