☰
Kubespray网络配置实战:从CNI选型到跨节点排障
2026/10/19 9:52:42 网站建设 项目流程

1. 为什么Kubespray里的网络配置最容易被“跳过”又最坑

这篇是Kubernetes基础系列里关于Kubespray部署过程中网络配置的实操笔记,也就是标题里说的network-pro部分。Kubespray用来部署集群确实省心,一句ansible-playbook就能把几十个节点拉起来,很多人习惯了“装完就跑demo”的节奏,对网络插件的理解停留在“选个默认的就好”。但以我这些年用Kubespray折腾各类环境的经验来说,网络恰恰是出问题最多、也最容易被忽略的一环。集群装不起来、节点反复NotReady、CoreDNS解析超时、跨节点Pod互通异常,大部分都能追溯到网络插件的配置和底层网络环境之间的配合出了问题。

这篇内容适合正在用Kubespray搭集群、或者已经搭好但时不时被网络问题折磨的人阅读。我不会重复官方文档里那套“复制粘贴就完事”的说明,而是把网络插件在Kubespray里到底扮演什么角色、选型时应该看什么、配置时哪些变量真正影响行为、部署后怎么验证、出问题怎么排,一条线讲清楚。说白了,network-pro不是指某个高不可攀的“专业插件”,而是指让网络这套东西在部署前、部署中、部署后都可预测、可控制、可排查。

1.1 网络插件不是Kubespray的“附带品”,而是部署参数的输入

Kubespray虽然把很多细节都封装成了Ansible角色,但它并不等于“装完拉倒”。你选择的网络插件会直接影响Kubeadm初始化的参数、kubelet的网络配置、节点的路由策略,以及apiserver里跟CIDR相关的参数。这些不是独立的东西,而是一层层耦合的。

举个例子,Kubespray需要提前知道你打算用哪个Pod网段,这个网段会被写进kubeadm的pod-network-cidr参数里,同时网络插件(比如Calico)的IP池也要和它保持一致。如果你只改了某一处,插件和kubeadm之间对不上,轻则Pod拿不到预期网段的IP,重则整个集群的网络策略直接失效。

更隐蔽的问题是,Kubespray的默认值不一定适配你的物理网络环境。比如它默认的Pod网段是10.233.64.0/18,Service网段是10.233.0.0/18。如果你的机房、云VPC或者办公网里恰好用了这一段地址,那问题就大了:Pod访问集群外服务时可能撞上已有的IP,路由表错乱,数据包在物理网络里迷路。这类问题不是插件的bug,而是“网络参数没有作为整体去规划”导致的结果。

我见过一个非常典型的场景:某套测试环境里,团队直接用了Kubespray的默认网段部署,本来一切正常,后来因为业务需要和另一套老系统的内网网段打通,发现两边的网段直接重叠。当时改CIDR的代价已经很大了,涉及重新初始化集群、清理所有Pod和网络策略。如果在部署前就把网络规划看成和版本规划同等重要的事情,这个坑完全可以避免。

1.2 “默认能用”和“生产可用”之间差了多远

很多人觉得Kubespray默认配置跑通了,网络就“没问题”了。实际上默认配置只能证明“在这个网络环境里勉强能用”,离“可控、高性能、可排障”还有不小的距离。

举几个默认配置下容易埋雷的点。Calico插件默认的封装模式在不同子网间可能是IPIP,IPIP协议在多数云平台的安全组里默认不放行。一旦节点跨可用区、跨VPC子网,你的物理ICMP和TCP可能都通,但IPIP封装的数据包全被丢在半路。这种问题用kubectl get pods -A根本看不出异常,至少我最初碰到时整了半天,最后才发现是安全组没放行协议号4。

再比如MTU。物理网卡MTU是1500,IPIP封装要多占20字节,VXLAN封装要多占50字节左右。如果你没有让Calico或Flannel根据underlay网络的MTU去自动计算,而是用了不匹配的值,就会出现“小包通、大包不通”的诡异现象。数据库连接时好时坏、视频流卡顿,排查到后面往往都是MTU在搞鬼。

网络策略也是默认容易忽略的点。Kubespray里有一项enable_network_policy,默认可能没开。不开的时候,Calico虽然也能转发流量,但不做策略控制。如果你对多租户隔离有要求,这会是个大隐患。

