news 2026/10/21 12:46:44

K8s网络通讯到Calico替换:Flannel VXLAN模式与NetworkPolicy实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
K8s网络通讯到Calico替换:Flannel VXLAN模式与NetworkPolicy实战解析

先把几个关键词在脑子里过一遍:k8s网络通信、Flannel vxlan模式、Calico网络插件替换、网络策略。这四个点其实是k8s网络这条主线上的四个递进环节——先搞清楚通信模型,再让插件把网络跑起来,跑起来之后遇到性能或策略需求,再决定换不换插件,最后用NetworkPolicy把安全边界收紧。

这篇文章我会按实际运维节奏来写,不空谈概念,重点放在数据怎么走、问题怎么查、替换怎么操作、策略怎么写这几个层面。如果你正在公司里维护一套k8s集群,或者准备从Flannel迁到Calico,又或者被网络策略折磨过,那这篇应该能给你省下不少时间。

1. 从k8s网络模型聊起:为什么Pod网络必须解决三件事

先别急着碰Flannel和Calico,k8s网络通信这件事,本质上是在回答三个问题:

  • 同一个Pod里的多个容器之间,怎么互相访问?这个最简单,共享同一个网络命名空间,直接通过localhost就能通。
  • 不同Pod之间(不管在不在同一台节点上),怎么互相访问?这就要求每个Pod都有自己独立的IP,而且这个IP在整个集群里全局可达。
  • Pod和Service之间怎么通信?Service是一个虚拟概念,它没有实体网卡,必须靠kube-proxy把流量转发到后端的Pod上。

你仔细琢磨一下,k8s网络模型有一个和其他传统虚拟化方案完全不同的前提:Pod IP必须是全局唯一且全局可达的。也就是说,任意一个Pod可以不经任何NAT转换,直接用Pod IP访问到另一个节点上的Pod。这一点和企业里常见的按网段划分物理服务器的习惯很不一样,很多第一次接触k8s的同学就是在这里绕晕的。

我举一个从传统运维切换过来的例子。在物理机或者虚拟机的时代,我们习惯把某个业务固定分配在一个网段,比如业务A用10.1.0.0/16,业务B用10.2.0.0/16,互相访问靠路由或者安全组。但在k8s里,Pod是"出生就带IP"的,而且可能随时被销毁重建,IP也会跟着变。这意味着你不可能靠固定IP去管理网络,只能依赖CNI插件动态解决地址分配和路由打通的问题。

CNI插件解决的就是这个事。它的职责可以用一句话概括:给Pod分配IP,并且想办法让这个IP在整个集群里可路由。Flannel、Calico、Weave、Cilium都属于CNI插件,它们解决同一个问题,但底层实现方式差异很大——这正是我们做选型时要弄清楚的。

还有一个常年被忽略的点:Pod之间的流量路径和传统网络也不太一样。k8s集群内,源Pod发往目标Pod的包,通常会经过veth虚拟网卡进入宿主机,再通过宿主机上的路由规则(由CNI插件写入)决定下一跳,最终到达目标Pod所在的宿主机,再进入目标Pod的veth。也就是说,一个Pod到Pod的数据包,是"上了宿主机"再"下来"的,而不是直接在二层广播域里找目标。理解了这个,后面排查网络问题时会顺很多。

2. Flannel vxlan模式到底在做什么:数据包的两次封装与一次隧道

Flannel是我最早在生产环境里用到的CNI插件,因为它部署简单、资源占用低、稳定性也够,非常适合集群规模不大的场景。Flannel支持的几种后端模式里,我实际用得最多的是vxlan,所以这里重点拆它。

2.1 vxlan模式的核心概念:VNI、VTEP和UDP封装

vxlan这个名字听起来很吓人,但拆开看就三个关键词:

  • VNI(VXLAN Network Identifier):vxlan隧道的标识,长度24位。Flannel默认用VNI 1,也就是说所有节点参与的都是同一个大二层网络。VNI决定了数据包会被扔进哪个逻辑隧道,不同VNI之间是隔离的。
  • VTEP(VXLAN Tunnel End Point):vxlan隧道的端点。Flannel会在每个节点上创建一张名为flannel.1的虚拟网卡,这张网卡就是当前节点的VTEP,IP地址通常是当前节点所在的Pod网段的网关地址。
  • UDP封装:原始的数据包被塞进UDP里,外层IP是源宿主机到目标宿主机的地址。这是vxlan和vlan最直观的区别——vlan扩展二层依赖交换机配置,而vxlan只需要三层网络能通,就能把二层桥接过去。

