首页 / 资讯中心 / 文章详情

一文搞懂flannel与calico:容器网络插件选型与踩坑指南

一文搞懂flannel与calico:容器网络插件选型与踩坑指南 ★ FEATURED ARTICLE
我参与维护的集群从十几个节点到上百个节点都有每次新集群开工总绕不开一个话题网络插件到底选flannel还是calico。这两者常被放在一起比较但实际用起来会发现它们的底层思路、性能表现、排错方式完全不是一回事。这篇文章就把我这些年实际部署、切换、排查这两个插件时积累的东西整理出来尽量讲清楚它们的数据走向、适用场景和真实踩过的坑。1. 容器网络为什么要单独一个插件CNI的职责边界1.1 从一个Pod跨节点访问说起先想一个问题两个Pod分别在不同的宿主机上它们的IP都是私有的、可能还是重复的网段它们怎么互相找到对方Kubernetes本身不处理这个逻辑。它只负责编排容器但“给Pod分配IP、让不同节点上的Pod能互通、让宿主机和Pod能互通”这些事情是交给CNIContainer Network Interface插件去做的。Kubelet在启动Pod之前会调用一个二进制程序把网络的“脏活”外包出去。这个二进制程序通常放在/opt/cni/bin/目录下Kubelet会根据/etc/cni/net.d/里的配置文件决定调用哪一个。这个设计有点像把手机交给运营商K8s只负责“这台Pod要上网”至于用宽带、5G还是光纤它不关心。flannel和calico就是两种完全不同的“运营商”它们的计费方式、覆盖范围、出故障的表现都不一样。1.2 CNI插件要解决的四件事一个CNI插件最基本要干四件事IPAM分配IP每个Pod启动时插件要从某个预分配的CIDR里拿出一段IP给它不能冲突。创建网络设备Pod里要有一个eth0宿主机上要有一个对应的虚拟设备通常是veth对负责把Pod的流量引出来。打通宿主机到Pod的路径宿主机要能直连Pod的IP否则kubelet探活、kubectl logs都没法工作。打通跨节点的路径Node A上的Pod要能访问Node B上的Pod这需要某种机制把不同节点的Pod网段“连”起来。flannel和calico在1、2、3上做得差不多真正的分歧在第4条。flannel用“隧道”calico用“路由”这是它们最核心的差别。1.3 flannel和calico面对同一张卷子解题思路完全不同flannel的思路很直白既然跨节点的网络不好打通那我就把整个集群的网络“包”一层让所有节点看起来在一个大二层里。它用VXLAN协议把Pod的包封装在UDP里从一个宿主机送到另一个宿主机。Pod IP的网段由flannel统一规划每个节点分到一个/24子网节点间通过隧道通信。这个方案的好处是只要节点IP能互通不管底下是三层路由、二层交换机还是跨地域专线flannel都能跑起来。calico的思路更像传统网络工程师每个Node都是一台“路由器”Pod的IP是这台路由器直连的网段通过BGP协议把这些网段通告给其他节点。主机之间的物理网络就是转发的骨干网。这个方案没有隧道封装数据包怎么进去就怎么出来性能更接近物理网络但前提是物理网络本身能完成这些路由和转发。理解了这两条路线后面所有关于性能、功能、兼容性的讨论都能对上号。2. flannel的层层拆包VXLAN和host-gw数据包到底怎么走2.1 VXLAN模式下一条是VTEP不是宿主机flannel使用最多的是VXLAN模式。搭建好之后每个Node上会多出一个叫flannel.1的虚拟网卡这个就是VTEPVXLAN Tunnel End Point。同时每个节点上还有个叫cni0的Linux bridge所有Pod的veth对都插在这个bridge上充当Pod的网关。假设我要从Node A上的Pod访问Node B上的Pod数据包的路径是这样的Pod的eth0发出数据经过veth对进入宿主机侧的cni0。内核查路由表发现目标Pod网段比如10.244.2.0/24的下一跳是10.244.2.1这个地址不是Node B的物理IP而是Node B上的flannel.1的IP。所以包被送到本机的flannel.1设备。flannel.1是个VXLAN设备内核会把原始IP包封装成UDP包外层源IP是本机物理IP外层目标IP是Node B的物理IPUDP端口默认8472。封装后的UDP包通过物理网卡发出去到Node B之后Node B的内核拆掉UDP外壳把原始数据包从flannel.1里解出来再走cni0转发给目标Pod。这里最容易混淆的地方是ip route里看到的下一跳是VTEP地址不是一个普通的物理IP。很多新手在排查网络时看到下一跳是个10.244开头的IP就以为这个IP可以直接ping通其实它只在隧道内部有意义。还有一个衍生问题VXLAN包经过物理网络时外层头部多了50字节VXLAN 8字节 UDP 8字节 IP 20字节 以太网14字节。如果物理网卡MTU是1500那隧道内的MTU只能是1450否则超过1450的原始包就会在宿主机上被分片性能直接崩掉。这个后面详细讲。2.2 host-gw模式免封装但物理网络必须像一张大二层如果物理网络本身就是二层互通的——比如所有Node都在同一个VLAN里——flannel可以改用host-gw模式。这种模式下没有隧道flannel只是把对方Node的Pod网段写进本机路由表下一跳直接指向对方Node的物理IP。还是刚才那个例子走host-gw时Node A上的路由表里会有一条10.244.2.0/24 via 192.168.1.2 dev eth0数据包从cni0出来查路由表发现下一跳是Node B的物理IP就直接从物理网卡发出去整个过程没有额外封装。性能自然比VXLAN好至少没有了MTU和UDP封装的负担。但host-gw有一个硬性前提所有Node必须在同一个二层网络里而且要能通过ARP直接解析到对方物理网卡的MAC地址。一旦跨了VLAN、跨了路由器包到了下一跳之后没人能继续转发网络就断了。所以host-gw适合机房内部网络环境简单、所有机器都接在同一台交换机上的场景不适合跨网段或复杂网络。2.3 部署flannel时最容易忽略的配置flannel部署在kubernetes集群里通常就是一个DaemonSet配置集中在ConfigMap的net-conf.json里。我会重点关注这几个字段NetworkPod网段必须和kubeadm init --pod-network-cidr保持一致。不一致会导致IPAM分配错乱。Backend选择vxlan还是host-gw。大部分默认模板是vxlan我建议在二层网络里手动改为host-gw。MTU默认模板通常留空让flannel自行推导。物理网卡MTU如果是1500VXLAN模式会自动用1450。如果物理网卡开启了巨型帧MTU 9000flannel也会基于它算出8950这在一个物理MTU不一致的网络里会出问题。实际操作里最省事的部署方式是kubectl apply -f kube-flannel.yml装完之后不要急着看Pod状态先确认每个Node上的CNI配置是否生成了cat /etc/cni/net.d/10-flannel.conflist如果配置目录里什么都没有多半是flannel容器启动失败或者Node没有正确注册。再看一眼路由表ip route | grep 10.244如果看到多个/24子网的路由且下一跳是各个节点的flannel.1说明flannel已经在正常宣告路由了。3. calico不上隧道也能跨节点BGP、IPIP与eBPF三种数据面3.1 为什么calico可以用路由完成Pod互通calico的核心思想是把每个节点当作一台路由器Pod是路由器下的局域网终端BGP负责把路由通告出去。具体到数据面每个Pod会分配一个veth对宿主机侧的叫caliXXX这个接口上配置了Pod IP的地址。每个节点会把自己的Pod网段默认是/26的子块通过BGP通告给其他节点。当Node A上的Pod要访问Node B上的Pod时数据包从veth进入Node A的内核路由表命中一条“下一跳是Node B物理IP”的路由直接走物理网卡发出。整个过程没有UDP封装数据包从源Pod出来到目标Pod除了IP路由之外没有任何额外的网络头。在我们的实际部署里启用calico最典型的网络路径就是物理网卡MTU是多少Pod就能用多少干净利落。这也解释了为什么很多注重吞吐和时延的场景都选calico。3.2 IPIP模式是给“跨三层网络”准备的兜底方案那是不是calico在任何物理网络里都能直接BGP不一定。BGP路由要求所有节点之间能通过物理网络互相转发。如果节点分布在不同的网段、不同的机柜中间还有防火墙或三层路由物理网络本身可能不学习也没有办法转发下一跳指向某个具体IP的Pod网段流量——因为这些Pod网段不是物理网络的直连路由。这时候calico提供了IPIP模式。它会在原始IP包外面再套一个IPv4头外层源IP是源Node物理IP外层目标IP是目的Node物理IP内层还是Pod的IP包。内核通过ipip隧道设备解封装。这样做的好处是物理网络只需要负责转发两个Node物理IP之间的流量不需要关心Pod网段天然跨三层。和VXLAN相比IPIP的封装开销只有20字节MTU可以到1480比VXLAN的1450略好。但IPIP也没有端口概念只靠IP协议号4区分所以有些安全策略严格的网络不允许IPIP协议这时候只能用VXLAN。calico也支持VXLAN模式只是默认更推荐BGP/IPIP。calico里对IPIP模式最常见的配置是CrossSubnet意思是如果源Node和目标Node在同一个子网就裸路由不封装只有跨子网的时候才自动启用IPIP。这是我在跨机房集群里比较推荐的折中方案。3.3 eBPF数据面是后面加的高阶玩法calico在较新的版本里提供了eBPF数据面选项。传统内核数据面里Service的负载均衡靠kube-proxy维护的iptables规则每个数据包都要遍历一堆规则链并发高的时候CPU消耗非常大。eBPF模式把datapath直接放到内核的BPF hook里转发路径更短还能支持DSR直接服务器返回模式访问Service的流量可以先到节点但返回的包直接从目标Pod所在的节点返回给客户端不再绕回那个节点适合对转发路径有要求的场景。eBPF模式的实际启用条件不低需要内核版本较新、挂载bpffs、关闭或替换掉kube-proxy等。我的建议是除非你在流量模型上有硬性要求并且团队里有熟悉eBPF的人否则先用默认的bird后端就行不必上来就挑战eBPF模式。3.4 calico部署后的关键检查项用官方manifest部署calico时需要关注几个环境变量CALICO_IPV4POOL_CIDRPod网段CIDR和kubeadm的pod-network-cidr一致。IP_AUTODETECTION_METHODcalico自动探测节点IP的方式。默认first-found在多网卡的节点上很可能探测到错误的IP比如把docker0的内网IP当作节点IP结果BGP建立不了。我在多网卡节点上一般手动指定interfaceeth0。IPIPModeAlways、Never或CrossSubnet。CALICO_NETWORKING_BACKENDbirdBGP路由、vxlan或birdlinux。部署后检查BGP邻居状态是很重要的一步kubectl exec -n kube-system calico-node-xxxx - calicoctl node status如果每个节点都显示Established说明BGP已经跑通了此时跨节点ping一下测试Pod就能通。如果状态是Starting或Idle优先看网络连通性和防火墙而不是急着看calico配置。4. 选型参考从性能到功能calico没有全面碾压flannel也没有想象中鸡肋4.1 性能差异快在数据路径不在带宽很多对比文章说“calico比flannel快”我会更精确地说快在数据包的转发路径尤其是在小包和PPS的场景。VXLAN模式下数据包多经历一次封装/解封装CPU的中断和上下文切换会明显增加。在高PPS的压测里VXLAN模式很容易先撞上CPU瓶颈。但在常见的业务流量里两者的带宽差异未必有网上说的那么夸张。过去我们做过简单压测同样物理网络下TCP大流量的吞吐差距不算特别悬殊因为物理网卡本身的带宽和CPU够用差距更大的是在网络小包、频繁连接建立的场景里calico的裸路由路径优势非常直观。所以我的看法是如果应用是小包高并发、对延迟敏感选calico如果只是普通Web业务、CPU有余量flannel也完全撑得住。4.2 NetworkPolicyflannel的短板是硬的Kubernetes的NetworkPolicy是集群层面的安全访问控制它定义的是“哪些Pod可以访问哪些Pod/端口”。这个能力不是K8s内置实现的必须由CNI插件来实现。flannel本身不支持NetworkPolicy如果你用纯flannel创建了NetworkPolicy对象也不会生效集群等于“全放通”状态。calico原生支持完整的NetworkPolicy包括基于Namespace标签选择、基于Pod选择器、Ingress/Egress双向控制、端口范围等。它靠每台Node上的Felix组件同步策略生成对应的iptables规则。从我个人经历来看很多企业级集群要求业务间网络隔离这一条往往是最终选calico的决定性理由而不是性能。需要补充一句如果团队有几乎为零的网络安全要求且内网环境封闭flannel的全放通也不是不能接受。但如果上面有安全审计那就老老实实用calico。4.3 运维复杂度与资源占用flannel的组件非常轻除了CNI配置生成之外几乎就一个守护进程资源消耗低排查也简单。calico的组件一堆calico-node负责路由和策略、Felix负责iptables、bird负责BGP还有calico-kube-controllers负责IPAM和策略同步资源占用和运维心智成本明显更高。我实际感受是一个小规模集群比如10个节点以下用flannel基本不用怎么管而calico每个节点至少需要关心BGP状态、IP池大小、Felix日志这些维度。当然这些复杂度换来了网络策略和高性能值不值看团队情况。4.4 一张选型决策表维度flannelcalico网络模型VXLAN/host-gw隧道BGP路由/IPIP/VXLAN数据路径有封装开销VXLAN或二层依赖host-gw裸路由路径短适合追求低延迟NetworkPolicy不支持原生支持功能完整跨三层网络VXLAN可以直接跑IPIP或VXLAN模式可以跑物理网络要求VXLAN模式无特殊要求BGP模式要求三层可路由IPIP/VXLAN无要求运维复杂度低中高资源占用低中高适合场景小型集群、内网简单环境、追求省心中大规模、需要网络隔离、注重性能5. 从flannel迁移到calico的完整实操记录5.1 迁移前要做好的网络盘点我们有一次实际迁移场景是一个已有的kubeadm集群Pod网段是10.244.0.0/16原先用的flannel VXLAN模式所有节点都在同一个机房同一个二层内。因为要上线NetworkPolicy决定切换成calico。迁移前我做了三件事确认所有节点的物理网卡都是二层互通的这样可以关闭IPIP直接用纯BGP路由性能最好。确认TCP 179端口在所有节点之间是放开的。BGP走TCP 179如果被防火墙拦住calico的邻居状态永远起不来。备份了每个节点上/etc/cni/net.d/下的CNI配置以便回滚。同时检查了Pod的Service记下需要重点验证的核心业务名单。5.2 清理flannel要清理哪些东西这里最容易踩坑的点是只删掉flannel的DaemonSet是不行的老的CNI配置、bridge、路由和IPAM数据都会残留会干扰calico启动。我的清理顺序是kubectl delete ds kube-flannel -n kube-system然后在每个节点上执行rm -f /etc/cni/net.d/10-flannel.conflist ip link delete cni0 ip link delete flannel.1这里特别要强调的是清理cni0和flannel.1这两个虚拟设备。如果不清新装calico时节点上会存在一个残留的bridgePod的veth可能接错地方或者出现路由表里既有老flannel路由又有calico路由的情况网络表现出随机丢包。ip link delete之后路由表里对应的旧路由也会一并消失后面calico才好接管。另外还要检查一遍所有Node上的路由表确认没有残留的10.244网段路由和flannel相关的iptables链。如果有ip route del手动清掉。5.3 部署calico并调整关键参数我用的是官方提供的calico.yaml但应用前必须把它改成和现网匹配的配置。这里改了三处- name: CALICO_IPV4POOL_CIDR value: 10.244.0.0/16 - name: IP_AUTODETECTION_METHOD value: interfaceeth0 - name: IPIPMode value: NeverIPIPMode是Never因为我们确认了节点都在二层互通裸路由就够用了没必要多一层封装。然后正式部署kubectl apply -f calico.yaml装完之后calico的DaemonSet开始在每个节点上启动等Pod变成Running后再执行kubectl exec -n kube-system calico-node-xxx -- calicoctl node status我们看到所有节点BGP状态都是Established接下来把老的业务Pod逐个滚动重启让它们获得新的IPAM分配和网络设备。5.4 迁移后必须做的连通性验证清单这一步很多人容易忽略觉得BGP状态是Established就万事大吉实际上一旦切换CNI整条链路都要重新验证一遍。我会按下面的顺序执行跨节点Pod间的ping确认Pod网段路由生效。跨节点Pod间访问Service ClusterIP确认kube-proxy的iptables规则和网络策略没有冲突。从节点访问任意Service的NodePort确认宿主机到Pod的路径正常。进入一个Pod确认DNS解析正常因为CoreDNS本身也是跨节点访问的。对高危业务做一轮HTTP请求压测确认时延没有出现明显跳变。我们在这次迁移里还碰到过一个奇怪现象换了calico之后有个Service的ClusterIP访问偶尔超时。后来排查发现是某个节点上还有一条老的flannel路由残留把部分流量引到了已经删除的老隧道设备上。清掉那条路由后立刻恢复。所以迁移后检查每个节点的完整路由表是一件非常必要的事。6. 真实集群里最容易翻车的网络故障排查手册6.1 MTU引发的“大包不通小包正常”无论是flannel VXLAN还是calico IPIPMTU问题都是常见又隐蔽的坑。有一个经典症状容器内ping跨节点Pod的小包能通但用curl下载大文件一直卡住或scp传几百MB的文件到一半就断。用ping -M do -s 1472测试发现超过某个大小之后就收不到回包。这个问题的本质是隧道封装把有效载荷减小了但应用层不知道。VXLAN场景下物理MTU是1500隧道内MTU只能到1450如果容器镜像或者网络配置里固定用了1500超过1450的数据包会先被封装再在物理网卡处因为超过MMU而被丢弃或者触发分片导致重组开销巨大。排查时先看路径上每个网卡的MTU用ip link确认再看隧道设备或Pod网卡的MTU是否匹配。也可以抓包看是不是有ICMP fragmentation needed的包没回来。修复方式就是显式设置MTUflannel在net-conf.json里写死mtucalico在IPPool的mtu字段里配置。6.2 防火墙放行了端口BGP却一直连不上calico的BGP是基于TCP 179的很多网络管理员会记得放行TCP 179却忘了VXLAN的UDP 8472或IPIP的协议号4。在自建机房和云环境里安全组规则比想象的更严格。遇到BGP邻居一直无法Established时我通常这样排查先确认对端节点网络通不通ping 对端物理IP。检查TCP 179是否通nc -vz 对端IP 179。在节点上抓包tcpdump -i eth0 tcp port 179看是否有SYN包发出有没有SYN-ACK回来。正常情况下能看到三次握手。如果只有SYN发出但没有响应大概率是对端防火墙或本机iptables把包丢了。检查本机iptables INPUT链看看是不是有默认DROP策略。有些节点上的安全加固脚本会在INPUT链里drop不明端口即使云安全组放行了也没用本机的iptables依旧会拦。需要注意的是问题不一定出在防火墙也可能是BGP Router ID重复了。如果手工配过Router ID且多个节点重复BGP会话会反复重置。用kubectl logs查bird日志可以看到持续出现的连接断开信息。6.3 高密度节点下BGP邻居翻动与full mesh困境calico默认使用full mesh模式即每个节点都和集群内其他所有节点建立BGP邻居。这个模型在节点量小的时候很省心一旦节点超过几十个问题就来了。假设有50个节点full mesh建立的BGP会话数是50*49/2 1225每个节点要维护49个TCP会话。如果还有频繁的网络波动、安全组抖动或者节点Pod反复重启BGP会话容易出现翻动表现为BGP状态在Established和Connect之间反复跳路由表一阵一阵地消失。这类问题的解决方案是引入路由反射器Route Reflector。让一组节点作为RR其他节点只和RR建立BGP会话这样会话数从O(n²)降到O(n)。calico里可以通过BGPPeer和BGPPeeringPolicy等配置把某些节点设定为RR同时关闭默认的nodeToNodeMeshEnabled。集群规模一旦接近100个节点我建议提前规划RR不要等BGP翻动了再去处理。6.4 NetworkPolicy规则不生效的排查顺序用calico之后运维多了一个日常任务网络策略不生效。排查时我有一套固定的顺序先确认集群里没有flannel或其他CNI组件残留。如果同时存在多个CNICNI配置目录里只应该有一个conflist多的话网络策略可能落到了错误的插件上。检查calico-node和felix容器状态是否Running且日志无报错。确认NetworkPolicy对象本身是否被正确解析kubectl get networkpolicy -n 业务命名空间 calicoctl get networkpolicy --namespace 业务命名空间检查selector是否匹配。选中了错误的Namespace或者Pod有非预期的标签策略根本不会命中目标。进入目标Pod抓包或看iptables规则链iptables -L -n -t filter | grep cali正常情况下命中策略的Pod会出现在对应的cali链里。如果链里没有该Pod的IP说明策略对象没关联到它。特别提醒一下hostNetwork模式的Pod是不受普通NetworkPolicy影响的因为它不走CNI创建的虚拟网络路径。如果业务Pod用了hostNetwork同时又指望网络策略能兜底那基本不会生效需要在设计业务部署时就避开这种混用。7. 关于默认答案的一些个人体会如果现在让我重新搭一个新集群在没有特殊要求的前提下我的默认选择其实还是calico哪怕节点只有十几个。原因是多数集群最终都会在某一天需要网络隔离、需要审计规则到那时候再迁移插件成本远高于一开始就选calico。但我也理解为什么很多小团队更愿意用flannel部署简单、排查成本低、一个DaemonSet打天下。如果你的集群就是一个内部开发环境风险可控flannel完全没有问题。最后分享一个排查习惯无论是flannel还是calico遇到跨节点网络不通时先看路由表再看防火墙最后才看插件日志。因为大部分问题都出在“Pod网段没有正确广播”“某个端口被拦”“MTU不匹配”这三类原因上插件本身崩溃的概率反而很低。先把网络路径捋顺了再去翻分布式系统的日志会省掉很多无用功。
阅读完成 · 觉得有帮助?
咨询建站