所以我在实际操作中的习惯是:部署前先把网络相关参数全部梳理一遍,而不是只在all.yml里改改版本号。这也是我写这篇长篇笔记的初衷——把该注意的坑提前暴露出来,而不是等集群跑起来再连夜补。

2. 选插件前先看清Kubespray的网络模型和变量层级

Kubespray部署的网络配置分散在多个变量文件里,并不是你把kube_network_plugin改成cilium就万事大吉了。同一个插件在不同版本的Kubespray里,支持的变量名和默认行为还可能有变化。这一节我把变量层级和选型逻辑拆开讲,搞清楚这些,后面配置再复杂也不太会搞乱。

2.1 网络参数都藏在哪些文件里

以我常用的Kubespray版本为例,和网络相关的配置主要在inventory/mycluster/group_vars/k8s_cluster/目录下,其中两个文件最核心:

  • k8s-cluster.yml:里面定义了kube_network_plugin、kube_pods_subnet、kube_service_addresses这些顶层参数。
  • kube-networking.yml:插件级的更细化配置,比如Calico的IPIP模式、VXLAN相关参数、网络策略开关等。

有非常多人在部署时会改k8s-cluster.yml,但忘了看kube-networking.yml里对应插件的详细选项。其实Kubespray的设计逻辑是:先把“插件类型”这个总开关定下来,再靠“插件级参数”去让插件适配你的环境。总开关决定你用谁,插件级参数决定它在你这里能不能活得舒服。

部署的时候还要特别注意inventory里的node分组。Kubespray默认把节点分成了kube_control_plane、etcd、kube_node等组。网络插件需要部署在部分或全部节点上,如果你自定义的node组名和默认角色对不上,可能导致网络组件没有在所有需要的节点上调度,集群虽然能起来,但某些节点的Pod网络就是不通。

所以我的建议是:拿到一个新环境后,第一件事不是去翻插件文档,而是先完整看一遍group_vars里的默认值和inventory分组。先知道“Kubespray打算怎么装”,再决定“我需要改成什么”。跳过这一步,后面很多操作就是在猜。

2.2 Calico、Cilium、Flannel在Kubespray下的真实差异

Kubespray支持的CNI插件不止一种,但实际生产环境里用最多的还是Calico、Cilium和Flannel这三类。它们之间的区别不能只看“功能谁多”,还要看你的内核、网络环境、团队维护能力。

维度FlannelCalicoCilium
部署复杂度低中较高
容器网络策略不支持(需额外组件)支持,基于iptables/ipset支持,基于eBPF更细粒度
默认数据面VXLAN或host-gwIPIP或VXLAN,配合BGPeBPF
内核要求低低至中要求较高,一般建议4.19以上
性能上限一般较高高,适合高并发低延迟场景
排障门槛低中高,需要理解eBPF和Hubble等

先说Flannel。它在Kubespray里的优势是简单,部署后基本不太需要动脑,适合纯粹的“快速起一个稳定可用的集群”。但它对NetworkPolicy基本无能为力,多租户隔离或者安全合规要求高的场景就不够用。如果你只是内部测试、跑跑CI,Flannel够用了。

Calico是Kubespray默认的插件,也是我个人的首选。它同时兼顾了路由和策略,BGP模式下能在大规模集群里走真正的三层路由,扩展性和性能都比较稳。Kubespray对Calico的封装也比较成熟,坑相对少。只要物理网络允许BGP或者IPIP/VXLAN,Calico都能找到合适的数据面。

Cilium的功能最强,eBPF带来的可观测性和安全能力很吸引人,特别适合做微服务治理、零信任网络这类场景。但它的前提条件也多:内核版本要够新,部分云平台对eBPF程序可能有限制,排障时需要的知识储备也不一样。我见过有团队兴冲冲上Cilium,结果在一个老内核的节点池上起不来,最后整组回滚到Calico。

2.3 插件版本和Kubernetes版本是配套的,别乱动

Kubespray的一个设计特点是,它在release时就把Kubernetes版本、插件版本和默认参数做了配对测试。你升级Kubespray版本时,网络插件版本通常也会跟着变。很多人喜欢在k8s-cluster.yml里把某个插件的image tag改成自己想要的版本,这在我看来风险很高。

