
1. Kubespray 网络层先把要解决的问题边界划清楚用 Kubespray 交付 Kubernetes 集群网络从来都不是“装完再看”的部分而是从第一步选变量开始就要定下来的地基。很多第一次用 Kubespray 的团队会把注意力放在 inventory、SSH 连接、镜像拉取这些环节结果集群装完Pod 也起来了却发现跨节点互访不通、Service 负载均衡异常、甚至 DNS 都解析不稳定。这些问题的根源绝大多数都出在 Pod 网络和节点网络之间的匹配关系没处理好。Kubespray 本身是一个 Ansible 实现的 Kubernetes 部署工具它把所有机器编排成 control-plane 和 worker再通过各组件之间的模板变量把 CNI、kube-proxy、kubelet 等配置发散到每个节点。网络相关的默认值能开箱装出一个集群但默认值适合的是单网段、小规模、纯测试环境。一旦你的节点分布在多个子网或者底层网络开了巨型帧、带了防火墙策略原来的默认参数就可能失效。这篇博文覆盖的范围包括Kubespray 里 CNI 怎么选、Calico 的 VXLAN/IPIP/BGP 模式在什么场景下用、kube-proxy 的 iptables 和 IPVS 怎么选型、部署前需要从底层网络收集哪些信息、实际部署时改哪些变量以及上线后遇到网络问题怎么顺着链路去查。适合三类人一是准备用 Kubespray 上生产集群的运维二是部署常见问题反复出现的平台工程师三是想理解 Kubernetes 网络数据通路的开发。如果你只是想要一条命令装完集群这篇不一定适合因为你会在里面看到很多“为什么不直接用默认值”的讨论这才是真正省时间的部分。为什么网络值得单独拿出来讲Kubernetes 的数据面由四层组成物理机之间的节点网络、跨主机通信的 Pod 网络、提供虚拟 IP 的 Service 网络、以及外部流量进入集群的入口网络。Kubespray 可以配置的部分主要在前三层但第一层节点网络完全靠你现有的底层设施它不负责管。这意味着你必须把底层网络的拓扑、MTU、防火墙规则弄明白后才能决定上层用什么封装模式这就是为什么直接默认安装经常埋雷。2. 网络插件选型Kubespray 为什么把 Calico 放在默认位置2.1 Kubespray 支持哪些 CNI各自有什么脾气Kubespray 从设计上支持多种 CNI 插件部署的时候只需要在 k8s-cluster.yml 里改一个 kube_network_plugin 变量就会把对应插件安装到集群。不同插件背后的数据通路完全不同选型时不能只看宣传性能要看你的团队对哪种技术栈最熟悉。CNI 插件数据通路特点适合场景明显短板Calico支持 BGP、VXLAN、IPIP可配合 eBPF生产环境中大型多子网集群对网络节点、IP 规划要求较高FlannelVXLAN/UDP 为主后接 host-gw起步阶段最简单的 Overlay 集群功能单一NetworkPolicy 支持弱CanalFlannel 数据面 Calico 策略面既要简单又要策略隔离多一层组件排查链更长Cilium基于 eBPF可完全替换 kube-proxy对性能和可观测性要求极高的环境内核版本和运维门槛比较高Weave自定义封装内置 DNS中小规模快速搭大规模场景性能上限偏低这个对比不是纸上谈兵。我见过 Flannel 在较大规模集群下频繁出现 VXLAN 隧道重建引发的连接抖动也见过 Cilium 在旧内核上直接拒绝加载 BPF 程序。选择 CNI 之前先确认你的内核版本、节点网段规划、以及排障团队的技术储备这比任何 benchmark 都重要。2.2 默认给 Calico 背后的逻辑Kubespray 默认 kube_network_plugincalico不是随随便便选的。Calico 最大的特点是数据通路不依赖 Overlay 也能工作三层路由的方式让 Pod 流量直接走物理网络路由表理论上更接近裸机性能。即使要 Overlay它也能在 VXLAN 和 IPIP 两种封装之间切换给不同网络条件留了退路。Calico 的另一个优势是原生支持 NetworkPolicy而且策略在数据面执行不需要额外跑一个 DaemonSet 来代理流量策略。对于有合规需求、需要做命名空间隔离的集群NetworkPolicy 几乎成了硬性要求Calico 在这条路上比 Flannel 成熟得多。运维侧的变量体系也很完整IP 自动检测、Felix 启动参数、MTU、后端模式都能配置遇到底层网络变化时不一定非要换 CNI。所以我个人建议除非团队里有明确的反向理由否则保留 Calico 默认值把时间花在把模式调对这件事上。3. Calico 的三种数据模式VXLAN、IPIP、BGP 的取舍3.1 一次跨节点访问数据到底走了什么路径要理解三种模式的区别需要先看懂一次 Pod 跨节点访问的完整路径。假设 node1 上的 Pod A 要访问 node2 上的 Pod B在 Calico 的 BGP 模式下流量从 Pod 出来经过 veth 对进入 node1 的 root namespaceCalico 在每台节点上通过 BIRD 或内部 BGP 组件与相邻节点建立 BGP 会话把自己的 Pod 网段通告出去。这样 node1 就知道去访问 PodB 应该把包发给 node2 的物理 IP流量直接走物理网络三层路由不做任何封装。代价是节点间的底层网络必须允许这些路由可达如果有防火墙挡住相关端口或者交换机禁止 BGP 协议这套模式就起不来。VXLAN 模式则是把数据包封装进 UDP 里外层 UDP 的端口是 4789封装后的包可以穿越三层网络底层网络只要允许普通 UDP 流量就行不需要额外放开 BGP 端口。每个节点上由 Felix 管理 VTEP 接口负责维护 VXLAN 隧道的地址信息。代价是每个包多了约 50 字节的封装开销而且要经过内核封包解包的过程延迟和吞吐都比裸路由略差。IPIP 模式介于两者之间它用 IP in IP 的方式把内部 IP 报文直接封装进一个新的 IP 报文没有 UDP 头和端口概念内核处理相对简单多出的开销大约是 20 字节。不过 IPIP 在某些网段和防火墙环境下会被特殊对待比如一些虚拟网络不支持 IPIP 协议所以现在用 IPIP 的新集群反而比前几年少了。这三种模式一句话总结网络通达性最好、延迟最低的是 BGP要穿越限制最多、兼容性最强的是 VXLANIPIP 像是一个只能用在特定网络环境里的折中方案。实际选型时我一般先问一句“底层网络允许我放行 BGP 吗”允许就 BGP不允许就 VXLANIPIP 只在明确知道支持它的时候才碰。3.2 在 Kubespray 里如何切换模式Kubespray 里控制 Calico 后端模式的变量不同版本名字有些变化但思路一致。常见的是 calico_mode 或 calico_network_backend取值通常有 bird对应 BGP和 vxlan对应 VXLAN。实际使用时长这样# inventory/mycluster/group_vars/k8s_cluster/k8s-net-calico.yml calico_mode: vxlan切换之后Calico 的 DaemonSet 会重新配置 felix节点上的 BIRD 进程是否运行也由该变量控制。如果你从 BGP 切到 VXLAN旧的路由通告会停止节点之间改用 UDP 隧道通信。我建议每次切换后用下面几条命令验证节点是否 Ready、Calico Pod 是否 Running、以及节点上的路由表是否已经切换成预期的形式kubectl get nodes -o wide kubectl get pods -n kube-system | grep calico ip route | grep bird ip route | grep vxlan验证这一步不要省。模式切换后路由表不会立刻变干净因为旧路由条目可能存在一段时间尤其在有大量存量 Pod 的集群里路由缓存会影响实际选路重启相关网络组件或者说把问题 Pod 重建一下都是必要的。3.3 IP 自动检测最容易忽略的隐藏地雷Calico 需要知道每台节点“自己是谁”即从哪个接口和 IP 通告拓扑。默认的 IP 检测策略是 first-found也就是找到第一个有效 IP 就拿来用。这个策略在只有一张网卡的节点上非常省心但在多网卡机器上就是灾难。云服务器常见的管理网卡、数据网卡、存储网卡共存万一 first-found 选到了存储网卡或内部管理网段Pod 流量会往一条物理上根本走不通的链路发现象就是 Calico 运行正常但跨节点 Pod 怎么都 ping 不通。解决办法是指定检测方法。Kubespray 里有 calico_ip_auto_method 这个变量你可以在部署时改成让 Calico 去访问一个可达地址来确定主 IP也可以直接手动指定接口。推荐下面这种写法calico_ip_auto_method: can-reach192.168.1.1这个 192.168.1.1 换成你节点的网关地址Calico 会主动探测到这个地址的路由从而选出生成默认路由的那张网卡。这比你用正则匹配接口名更稳健因为接口名在不同厂商的操作系统上差异太大而网关地址是网络管理员最确定的信息。4. kube-proxy 模式iptables 与 IPVS 不只是性能之争4.1 Service 的虚拟 IP 是哪里来的Kubernetes 的 Service 是一个虚拟概念它没有网卡也没有真实进程监听。为每个 Service 分配的 ClusterIP 之所以能访问是因为每个节点上 kube-proxy 在维护着从 ClusterIP 到后端 Pod IP 的转发规则。这里有两个经典实现iptables 和 IPVS。iptables 的实现方式是链式匹配。每个 Service 对应一串规则每个 Endpoint 又对应一条 DNAT 规则。流量到 ClusterIP 后内核在 PREROUTING 链里依次匹配命中的规则把目标地址改成某个 Pod IP。这个设计在 Service 数量少、Endpoint 数量少的时候没问题但当集群里有几百个 Service 和几千个 Endpoint 时iptables 的规则会变得非常长新建一条连接需要遍历的规则数量呈线性上升CPU 开销和延迟都会变得难看。IPVS 的实现则完全不同。它运行在内核空间使用哈希表存储转发规则查找复杂度是 O(1)不随规则数量线性增长。IPVS 支持多种调度算法比如轮询、最少连接等而且在转发性能上比 iptables 更稳定。从运维角度看IPVS 模式还有一个排查优势你能用 ipvsadm -Ln 直接看到某条 Service 的调度策略和后端列表比去数 iptables 规则直观得多。对比项iptablesIPVS规则存储链表顺序匹配哈希表查找复杂度随规则数上升O(1)调度算法基本随机/轮询支持多种算法排查手段iptables-saveipvsadm -Ln内核模块依赖无额外要求需要 ip_vs 模块4.2 Kubespray 里的切换与验证Kubespray 默认的 kube_proxy_mode 是 iptables你可以直接切到 ipvs修改位置在 k8s-cluster.ymlkube_proxy_mode: ipvs切换后kube-proxy 组件会以 ipvs 模式启动但它仍然需要 iptables 处理一小部分特殊情况比如 ExternalIP 和健康检查流量。节点上需要装载 ip_vs 相关内核模块Kubespray 会自动做 modprobe但如果你的内核被裁剪过模块加载可能失败这属于部署时容易掉链子的点。验证方式如下kubectl -n kube-system logs daemonset/kube-proxy --tail20 ipvsadm -Lnipvsadm 输出里能看到 ClusterIP 对应的 Virtual Service以及底下的后端列表。如果没有任何输出而集群里有 Service 存在先检查 ip_vs 模块是否加载再检查 kube-proxy 日志。切到 IPVS 不代表 Service 问题全消失后端 Pod 异常、协议不匹配、会话保持策略等问题在 IPVS 模式下同样存在只不过排查入口更清晰。4.3 什么时候你反而该留 iptablesIPVS 不是万能解药。如果你的集群规模很小、Service 数量只有几十iptables 完全够用而且它对所有 Linux 内核版本的兼容性几乎是最好的不需要额外确认模块和版本。如果团队对内核模块有洁癖不希望引入新的 netfilter hookiptables 是更保守的选择。另外某些老版本内核里 IPVS 对 IPv6、会话保持或自定义 DNAT 行为的支持不完整这些边界情况要提前摸清。我的经验是50 个节点以内、Service 数不超过 200iptables 用起来没毛病超过这个量级再考虑 IPVS不要让模式选型变成伪优化。5. 从默认值到可上生产的网络参数一次实际部署记录5.1 部署前必须从底层网络收集的信息动手部署之前我习惯先画一张简单的网络拓扑图哪怕是手绘的也可以。图上至少要标注这几个信息所有节点的管理网段、业务网段、网关 IP、节点之间的 MTU 是否一致、防火墙是否拦截了相关端口、有没有三层交换机支持 BGP。不要相信面板上显示的“网络很健康”实际拿 ping、mtr 或者直接 ping 大包验证一下才知道。MTU 对齐是很容易忽略的点。如果底层网络允许的 MTU 是 1500而你选择了 Calico VXLAN 模式隧道封装会多出约 50 字节的开销此时默认的 Pod 网卡 MTU 就得相应调小否则大包在物理链路上被分片流量时好时坏。这个问题表现为 ping 小包正常、curl 大响应超时、某些服务偶发断流。Kubespray 里有变量可以调整 Calico 的隧道和相关接口 MTU建议把它和底层实际 MTU 对齐。5.2 修改核心变量以下列举的变量位置在 inventory/mycluster/group_vars/k8s_cluster/ 下文件名和具体变量在不同版本中略有差异但都集中在 k8s-cluster.yml 和 k8s-net-calico.yml 两个文件里。# k8s-cluster.yml kube_network_plugin: calico kube_service_addresses: 10.233.0.0/18 kube_pods_subnet: 10.233.64.0/18 kube_proxy_mode: ipvs # k8s-net-calico.yml calico_mode: vxlan calico_ip_auto_method: can-reach192.168.1.1kube_service_addresses 和 kube_pods_subnet 是网段规划需要确保它们与分析好的集群网段不冲突尤其不能和节点物理网段重叠。如果重叠Kubernetes 内部路由会和节点路由打架这种情况极难排查。一次我在交付时把 Pod 网段规划成 10.233.64.0/18而某个机房的业务 VLAN 恰好也用了 10.233.0.0/16结果业务流量被路由到 Pod 网段出不去最后整体重配才恢复这个教训值得记下来。5.3 运行 Playbook 后的验证清单配置完变量后按 Kubespray 标准流程执行ansible-playbook -i inventory/mycluster/hosts.yml cluster.yml -b这一步跑完集群基本就绪。验证网络这一层我按以下顺序执行确认节点 Ready且每个节点上的 Calico 和 kube-proxy Pod 都是 Running。创建两个位于不同节点的测试 Pod分别进入后互相 ping确认 Pod CIDR 路由生效。创建一份带多个副本的 Deployment再用 ClusterIP Service 暴露反复访问 Service IP观察负载是否分到不同副本。用 ipvsadm -Ln 查看 Service 后端列表确认 Endpoint 实时同步。如果开了 NetworkPolicy额外在命名空间里加一条拒绝规则验证策略能立即阻断。这个清单看起来简单但能覆盖大部分网络问题的风险面。我见过不少团队只验证到节点 Ready 就宣称集群可用结果业务部门上线当天就发现跨节点 Service 不通。6. 常见网络问题速查现象、原因、处理步骤6.1 Calico Pod 一直重启或未就绪如果 Calico felix 一直 CrashLoopBackOff先不要急着改配置。看日志是第一步kubectl -n kube-system logs ds/calico-node | grep -i error常见原因有两个一是节点 IP 检测错导致 felix 拿不到正确地址二是 API Server 连接异常。检测 IP 错的按前面说的方式指定 calico_ip_auto_method如果是 API 连接问题检查节点到 apiserver 的连通性和证书有效期。还有一个细节不同节点如果系统时间偏差过大会导致集群内证书校验不定期失败网络组件表现成间歇性重启所以校准时间也是网络排查的一部分。6.2 Pod 跨节点不通同节点却正常这种问题基本可以锁定在 Overlay 或路由通告上。如果你用 BGP 模式先看 BGP Peer 是否建立如果你用 VXLAN先看外层 UDP 4789 是否被防火墙放行。一个实用操作是在源节点和目标节点上分别抓包tcpdump -i any udp port 4789 -n没有包说明封装或路由有问题有包但回不来再查返回路径的防火墙和路由表。BGP 模式下还要确认节点的路由表里有没有对端 Pod 网段的路由如果只有本机 Pod 路由说明 BGP 会话没有把网段通告过来优先看 BGP Peer 状态和交换机侧策略。6.3 Service IP 能访问但业务报连接重置这类问题多数不是 Service 本身的问题而是 kube-proxy 的转发与实际 Pod 的健康状态脱节。先看 Endpointkubectl get endpoints确认 Service 后端是否都在。如果 Endpoint 存在但访问时连接被 RST多半是后端 Pod 监听端口不匹配或者就绪探针没通过但 Endpoint 还残留在缓存里。把 kube-proxy 切成 IPVS 模式后用 ipvsadm -Ln 能直接看到后端列表配合 tcpdump 看 SYN 包发给了谁很快就能定位。另一种隐蔽情况是 Pod 内的服务只监听 IPv6 地址而 Service 配的协议族是 IPv4这种情况下连接也会表现为被拒绝。6.4 大包超时、小包正常这是 MTU 不一致的典型症状。推荐先把问题固定成可复现的实验ping -M do -s 1472 -c 3 对端Pod IP如果 1472 字节能通而 1500 以上不通说明中间链路 MTU 小于报文长度。解决办法是把集群内 Calico 的 MTU 调小到与底层一致。调完之后还要清理已存在的连接因为连接建立时的 MSS 协商结果不会自动更新最稳妥的办法是重启相关工作负载或对测试 Pod 重新创建进行验证。6.5 节点多网卡导致 Pod 网段路由走错多网卡机器经常遇到现象是 Pod 同节点互通、跨节点完全不通或者外部访问 Service 来回路径不一致。解决办法就是在部署前把 can-reach 或接口指定清楚并保证路由表中默认路由对应的网卡与预期一致。如果已经部署完才发现问题可以改变量重新跑一遍相关角色的 playbook不用整集群重建。这里要特别提醒不要依赖 eth0/eth1 这样的接口名做选择因为系统重启后接口名顺序可能改变用 IP 探测或稳定的业务网段来指定更靠谱。6.6 常见问题速查表现象可能原因优先排查项Calico 未就绪IP 检测错/API 不通日志、路由表跨节点 Pod 不通防火墙/BGP 未建立/路由缺失隧道端口、BGP 状态Service 连接被重置Endpoint 失联/健康检查失败endpoints、ipvsadm大包超时MTU 不匹配ping -M do -s 1472网络时通时不通多网卡路由此/链路聚合不均路由表、mtr这些排查技巧都是从真实故障里提炼的。在问题最严重的时候反而要先停下来梳理数据链路每一跳的协议而不是一连串地重启和重装。7. 部署完之后的几个收尾动作集群装好之后还有几个网络相关的动作建议补上。第一把 Calico 的 Node 信息导出来核对一遍确保每个节点都用自己的业务地址注册到集群而不是随便一个管理网卡地址。第二在 CoreDNS 里加一条 hosts 记录把集群内部常用的服务域名提前写好能减少后续业务联调时对 DNS 排障的焦虑。第三如果计划用 LoadBalancer 类型的 Service提前确认上层负载均衡设备到节点 NodePort 网段的连通性否则这个能力上线后会被卡在物理网络策略上。还有一个多数人会忽略的地方网络组件的资源限制。Calico 和 kube-proxy 的 Pod 默认资源请求不高但在节点数量增长后felix 的 CPU 和内存消耗会上升。建议在交付文档中标注当前集群规模下的资源消耗基线后续扩容时对比这个基线提前发现网络层的性能拐点。如果团队还没有专门的网络监控至少先把节点层面的网络指标收起来比如每秒包量、每秒连接数、TCP 重传率。这些指标在出现问题前会给出早期信号比如重传率突然上升往往意味着底层链路开始不稳定早一步发现就能避免大面积业务抖动。8. 个人最后的一点体会按个人经验Kubespray 网络配置的一条主线是先画拓扑再选数据模式最后调参数。画拓扑是最容易被跳过但最值得花时间的因为你只有知道底层网络长什么样才能判断 BGP 是否可用、MTU 该设多少、IP 检测该指向谁。选数据模式时不要被性能测试数据冲昏头先保证功能可用、可维护、可排障再优化性能。调参数时每次只改一个变量改完先验证通不通再继续这个习惯能省下大量排查时间。最后再分享一个小技巧如果条件允许部署前准备一台与集群节点网络环境一致的测试机把规划好的 Pod 网段、Service 网段先用大包测试和路由检查试一遍确认不冲突、不丢包。这个动作十分钟就能做完但能规避掉部署后绝大部分的网络翻车场景。Kubespray 的网络配置从来不是一劳永逸的工程它更像是对物理网络环境的一次映射只不过这份映射是写在 yaml 文件里的。