我们用生活化的方式理解一遍:两个宿舍楼(两台宿主机)之间,A楼的同学(Pod A)要送一封信给B楼的同学(Pod B)。A楼宿管(VTEP)看到收件人是B楼的,就把信套进一个大信封(UDP封装),大信封上写着"送到B楼宿管收"。B楼宿管收到后拆开大信封,再把里面的原信送到同学手上。这个"再套一层信封"的过程,就是vxlan的核心逻辑。

2.2 跨节点通信的完整数据流:从Pod A到Pod B的每一跳

我们把一个实际跨节点的数据包流程拆到最细,这是排查网络问题时必须要掌握的。

假设Pod A在node1上,IP是10.244.1.5;Pod B在node2上,IP是10.244.2.8。Flannel网络配置为10.244.0.0/16,node1负责10.244.1.0/24,node2负责10.244.2.0/24。

第一步,Pod A发出数据包,源IP是10.244.1.5,目标IP是10.244.2.8。因为目标IP不在同一个子网内,Pod A的默认网关指向了eth0对应的宿主机侧veth的网关,也就是10.244.1.1。数据包通过veth对进入node1的root命名空间。

第二步,node1上的内核路由表发挥作用。Flannel在启动时会往节点路由表里写入一条关键路由:10.244.2.0/24这条子网的下一跳是10.244.2.1(这就是node2上flannel.1的地址),并且指向的设备是flannel.1。这时数据包被送到flannel.1这张虚拟网卡。

第三步,vxlan封装发生。node1的flannel.1设备查看自己的FDB(转发数据库)表,找到10.244.2.1这个VTEP的MAC地址对应的宿主机实际IP,这个IP就是node2的内网IP,比如192.168.1.12。于是内核把原始以太网帧作为UDP的payload,外面再包一层UDP头(目的端口默认8472)和IP头(源IP为node1 IP 192.168.1.11,目的IP为node2 IP 192.168.1.12)。

第四步,这个外层包通过eth0发出,经过物理网络到达node2。node2收到后,内核拆掉外层UDP和IP头,还原出原始的二层帧,交给本机的flannel.1设备。

第五步,node2的flannel.1设备根据目标MAC地址10.244.2.8,找到对应的本地veth设备,把数据包通过veth对送进Pod B的网络命名空间,Pod B收到原始数据包。整个过程中,源Pod和目标Pod感知不到vxlan封拆的存在,它们以为自己就在同一个二层网络里。

2.3 vxlan模式能跑通,但性能损耗你要心里有数

Flannel vxlan模式最大的好处是部署极简,对底层网络没有额外要求,只要节点间三层可达就行。这非常适合刚开始上k8s、网络设备不归自己管的场景。

但代价也很明显:一次跨节点通信要经历两次封包拆包,而且UDP封装格式本身不如纯路由转发高效。在我实测的集群里,vxlan模式相比宿主机之间的直连,网络带宽大约有10%~20%的损失,延迟也会增加0.2到0.5毫秒,这个数值在跨可用区或者网络负载高的环境下会更明显。

所以如果你的业务是计算密集、小包大流量、对延迟敏感的类型,比如实时音视频或分布式数据库,Flannel vxlan模式不一定是最好选择。这时候就要认真考虑Calico了——它走的是另一套思路。

3. Calico替换Flannel实操:从选型考量到命令级迁移步骤

为什么选择Calico而不是继续优化Flannel?我看重的其实是三点。

  • 支持BGP路由模式,跨节点通信不需要额外封装,性能接近宿主机直连。
  • 原生支持NetworkPolicy,Flannel本身不提供网络策略的强制执行能力,这是很多安全需求无法绕开的硬伤。
  • 扩展性好,Pod数量上千甚至上万时,Flannel的vxlan隧道管理会成为瓶颈,Calico在规模上的表现更稳。

当然,Calico也不是没有代价。它的组件更多,运维复杂度高一些;BGP模式下要求节点二层可达(混合云场景要单独处理);Felix组件在每个节点上都要跑,消耗的CPU和内存比Flannel高。做替换决策时,必须结合自己集群的规模和业务类型来权衡。