网络插件版本和Kubernetes的CRD结构、API版本、组件启动参数都有兼容性约束。比如某个Calico新版本要求Kubernetes最低版本,否则CRD无法正常注册;或者反过来,Cilium的新版本默认启用了某个需要新版内核的功能,让老节点全部异常。这些都不是改一行配置能解决的。

如果你确实需要升级插件版本,我的建议是:先在一个测试集群上同步升级,跑完网络验证清单再动生产环境,而不是直接改tag重跑playbook。Kubespray社区已经帮你踩过一遍坑了,默认版本组合反而是最省心的组合。

3. 部署前先把网段、MTU、封装模式这三个变量定下来

这一节我想重点讲部署前最关键的规划项。一个Kubespray集群的网络是否稳定,很多时候在部署前就已经决定了,后面只是验证和补救。网段、MTU、封装模式,这三件事我每次部署都是提前确认好再动手。

3.1 Pod网段和Service网段:别和已有网络撞车

Kubespray默认的Pod网段是10.233.64.0/18,Service网段是10.233.0.0/18。这两个网段看起来不惹眼,但只要你所在的物理环境、云VPC或者其他系统用到了10.233.0.0/16这个大段里的任何地址,就有冲突风险。

为什么冲突会带来麻烦?因为K8s集群里的Pod访问外部IP时,如果目标IP恰好落在集群内部路由表里,数据包就会被本地的路由规则截走,转发给某个不存在的Pod网关,而不是发往物理网络。结果是:集群内部没问题,一访问某些外部服务就超时,表现极其诡异。

我在部署前一定会做的一件事是:把所有节点的物理网段、公司内网网段、其他关联系统的网段都列出来,确认和Pod网段、Service网段完全不相交。如果有交集,宁可多花点时间改掉Kubespray的默认值,也不要带着隐患上线。

同时要考虑集群规模。/18的Pod网段有大约16000多个地址,对于大多数集群够用。但如果你预期Pod密度极高、或者要跑大量StatefulSet,建议提前算好需要的地址量,必要时扩大到/16。注意,集群一旦建好,这个参数就不能随意改了。

3.2 MTU和封装模式:小包通、大包不通的根源

MTU问题是我最想强调的一个点,因为它特别容易伪装成其他问题。容器的网络数据包在跨节点通信时,如果插件选择了IPIP或VXLAN,会在原始报文外面再套一层。这意味着物理网络上传输的报文会比容器里看到的报文大一些。

假设物理网卡MTU是1500,IPIP封装会额外占用20字节,那么容器网卡的MTU最多只能设为1480,否则超过物理链路承载上限的报文会被静默丢弃。VXLAN的额外开销更大,通常是50字节左右,容器MTU建议设为1450或更低。如果插件配置没有正确适配,就会出现:小包(比如ping默认大小)能通,但数据库交互、HTTP大响应体等大包直接卡死或超时。

验证方法也简单:在Pod里执行ping -M do -s 1400和ping -M do -s 1500对面Pod的IP,小包通、大包不通基本就是MTU问题。

对于封装模式的选择,我一般按这个逻辑判断:

  • 所有节点在同一个二层域,可以关闭IPIP,直接走BGP或host-gw,性能最好。
  • 节点跨子网、跨可用区,用IPIP的CrossSubnet模式,或者直接VXLAN,避免依赖底层网络的路由协议。
  • 云平台环境,要确认安全组是否放行对应协议,IPIP是协议号4,VXLAN是UDP 4789。

3.3 网络策略、NodePort范围和负载均衡模式

除了网段和MTU,还有几个跟网络使用体验直接相关的参数需要提前想清楚。

enable_network_policy这个开关,打开后Calico会为带NetworkPolicy资源的命名空间做策略编排。如果你暂时用不到网络策略,可以不开,能少一些iptables规则和性能损耗。但如果你有安全隔离需求,就一定要开,并且事先选好策略模型。

kube_nodeport_range默认是30000-32767,如果你的业务需要对外暴露NodePort,建议提前和防火墙策略对齐。有些公司内部安全策略不允许对公网开放高位端口,那你可能需要调整这个范围,或者改用LoadBalancer方案。

