公司动态
【Kubernetes从入门到精通】第44篇:Flannel——最简单的K8s网络方案,但别小看它
上一篇【第43篇】K8s网络模型——每个Pod一个独立IP背后的大智慧下一篇【第45篇】Calico——BGP驱动的企业级K8s网络摘要还记得上一篇吗咱们把K8s网络的宪法给学了——所有Pod直连、不搞NAT。但光有宪法不行啊你得有执行机构。Flannel就是K8s网络界最老牌的执行者——它的设计哲学可以用一句话概括“我就干一件事——让不同Node上的Pod能通信其他花里胡哨的我不管。”Flannel是CoreOS团队在2014年发布的比K8s还早。它几乎跟K8s同步成长是大多数K8s新手第一次接触的CNI插件。为什么因为它简单到令人发指——部署只需要一个DaemonSet 一个ConfigMap配置一个Pod CIDR就完事了。但这不代表Flannel是廉价的入门玩具。今天咱们把它的三种后端模式(VXLAN、host-gw、UDP)彻底拆开看看——VXLAN到底多封了多少字节host-gw为什么快DirectRouting又是怎么在VXLAN和host-gw之间左右横跳的读完这篇你对所有Overlay网络的理解都会上一个台阶。一、Flannel架构全景——“一个子网一张表”1.1 Flannel到底做了什么Flannel的核心理念就四个字路由表同步。它不搞复杂的路由协议也不搞内核黑科技就做一件极其朴素的事【Flannel核心工作流程——只管路由表】 ┌──────────────────────────────────────────────────────────────┐ │ K8s API Server │ │ ┌────────────────────────────┐ │ │ │ Node 1: subnet 10.244.1.0/24│ │ │ │ Node 2: subnet 10.244.2.0/24│ │ │ │ Node 3: subnet 10.244.3.0/24│ ← Node注解存储 │ │ └────────────────────────────┘ │ └──────────────┬──────────────┬──────────────┘ │ │ │ │ ┌────────────▼───┐ ┌──────▼──────────┐ ┌──────────────────┐ │ │ flanneld │ │ flanneld │ │ flanneld │ │ │ (Node 1) │ │ (Node 2) │ │ (Node 3) │ │ │ │ │ │ │ │ │ │ 监控Node注解 │ │ 监控Node注解 │ │ 监控Node注解 │ │ │ 获取全集群路由 │ │ 获取全集群路由 │ │ 获取全集群路由 │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ ▼ │ │ ▼ │ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ │ │ 本地路由表 │ │ │ │ 本地路由表 │ │ │ │ 本地路由表 │ │ │ │ │10.244.2.0/24│ │ │ │10.244.1.0/24│ │ │ │10.244.1.0/24│ │ │ │ │ → Node2 │ │ │ │ → Node1 │ │ │ │ → Node1 │ │ │ │ │10.244.3.0/24│ │ │ │10.244.3.0/24│ │ │ │10.244.2.0/24│ │ │ │ │ → Node3 │ │ │ │ → Node3 │ │ │ │ → Node2 │ │ │ │ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────────┘ └─────────────────┘ └──────────────────┘ │要点Flannel不需要独立的控制面数据库Calico的BIRD需要BGP PeerCilium需要etcd。它直接利用K8s API Server来存储和同步路由信息——每个Node的flanneld守护进程监听Node注解的变化发现自己拿到了哪个子网、别人拿到了哪个子网然后把路由规则写到本地内核路由表里。干净利落。1.2 Flannel的整体数据流【Flannel数据面架构——veth→网桥→flanneld→网卡】 ┌──────────────────────────────── Pod A ────────────────────────┐ │ App (10.244.1.5) │ │ │ │ │ │ 目的地: 10.244.2.6 │ │ ▼ │ │ eth0 (veth pair一端) │ └────────────────────────────────────────────────────────────────┘ │ veth pair ▼ ┌─────────────────── Host Network Namespace ──────────────────────┐ │ vethXXXX (veth pair另一端, 插在网桥上) │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ cni0 │ ← Linux Bridge (VXLAN/host-gw模式的默认网桥) │ │ │ (bridge) │ 路由表: 10.244.2.0/24 → Node2的IP │ │ └────┬─────┘ │ │ │ │ │ │ 查路由表: 10.244.2.0/24 走哪个设备 │ │ │ │ │ ├── VXLAN模式: 走 flannel.1 (VTEP设备) │ │ ├── host-gw模式: 直接走 eth0 (物理网卡) │ │ └── UDP模式: 走 flannel0 (TUN设备) │ │ │ │ │ ▼ │ │ ┌──────────┐ ┌──────────┐ │ │ │ flannel.1 │ 或 │ eth0 │ 或 ┌──────────┐ │ │ │ (VTEP) │ │ (物理网卡)│ │ flannel0 │ │ │ └─────┬─────┘ └────┬─────┘ │ (TUN) │ │ │ │ │ └────┬─────┘ │ │ │ VXLAN封装 │ 直接路由 │ UDP封装 │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ eth0 (物理网卡) │ │ │ │ 发送到目标Node │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘二、三种后端的底层原理——“VXLAN、host-gw、UDP到底谁行”2.1 三种后端全景对比在深入原理之前先把三者的关键差异摆出来维度VXLANhost-gwUDP封装层级Linux内核原生VXLAN无封装用户态UDP封装性能高内核级极高零封装低用户态拷贝延迟~0.1ms额外延迟接近直连~1-2ms额外延迟带宽损耗~5%VXLAN头50字节0%~10%-20%最大MTU1450字节(默认)1500字节1472字节网络要求L3可达即可L2相邻同网段L3可达即可适用场景公有云/跨机架自建机房/同网段历史遗留/内核不支持VXLAN加密支持❌❌❌2.2 VXLAN模式——“套娃传信”VXLAN是Flannel的默认后端也是生产环境最常用的模式。它的核心思想是Mac-in-UDP封装——把原始的二层以太网帧整个包在一个UDP包里面。【VXLAN封包结构——一封信套一个信封】 原始数据包Pod A → Pod B: ┌──────────────────────────────────────────────────────────┐ │ Src IP: 10.244.1.5 │ Dst IP: 10.244.2.6 │ TCP Payload │ └──────────────────────────────────────────────────────────┘ VXLAN封装后的数据包Node1 → Node2: ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ 外层IP头 │ 外层UDP头 │ VXLAN头 │ 原始L2帧 │ │(20字节) │(8字节) │(8字节) │(内层IP头20字节 内层TCP头20字节 Payload) │ │Src:10.0.0.1│DstPort: │VNI:1 │Src:10.244.1.5 Dst:10.244.2.6 │ │Dst:10.0.0.2│8472 │Flags:0x08 │ │ └─────────────────────────────────────────────────────────────────────────────────────┘ │←────────── 总共增加了50字节 ──────────→│←────────── 原始数据 ───────────────────────→│ 封包字节数明细: - 外层IP头: 20字节标准IPv4头 - 外层UDP头: 8字节 - VXLAN头: 8字节VNI 24bit reserved - 外层以太网头: 14字节 总计: 50字节的封装开销要点VXLAN多出来的50字节可不是小事标准以太网MTU是1500字节减去50字节的VXLAN封装实际可用MTU只有1450字节。如果你不在Pod内调整MTU大包会被分片性能直接打折。Flannel默认会通过CNI配置给Pod设置合适的MTU但如果你自己调整了网络配置别忘了这个坑。VXLAN流量路径实战演练# 1. 查看VXLAN设备 flannel.1 (VTEP)ip-dlinkshow flannel.1# 输出:# 5: flannel.1: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1450 qdisc noqueue state UNKNOWN# link/ether 6e:5a:3b:1c:8f:2d brd ff:ff:ff:ff:ff:ff promiscuity 0# vxlan id 1 local 10.0.0.1 dev eth0 srcport 0 0 dstport 8472 nolearning# 2. 查看fdb表Forwarding Database——MAC→IP的映射bridge fdb show dev flannel.1# 输出:# 6e:5a:3b:1c:8f:2d vlan 1 master flannel.1 permanent# 4a:7b:2c:9d:3e:5f dst 10.0.0.2 self permanent# ↑ flannel.1知道要发给MAC地址4a:7b:2c:9d:3e:5f的包# 目标节点是10.0.0.2(Node2的物理IP)# 3. 查看ARP表IP→MAC的映射ipneigh show dev flannel.1# 输出:# 10.244.2.0 lladdr 4a:7b:2c:9d:3e:5f PERMANENT# ↑ flannel.1知道10.244.2.0/24子网的网关# MAC地址是4a:7b:2c:9d:3e:5f# 4. 查看路由表iproute show|grepflannel# 输出:# 10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink# 10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink一个VXLAN包从Node1到Node2的完整旅程【VXLAN包转发全路径——六步之旅】 Pod A (10.244.1.5) Pod B (10.244.2.6) │ │ │ ①发出: dst10.244.2.6 │ ▼ │ eth0pA │ ⑧到达Pod B │ │ │ veth pair │ ▼ │ vethXXXXpN1 │ ▲ │ │ │ ▼ │ │ cni0 (bridge) ──查路由表──► dst10.244.2.0/24 │ │ │ → 走 flannel.1 │ │ ▼ │ │ flannel.1 (VTEP) │ │ │ ②查ARP: 10.244.2.0 → MAC_a │ │ │ ③查FDB: MAC_a → dst10.0.0.2 │ │ │ ④VXLAN封装: 外层 dst10.0.0.2:8472 │ │ │ │ │ ▼ │ │ eth0 (物理网卡) ──────────────────────┐ │ │ ⑤外层包发送到Node2 │ │ │ │ │ │ ┌─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Node2 eth0 (物理网卡) │ │ │ ⑥收到VXLAN包 │ │ ▼ │ │ flannel.1 (VTEP) │ │ │ ⑦内核解封装: 去掉VXLAN/UDP/IP外层 │ │ │ 露出原始包: dst10.244.2.6 │ │ ▼ │ │ cni0 → vethYYYY → eth0pB ──────────────────────────┘要点注意第②③步——VXLAN需要两次查表。第一次查ARP表把Pod子网IP映射到VTEP的MAC地址第二次查FDB表把MAC地址映射到目标Node的物理IP。这两张表都是flanneld自动维护的你基本不用干预。但如果你遇到VXLAN通信故障排查思路就是先查路由表有没有10.244.2.0/24的记录再查ARP表有没有下一跳的MAC最后查FDB表有没有MAC到Node IP的映射。2.3 host-gw模式——“不封包直接走”host-gw是Flannel三种后端中性能最好的模式。它的原理简单到令人发指不给数据包套任何壳直接在本机路由表里写入目标Node的IP作为网关。【host-gw数据流——路由表直达零封装】 Pod A (10.244.1.5) Pod B (10.244.2.6) │ │ ▼ ▲ cni0 bridge │ │ 查路由表: │ │ 10.244.2.0/24 via 10.0.0.2 dev eth0 │ │ 10.244.3.0/24 via 10.0.0.3 dev eth0 │ ▼ │ eth0 ──────────── 直发 ──────────────► eth0 │ (10.0.0.1) 原始包 (10.0.0.2) │ │ dst10.244.2.6 │ │ src10.244.1.5 │ ▼ │ 没有任何封装直接路由 │ │ ┌────────────────────────────────────────────────┘ │ ▼ Node2 eth0 收到 → 查本地路由 → cni0 → Pod B查看host-gw模式的路由表# host-gw模式下路由表特别干净iproute show# 输出:# 10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1# 10.244.2.0/24 via 10.0.0.2 dev eth0 ← 直接走物理网卡# 10.244.3.0/24 via 10.0.0.3 dev eth0# 没有任何隧道设备flannel.1 不存在host-gw的优缺点非常鲜明优点缺点零封装性能天花板要求所有Node在同一个二层网络路由表清晰排查简单跨网段/跨数据中心不行没有MTU问题路由表条目随Node数量线性增长CPU开销极低不支持加密要点host-gw最大的限制不是Flannel的锅而是物理网络的锅。K8s如果要跨地域部署不同机房之间的Node通常不在同一个二层网络里那host-gw就抓瞎了。这时候VXLAN又杀回来了——VXLAN只需要L3可达不要求L2同网段。所以这两种模式不是谁比谁好是各有各的适用场景。2.4 UDP模式——“历史的眼泪”UDP模式是Flannel最早的后端现在已经基本被VXLAN取代了。它的工作原理是【UDP模式数据流——用户态转发慢得离谱】 Pod A ──► cni0 ──► flannel0 (TUN设备) ──► flanneld (用户态进程) │ │ │ 用户态读包 │ 用户态封装UDP │ 用户态写回内核 │ │ ▼ ▼ eth0 ←── 内核 ←── 用户态 ──→ eth0 (三次内核↔用户态切换)我说慢得离谱不是夸张。VXLAN模式是内核直接处理封装包不离开内核空间。但UDP模式是用户态进程处理封装——内核收到包→交给用户态flanneld→flanneld封装→写回内核→发出去。一个包要经过三次内核↔用户态的上下文切换。【三种模式的性能对比——一条10Gbps链路的实际吞吐量】 ┌────────────────────────────────────────────────────────────────┐ │ │ │ host-gw: ████████████████████████████ 9.8 Gbps │ │ (零开销, 只有物理网卡和内核协议栈的开销) │ │ │ │ VXLAN: ██████████████████████████ 9.3 Gbps │ │ (~5%损失, 内核VXLAN处理效率很高) │ │ │ │ UDP: ████████████ 4.2 Gbps │ │ (~57%损失, 用户态拷贝上下文切换灾难) │ │ │ │ (以上数据为100字节小包测试大包差距会缩小但仍显著) │ └────────────────────────────────────────────────────────────────┘UDP模式今天唯一的应用场景是内核版本太老不支持VXLAN的内核模块Linux 3.7之前。如果你用的K8s节点内核是3.10完全不用考虑UDP模式。三、DirectRouting——“VXLAN的贤者模式”3.1 问题的提出VXLAN虽然通用性无敌不要求L2同网段、内核级高性能但它有一个让人不舒服的地方即使两个Pod在同一个网段的Node上VXLAN照样给你封包。比如Node1(10.0.0.1)和Node2(10.0.0.2)在同一个二层网络里物理网卡直接就能通信但VXLAN模式下的数据包还是要经过三层封装VXLAN头UDP头外层IP头白白浪费性能。Flannel从v0.12.0开始引入了DirectRouting来解决这个问题。它的思路很巧妙【DirectRouting工作原理——能直连就不走隧道】 ┌──────────────────────────────────────────────────────────────┐ │ flanneld 判断逻辑 │ │ │ │ 收到新的路由信息: │ │ 10.244.5.0/24 → Node5 (IP: 10.0.0.5) │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ① Node5的IP和我的IP在同一个子网吗 │ │ │ │ ┌─────────┬──────────┐ │ │ │ │ │ YES │ NO │ │ │ │ │ └────┬────┘ └───┬──┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ ┌────────────┐ ┌────────────┐ │ │ │ │ │ host-gw │ │ VXLAN隧道 │ │ │ │ │ │ 直接路由 │ │ 封包转发 │ │ │ │ │ │ via eth0 │ │ via flannel.1│ │ │ │ │ └────────────┘ └────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘3.2 DirectRouting配置# flannel-configmap.yamlapiVersion:v1kind:ConfigMapmetadata:name:kube-flannel-cfgnamespace:kube-flannellabels:tier:nodeapp:flanneldata:net-conf.json:|{ Network: 10.244.0.0/16, Backend: { Type: vxlan, DirectRouting: true # ← 就加这一行 } }部署后查看效果# 开启DirectRouting后同网段Node的路由走eth0iproute show|grep10.244# 10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1# 10.244.2.0/24 via 10.0.0.2 dev eth0 ← 直连# 10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink ← 跨网段走VXLAN# 10.244.5.0/24 via 10.244.5.0 dev flannel.1 onlink ← 跨网段走VXLAN# 注意看10.244.2.0/24走的是 eth0直接路由# 而10.244.3.0/24走的是 flannel.1VXLAN隧道要点DirectRouting不是一种新的后端模式它是VXLAN模式的一个优化选项。它不会改变Flannel的整体架构只是在flanneld写入路由表时多了一次判断目标Node跟我在同一个二层网络吗是就给eth0的路由不是就给flannel.1的路由。这种按需直连的策略让你既能享受VXLAN的跨网络通用性又不损失同网段通信的性能。3.3 DirectRouting的适用前提DirectRouting要生效需要满足一个条件条件: 两个Node之间必须能通过物理IP直接通信不需要NAT/隧道 Node1: 10.0.0.1/24 Node2: 10.0.0.2/24 ✅ 同一个子网 → DirectRouting生效 Node1: 10.0.0.1/24 Node2: 10.0.1.2/24 ❌ 不同子网 → DirectRouting不生效走VXLAN 除非有路由能到达 Node1: 10.0.0.1 (私有IP) Node2: 203.0.113.5 (公网IP) ❌ 公网通信 → DirectRouting不生效四、Flannel的部署与配置——“一条命令搞定”4.1 标准部署Flannel的部署跟其他CNI插件一样简单# 方式1: 直接apply官方YAMLkubectl apply-fhttps://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml# 方式2: kubeadm初始化时指定kubeadm init --pod-network-cidr10.244.0.0/16# 方式3: 使用Helmhelm repoaddflannel https://flannel-io.github.io/flannel/ helminstallflannel flannel/flannel\--setpodCidr10.244.0.0/16\--setbackendvxlan4.2 自定义配置Flannel的所有行为由一个ConfigMap控制apiVersion:v1kind:ConfigMapmetadata:name:kube-flannel-cfgnamespace:kube-flanneldata:cni-conf.json:|{ name: cbr0, cniVersion: 0.3.1, plugins: [ { type: flannel, delegate: { hairpinMode: true, isDefaultGateway: true } }, { type: portmap, capabilities: { portMappings: true } } ] }net-conf.json:|{ Network: 10.244.0.0/16, # Pod CIDR SubnetLen: 24, # 每个Node的子网掩码 SubnetMin: 10.244.1.0, # 子网起始可选 SubnetMax: 10.244.254.0, # 子网结束可选 Backend: { Type: vxlan, # 后端类型: vxlan/host-gw/udp VNI: 1, # VXLAN网络标识符 Port: 8472, # VXLAN端口 DirectRouting: true # 开启DirectRouting } }要点SubnetLen这个参数容易被忽略但很重要。默认是24意味着每个Node分一个/24子网最多254个Pod。如果你一个Node上要跑上千个Pod比如做批处理记得调成更小的掩码比如SubnetLen: 22每个Node最多1022个Pod。但同时你的Network整体Pod CIDR也得够大。4.3 验证Flannel是否正常工作# 1. 检查flannel Pod是否运行kubectl get pods-nkube-flannel# NAME READY STATUS RESTARTS AGE# kube-flannel-ds-abcde 1/1 Running 0 10m# kube-flannel-ds-fghij 1/1 Running 0 10m# kube-flannel-ds-klmno 1/1 Running 0 10m# 2. 查看每个Node分配的子网kubectl get nodes-ojsonpath{range .items[*]}{.metadata.name}{\t}{.spec.podCIDR}{\n}{end}# node1 10.244.0.0/24# node2 10.244.1.0/24# node3 10.244.2.0/24# 3. 检查flannel.1设备是否存在VXLAN模式iplinkshow flannel.1# 5: flannel.1: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1450 qdisc noqueue state UNKNOWN# 4. 检查路由表iproute show|grepflannel# 10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink# 10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink# 5. 跨Node Pod通信测试最关键的一步# 在node1上创建一个测试Podkubectl run test-pod--imagebusybox--rm-it--restartNever --sh# 进入Pod后:wget-qO- http://10.244.2.5:80# 访问另一个Node上的Pod# 有响应 → Flannel工作正常五、Flannel的局限与替代——“什么时候该换掉它”5.1 Flannel不擅长的事Flannel的设计哲学是只做一件事所以以下东西它统统不支持需求Flannel支持吗替代方案NetworkPolicy网络策略❌ 不支持Calico / Cilium网络加密❌ 不支持Calico(WireGuard) / CiliumL7流量管控❌ 不支持Cilium / IstioIPAM精细化控制❌ 每Node固定子网Calico(IP池) / Cilium多集群互联❌ 不支持Submariner / Cilium Cluster Mesh可观测性/流量监控❌ 不支持Cilium(Hubble)5.2 Flannel vs Calico 快速选型【Flannel vs Calico——选择困难症救星】 你的场景是... ┌─────────────────────────────────────────────────┐ │ 我就想Pod能通别的不管 │ │ → Flannel (VXLAN) │ │ 简单、稳定、够用 │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ 我要NetworkPolicy要隔离 │ │ → Calico │ │ 或者: Flannel Calico(for policy only) │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ 我要高性能我们是自建机房L2网络 │ │ → Flannel(host-gw) 或 Calico(BGP Direct) │ │ host-gw最简单BGP更灵活但部署复杂 │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ 我要加密、要可观测性、要L7策略 │ │ → Cilium │ │ 别为难Flannel了 │ └─────────────────────────────────────────────────┘要点很多人问我Flannel和Calico能不能一起用——可以但不推荐。Flannel负责联通Calico只负责NetworkPolicy这是一个常见的组合Canal项目就是这么来的。但实际上现在Calico自己的VXLAN模式也足够好用了没必要同时维护两个CNI插件增加排查难度。本篇小结Flannel的核心竞争力就一个字——简。它不用etcd、不需要BGP、不需要了解内核eBPF就靠一个DaemonSet一个ConfigMap把K8s网络的宪法所有Pod直连不通NAT给落地了。VXLAN是它最强的武器——给Pod包套上一层UDP壳让它能突破物理网络的L2限制跨网段、跨机房都能通。DirectRouting则是个聪明的优化——在同一网段内自动切换成host-gw模式让你鱼和熊掌兼得。但Flannel的简洁也是它的天花板。它不支持NetworkPolicy、不支持加密、不支持多集群——这些高级功能需要下一篇文章的主角Calico来填补。Flannel是你入门K8s网络的最佳伙伴但不是你的最终归宿——当你需要更精细的网络控制时就该说再见了。上一篇【第43篇】K8s网络模型——每个Pod一个独立IP背后的大智慧下一篇【第45篇】Calico——BGP驱动的企业级K8s网络