3.1 替换前必须确认的三件事

动生产集群的网络组件,哪怕只是替换CNI插件,都是一件需要严格按步骤来的事。我在实际操作中踩过坑,所以先强调三个前置确认项:

第一,确认当前集群的Pod网段。Flannel默认是10.244.0.0/16,Calico默认是192.168.0.0/16。如果你改Calico配置后网段不一致,已经分配出去的Pod IP会和新的网络规划冲突,导致部分Pod失联。最稳妥的做法是让Calico直接沿用现有网段。

第二,确认节点上旧插件的残留文件位置。Flannel会在/etc/cni/net.d/目录下生成conf文件,在/opt/cni/bin/目录下放二进制。替换时如果不清理,节点上的kubelet可能还会按照旧配置去调用cni插件,造成新Pod无法启动或者启动了但网络不通。

第三,确认应用是否依赖浮动的Pod IP访问逻辑。因为替换过程会重建所有非主机网络的Pod,新的Pod IP会变化,如果有应用硬编码了旧的Pod IP,替换前一定要先排查,否则流量会中断。

3.2 手工迁移的操作流:先装Calico、再删节点Flannel、最后清残留

我不推荐在已运行的集群上用一条命令装Calico就了事,至少要按下面这个顺序来:

  1. 用kubectl apply安装Calico清单。如果你在自建集群里,建议使用BGP模式,并按集群规模修改IP_AUTODETECTION_METHOD。这个参数很关键,它决定了Calico检测节点网卡的方式。如果你的集群是双网卡环境(业务网和管理网并存),写错这个参数会导致BGP邻居建立不了。
  2. 逐节点排水和删除Flannel。先把节点置为不可调度并驱逐Pod,然后把Kubelet停掉,最后删除Flannel的conf文件和二进制。
  3. 重新启动Kubelet,等待新的Pod调度到该节点,验证Pod能拿到Calico分配的IP。
  4. 全部节点迁移完成后,删除旧的Flannel DaemonSet和kube-flannel-net网卡。

讲到这我必须提一个非常容易踩的坑:Calico安装完成后,如果没有手动清理Flannel在节点上创建的路由规则,内核转发表里就会残留旧路由。这时新Pod即使从Calico拿到了正确的IP,因为路由表里优先级更高的旧Flannel路由还没删,数据包可能被错误转发到flannel.1设备上。这个问题表象非常隐蔽,表现为Pod能创建但网络不通,ping网关都没反应。排查方法是登录节点执行ip route命令,看到不相关的Flannel网段路由残留时手动删除。

3.3 迁移后的验证清单和回滚预案

迁移完成不代表网络就正常了,要系统性验证一遍。我在线上环境会跑下面几项:

  • 同节点不同Pod互通:用kubectl exec进一个Pod,ping同节点另一个Pod的IP。
  • 跨节点Pod互通:ping一个明显在另一台节点的Pod IP。
  • 跨节点Service访问:从Pod内通过ClusterIP访问一个Service,看是否正常。
  • DNS解析:在Pod内执行nslookup,确认kube-dns能解析Service名。
  • 网络策略执行验证:部署一个测试deployment,确认Calico创建的iptables规则生效。

如果验证中发现大范围异常,最快的回滚路径是:重新apply回Flannel的DaemonSet,删除Calico的安装清单,重启所有节点的Kubelet,再清一次路由表。之所以强调回滚预案,是因为线上环境你永远不知道下一个变量是什么,提前准备好,真出问题时手不会抖。

3.4 Calico替换后的性能对比测试方法

如果你也想量化替换的效果,可以在切换前后分别做一次简单的性能测试。我常用的方法是:在节点上部署两个测试Pod,一个固定在node1,一个固定在node2,然后测试TCP带宽和发包延迟。

测试TCP带宽可以用iperf3,服务端和客户端各起一个容器。测试延迟可以用ping带时间戳的方式,统计RTT的中位数和P99。切换后我一般会对比三个指标:带宽、平均RTT、P99延迟。如果是同机房、同一二层网络环境,Calico相比Flannel vxlan在P99延迟上的优化往往是最直观的。

4. 网络策略范例总结:从默认放行到精细管控的落地套路