Kube-proxy的模式也值得看一眼。较新版本的Kubespray支持配置kube_proxy_mode: iptables或ipvs。iptables更经典,排障也直观;ipvs在节点数和Service数较多时性能更好,但依赖ipvsadm和内核模块。我个人的习惯是,中大规模集群优先考虑ipvs,但前提是节点上相关模块已经加载。如果你不确定,iptables反而是最稳的选择。

4. 以Calico为例的Kubespray网络变量配置实操

前面讲的都是思路,这一节给出一份可以照着改的实际配置。我以Calico为例,因为它是Kubespray默认也是我推荐的首选。这里给出的内容基于常见实践,具体环境请以你的Kubespray版本实际变量名为准。

4.1 一份可以直接参考的Calico配置模板

在group_vars/k8s_cluster/kube-networking.yml里,我通常这样设置:

# 总开关:使用Calico CNI kube_network_plugin: calico # 是否启用网络策略 enable_network_policy: true # Calico IP自动分配方式,eth.* 表示匹配节点上以eth开头的网卡 calico_ip_auto_method: "interface=eth.*" # 容器网卡MTU,物理网卡1500且使用IPIP时为1480 calico_veth_mtu: 1480 # IPIP封装模式:Always / CrossSubnet / Never # 如果所有节点在同一子网,不需要IPIP,可以设为Never # 跨子网时建议设为CrossSubnet,只在跨子网流量上做封装 calico_ipip_mode: "CrossSubnet"

再看k8s-cluster.yml里对应的网段规划:

# Pod网段,要注意和现有网络不冲突 kube_pods_subnet: 10.244.64.0/18 # Service网段,也要避开已有网络 kube_service_addresses: 10.244.0.0/18

这一个地方容易困惑:很多人改了Pod网段,但没改Service网段,或者只改了其中一个,结果DNS和Service访问对不上。所有和网段相关的变量必须一起规划、一起改。

如果你的网络环境比较简单,所有节点都在同一个二层域,且愿意直接走主机路由,可以把IPIP关掉,MTU不扣封装开销,性能更好。相应的配置是calico_ipip_mode: "Never",calico_veth_mtu: 1500。但如果节点跨子网,这种配置就会出问题,因为裸路由在三层环境下不一定能互通。

4.2 修改网络参数后重跑playbook的正确姿势

Kubespray部署集群后,有时你会觉得网络参数不合适,想重新跑一遍playbook来调整。这比全新部署风险高得多。以我的经验,除非Changelog明确说支持原地修改,否则必须做足准备。

第一步,备份inventory和当前的网络插件配置。我习惯把group_vars整个目录复制一份带时间戳的备份,方便出问题时对比回滚。

第二步,重新运行部署命令。典型流程是:

ansible-playbook -i inventory/mycluster/hosts.yml cluster.yml -b -v

但要注意,不是所有参数都支持热更新。网段相关的参数基本没法原地改,改了之后集群上已有的Pod IP不会自动迁移,路由表和Calico的IP池之间会出现不一致。如果确实要改网段,我建议在维护窗口内做一次全新部署,或者把关键应用迁移出去后重置集群。

如果只是调整MTU、切换IPIP模式这类数据面参数,可以先修改配置,再逐步重启节点上的calico-node服务。不过也要注意:切换封装模式时,Calico的IPPool配置如果还是旧的backend,新配置不会自动覆盖。手动检查一下IPPool对象,必要时删除并重建池,再触发一轮滚动重启。

我踩过最惨的一次坑是:只改了calico_ipip_mode,没有检查已有IPPool,重启后有些节点的路由还在老模式上,新旧路由混在一起,跨节点通信断了大半天。

所以在重跑playbook之前,我的固定动作是:

  • 确认inventory里没有忘记更新节点角色和分组。
  • 对比本次配置变更,区分哪些参数支持热更新,哪些必须重建集群。
  • 至少要有一台可回滚的“金丝雀”节点或测试集群先试一把。

5. 部署完先按这份清单验证网络功能,别急着上业务

Kubespray运行结束不代表网络就绪。我见过太多次“集群起来了,一跑业务就出事”,其实很多问题在部署后一小时内用几个简单命令就能查出来。这里分享一套我每次部署后都会执行的网络功能验证清单。

5.1 从CoreDNS到Pod-Pod连通性的基础检查

部署完成后,先看最基础的三件事:CoreDNS是否正常解析、Pod之间能否互通、Pod到Service是否通。

先看CoreDNS的Pod状态和日志:

kubectl get pods -n kube-system -l k8s-app=kube-dns kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50

如果CoreDNS反复CrashLoopBackOff,多半是网络插件没有正常工作,DNS Pod拿不到正确IP或者节点之间无法通信。

再创建一个临时Pod来测试解析和连通性:

kubectl run test-dns --image=busybox:1.28 --rm -it --restart=Never -- sh

在Pod里执行:

nslookup kubernetes.default.svc.cluster.local wget -qO- http://kubernetes.default.svc.cluster.local

可以解析且能访问,说明CoreDNS到集群网络的链路基本没问题。

然后是跨节点Pod之间的连通性。由于我们创建的临时Pod可能恰好在同一个节点,为了避免假阳性,我一般会用一个DaemonSet或者通过nodeSelector把测试Pod调度到多个节点上:

kubectl get pods -o wide

在其中一个Pod里ping另一个节点的Pod IP。如果通了,再看一下MTU的影响,用不同包大小测一遍:

ping -M do -s 1400 <目标Pod IP> ping -M do -s 1500 <目标Pod IP>

5.2 Service、NodePort和外部访问的验证

Pod到Pod通了,不代表Service就一定正常。Kube-proxy是另一层逻辑,它负责把ClusterIP的流量转发到后端Pod。简单验证方式是:

kubectl get svc -A kubectl run test-svc --image=busybox:1.28 --rm -it --restart=Never -- sh # 在Pod里访问ClusterIP wget -qO- <ClusterIP>:<port>

如果wget超时,且Pod到Pod连通性是正常的,那问题基本出在kube-proxy的iptables/ipvs规则或者Service的选择器上。

NodePort的验证就更容易了:在集群外的一台机器上访问<任意节点IP>:<NodePort>。如果只在集群内通、集群外不通,优先检查节点上的防火墙端口、云安全组的入方向规则。

LoadBalancer场景比较复杂,取决于你的云环境和负载均衡实现。至少要到能够通过负载均衡IP访问后端Service这一步。

5.3 NetworkPolicy是否真的在起作用

如果你开启了enable_network_policy,不要只看到有NetworkPolicy资源就以为安全了。我习惯做一次正反验证:

创建一个带有app=test标签的Pod,再创建一个不带任何标签的Pod。配合一条只允许app=test访问的NetworkPolicy,验证:

  • 标签匹配的Pod访问目标:通。
  • 不匹配的Pod访问目标:不通。

如果两条结果都符合预期,说明策略生效链路是完整的。如果“不通”没有出现,说明网络策略可能没被插件加载,或者还有默认允许规则没有清理。

这里也提醒一句:NetworkPolicy的验证本身要小心,别在正式业务命名空间里做。我一般会在一个临时namespace里验证完再删掉。

6. 跨节点Pod不通的排查链路:从路由表到防火墙逐步走

前面讲了很多规划性的内容,但网络问题最大的考验还是事发时的排查能力。这一节我用一个真实遇到过的场景来演示完整的排查链路,方便你在自己的环境里复现同样的思路。

6.1 场景现象:集群正常但跨节点Pod访问时好时坏

某次部署完成后的环境是三节点集群,Calico网络。kubectl get nodes全部Ready,CoreDNS也起来了,但业务反馈:在不同节点上的Pod之间互相访问,有些IP能通,有些一直超时,CoreDNS偶尔还会丢几个包。

这种“一半通一半不通”的状态是很典型的。它不是整体网络坏了,而是某种特定条件下数据包走不通。最常见的条件分两类:跨子网时封装模式不对、或者底层网络对特定协议放行不一致。

6.2 从数据包的路径逐段定位:路由、隧道、防火墙

第一步,看路由表。在问题节点上用ip route查看目标Pod网段是怎么走的:

ip route | grep 10.244