网络策略(NetworkPolicy)这个概念,很多刚入门的人容易把它理解成"防火墙"。它确实有防火墙的作用,但工作机制完全不同。它是通过给Pod打标签,然后在策略里引用标签选择器,最后转换成CNI插件(如Calico)的iptables规则来生效的。

一个重点:如果集群里用的是Flannel但没装支持策略的辅助组件,NetworkPolicy配置了也不会生效。这也是前面说Flannel会被替换的原因之一。Calico天然支持NetworkPolicy,所以替换之后,策略管控才能真正落地。

4.1 四个必掌握的NetworkPolicy范例

我不打算罗列官方文档的所有选项,只分享四个生产环境里最常用的范例,可以直接抄。

第一个:默认拒绝所有入站流量。

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress spec: podSelector: {} policyTypes: - Ingress

这个策略看似简单,但它是"白名单管控"的基础。没有它,其他所有Ingress策略都可以被绕过。所以我在命名空间里通常会配一条这样的默认拒绝策略,然后只放行明确需要的流量。

第二个:只允许同一个应用的不同实例互相访问。

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-same-app spec: podSelector: matchLabels: app: myapp policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: myapp

注意这里podSelector和ingress里的podSelector含义不同,前者是"策略保护谁",后者是"允许谁访问"。如果你想让只有带同样标签的Pod能访问,前后两个标签要一致。

第三个:允许来自某个网段的客户端访问特定端口。

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-from-admin-net spec: podSelector: matchLabels: app: web policyTypes: - Ingress ingress: - from: - ipBlock: cidr: 10.200.0.0/16 ports: - protocol: TCP port: 8080

这个场景常用于管理后台或接口服务,只允许公司内部的运维网段或办公网段访问。ipBlock的cidr注意要写得准确,如果漏写了端口,默认是放行所有端口的流量,这个细节很多人踩过坑。

第四个:限制Pod只能访问特定外部IP的DNS和HTTP端口。

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: restrict-egress spec: podSelector: matchLabels: app: worker policyTypes: - Egress egress: - to: - ipBlock: cidr: 203.0.113.0/24 ports: - protocol: TCP port: 443 - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system ports: - protocol: UDP port: 53

这条策略包含两部分出口流量:外部HTTPS接口和集群内DNS解析。注意namespaceSelector里kubernetes.io/metadata.name是k8s自动给每个命名空间打的标签,不需要你手动维护。

4.2 网络策略排查实录:五个高频问题

在我实际帮别人排查网络策略问题的过程中,下面这几个场景出现频率最高:

第一个:策略配置了但不生效。最可能的原因是CNI插件不支持,比如还在用Flannel。排查方法很简单,先确认插件支持NetworkPolicy,再确认策略状态。

第二个:策略写太严格,Pod访问不了Service。这个坑很常见。NetworkPolicy是按Pod维度匹配的,但Service通常会把流量转发给多个后端Pod。你如果只允许了某个源IP访问其中一部分Pod,另一部分可能会弹出连接拒绝。所以写策略时,一定要看清楚Service选择器匹配的Pod标签范围。

第三个:误伤kube-system命名空间。默认拒绝Ingress的策略,如果同时加了一条拒绝Egress,Pod内部的DNS解析会被一起阻断。所以你的策略里一定要保留到kube-system的UDP 53端口放行,否则所有Pod都解析不了域名。

第四个:ipBlock的CIDR写错,把集群自身网络也隔离了。有时候你想限制外部来源,但Pod直连宿主机时源地址是宿主机网段,如果你忘了放行这个网段,会让很多内部调试变得非常困难。

第五个:多个策略叠加时的选择器逻辑搞混。NetworkPolicy的Ingress规则是"或"关系,多条规则只要满足其中一条就放行。但如果你写了两条策略,一条允许源A,一条允许源B,流量来自源C时,如果默认拒绝策略也存在,C就被拦。把选择器的"或"与"和"关系梳理清楚是避免误伤的关键。

4.3 从默认放行到白名单管控的渐进式落地方案

最后分享一个我比较推荐的生产环境网络策略落地路线,不要一口气把所有命名空间都加上默认拒绝策略,而是一条一条来。