如果看到类似10.244.x.x via 10.244.y.y dev tunl0这样的记录,说明Calico把跨节点流量交给了IPIP隧道设备tunl0。接下来就需要验证隧道本身是否通。

第二步,在源节点和目标节点各开一个tcpdump:

tcpdump -i any host <源Pod IP> and icmp

在Pod里ping目标Pod IP,观察源节点是否把ICMP报文发出去了,目标节点是否收到。如果源节点发出了,目标节点的物理网卡完全没收到任何IPIP协议报文,那问题多半发生在节点之间的网络传输层。

第三步,看calico-node的运行状态和日志:

kubectl logs -n kube-system -l k8s-app=calico-node

如果BGP peer状态异常、Felix启动失败,日志里会直接体现出来。但很多节点间丢包问题是发生在Kubespray部署范围之外的,比如云平台安全组、机房防火墙、交换机ACL。

第四步,检查iptables。虽然Calico的流量管理很复杂,但基础的FORWARD链如果被配置成DROP,跨节点转发也会失败:

iptables -L FORWARD -n

注意查的时候不要只盯系统默认链,还要看cali-FORWARD这类Calico自定义链。如果发现流量在自定义链里被drop,可以进一步用iptables -L cali-FORWARD -n -v看计数器。

6.3 根因定位:安全组没放行IPIP协议

前面排查到“源节点发出、目标节点没收到”,基本就把范围缩小到underlay网络了。在云平台上,最常见的原因就是安全组规则只放行了TCP、UDP、ICMP,没有放行协议号4(IP-in-IP)。

判断方法很直接:在两台节点上分别执行:

tcpdump -i eth0 ip proto 4

然后在源节点ping目标节点上的Pod IP。如果源节点tcpdump能看到IPIP协议包,目标节点tcpdump却什么都没有,那就可以确定是中间网络丢包。再配合检查安全组规则中是否放行了IP-in-IP(协议号4),就能复现根因。

修复方式是放行安全组/防火墙中协议号为4的入方向和出方向流量。如果不想放行IPIP协议,另一个思路是把Calico改成VXLAN模式,走UDP 4789端口,这通常比IPIP更容易被传统防火墙支持。

6.4 同一个场景下的变种:MTU和rp_filter也常背锅

排查过IPIP协议后,我还想提两个容易混淆的变种问题。

第一个是MTU导致的大包不通。现象和“跨节点丢包”很像,但细节不同:ping -s 1400通,ping -s 1500不通。如果你已经把安全组放行协议4都检查完了还没找到原因,一定测一下不同包大小。很多云平台上虚拟网卡的默认MTU是1500,IPIP封装后容器MTU必须下调,否则就会出现这种情况。

第二个是rp_filter(反向路径过滤)。Linux内核默认在/proc/sys/net/ipv4/conf/all/rp_filter里配置了过滤规则。如果一个多网卡节点收到一个源地址来自“非最佳路径”的包,可能被直接丢弃。表现也是跨节点通信丢包,但你在tcpdump里能看到包到达了节点、却迟迟没有回包。遇到这种情况,检查各网卡的rp_filter设置,必要时改成2(宽松模式)或者1(严格模式但配置正确)。

7. 最后说几句network-pro的实践心得

回到标题里的network-pro。我理解的pro不是指“比别人多装了一个牛逼插件”,而是指网络配置这件事要做得有章法、可预测、可维护。Kubespray虽然把部署过程自动化了,但它不会替你做网络规划,更不会替你避免所有环境差异带来的坑。

我在自己的项目里总结了几条固定习惯。第一,每次部署前,把网络相关参数单独摘出来做成一份变更记录,标明改了什么、为什么改、可能影响什么。第二,任何网络参数的调整都要先在测试环境跑一遍验证清单,再上生产。第三,尽量别在一个正在业务的集群上折腾网络插件,插件和数据面切换的坑远比收益更频繁。

Kubespray的网络部分之所以值得单独拿出来讲,是因为它的默认值确实能让你“快速跑起来”,但从快速到稳定之间,隔着的正是这些不起眼的规划、验证和排障习惯。如果你现在正准备部署一个集群,先花半天把网段、MTU、封装模式、安全组规则这些底层细节理清楚,后面会少熬很多个凌晨。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询