我通常的做法是:先挑一个低风险命名空间做试点,加上默认拒绝Ingress策略,然后观察两天,看业务接口调用是否正常。确认稳定后,再逐渐覆盖其他命名空间。上线前先用kubectl get netpol -A统计一下策略变化,同时检查核心组件(比如监控、日志、Ingress Controller)所在的命名空间,要把它们的入站和出站流量全部梳理清楚再动手。

5. 写在最后:我维护k8s网络这几个月的一些体会

Flannel和Calico的选型没有绝对的对错,只有是否匹配你的场景。如果你的集群规模不大、业务对网络延迟不敏感、又没有复杂的访问控制需求,继续用Flannel完全不是问题——它的稳定性和易用性在这个场景下是加分项。但只要你开始面临三个信号:跨节点流量大且延迟敏感、需要对Pod做精细的网络隔离、集群规模已经过了几百个节点,那么Calico就是值得你付出迁移成本的选择。

我还想提醒一点:网络组件替换这种事,一定要在业务低峰窗口操作。每一次节点排水、重启Kubelet都会引发该节点上所有Pod重建。如果业务没有多副本冗余,就会产生短暂中断。我的习惯是迁移前先梳理每个工作负载的副本数和分布,把没有冗余的Pod所在节点放到最后处理,给系统留出缓冲。

另外,平时维护k8s网络配置时,尽量把CNI插件的配置文件、DaemonSet清单、策略YAML都纳入Git管理,用kubectl apply直接应用,而不是手动改节点上的配置文件。我曾经见过手动编辑/etc/cni/net.d下的配置后,节点重启时配置恢复默认,导致Pod网络大面积异常的情况。版本化管理配置,是你在深夜处理故障时最好的后盾。

这期就先写到这。如果你最近也在做Flannel到Calico的迁移,或者正在写第一批NetworkPolicy,欢迎把遇到的问题或踩过的坑整理出来,咱们下一次继续聊。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 12:46:21

Ingress-nginx核心原理与实战:K8s七层负载均衡从入门到排错

做K8s运维到今天,如果让我说哪个组件最容易把概念搞混,我一定选Ingress。Service和Pod那套逻辑不算难懂,但一碰到Ingress,脑子里得同时转三个对象:Ingress资源、Ingress Controller,还有Controller背后真正…

作者头像 李华
网站建设 2026/10/21 12:46:20

AI编程流水线:用Subagent工作流把写代码变成严谨工程

最近常有人问我:AI 编程工具我也在用了,为什么在别人手上那么神,在我手上就翻车?演示视频里它能一口气写几百行代码,放到真实项目里却各种离谱——改 A 坏 B、上下文越聊越乱、代码能跑但没人敢上线。我通常直说&#…

作者头像 李华
网站建设 2026/10/21 12:46:20

C++实战:27个趣味程序从编译到二次开发

简介:这份资源是面向C初学者与进阶学习者的27个经典趣味程序合集,以「学练结合」的方式帮助读者在动手实践中理解语法与编程思想。内容覆盖从Hello World变体、数据类型与变量、数组与字符串处理,到指针与内存操作、类与对象、继承多态封装等…

作者头像 李华
网站建设 2026/10/21 12:46:09

CycleGAN无配对图像翻译:循环一致性损失与Keras实现避坑指南

简介:这份资源是CycleGAN的Keras实现代码包,面向具备一定深度学习基础、希望上手无监督图像到图像转换的开发者与研究者。CycleGAN通过两个生成器与两个判别器的对抗博弈,配合循环一致性损失,在无需成对样本的条件下完成跨域风格迁…

作者头像 李华
网站建设 2026/10/21 12:45:53

改进DETR算法的齿轮表面缺陷识别方法与工程实践

齿轮表面缺陷检测这个方向,前几年大家讨论最多的是YOLO改到第几版、mAP能到多少。这两年风向变了,越来越多实验室和工厂技术团队在往DETR这类端到端检测器上转。原因也不难理解:齿轮缺陷的形状差异实在太大,一条划痕可以横跨整个齿…

作者头像 李华
网站建设 2026/10/21 12:45:49

self_operating_computer实战:多模态大模型操控电脑的完整指南

第一次接触 self_operating_computer 时,我第一反应是:“这不就是给大模型装上了一双眼睛和一只手吗?”它要做的事情其实很直白:让 AI 看着电脑屏幕,自己移动鼠标、敲击键盘,像真人一样完成你交代的任务。…

作者头像 李华