资讯动态

kube-proxy性能优化:从iptables切换到IPVS解决大规模集群转发瓶颈

发布时间:2026/10/21 5:32:00 来源:尧图企业网站定制
做过Kubernetes集群运维的人几乎都遇到过这么一种情况集群规模不大时一切顺手Service数量一过几百上千业务侧开始报“接口偶发超时”抓包又抓不到明显异常最后查下来很大概率问题出在kube-proxy的转发链路上。Kubernetes默认的kube-proxy使用iptables模式做ClusterIP到后端Pod的DNAT转发也就是大家常说的路由转发。iptables本身没有错但当规则数量涨到几千上万条时这种链式匹配的转发方式就会成为集群的瓶颈。把转发模式从iptables换成IPVS是解决这个问题最直接、收益最明显的手段之一。这篇文章就围绕这件事展开iptables模式到底卡在哪、IPVS和它的原理差异、切换前要做什么准备、怎么切换、以及切换之后哪些地方容易翻车。不管你是用kubeadm搭的集群还是二进制方式部署的集群都可以照着操作。1. Service数量过千之后iptables那条“链”为什么会拖垮转发1.1 ClusterIP真正的转发者不是kube-proxy而是内核规则在Kubernetes里Service是一个虚拟概念ClusterIP本身也没有对应任何实体网卡。你ping ClusterIP的时候实际收到响应的是某个后端Pod的数据面。但“把发往ClusterIP的包交给某个Pod”这件事并不是控制器直接做的。kube-proxy只负责一件事持续监听Service和Endpoints的变化然后把变化同步到内核的转发规则里。真正每时每刻在处理数据包的是内核里的iptables规则或者IPVS虚拟服务器。理解这个分工很重要。很多人以为是kube-proxy在转发流量其实kube-proxy只是规则的“写入者”流量根本不会经过它。也正因为这样转发路径上的性能消耗完全取决于内核里规则的结构和匹配方式kube-proxy进程本身的内存与CPU反而通常不是瓶颈。在iptables模式下每当有新Service创建kube-proxy就会在NAT表里生成一组规则。一条Service的规则不是孤立的它会和已有的规则串在同一条链路上成为一个顺序匹配的检查点。服务数量越多这条链路就越长每个数据包经过时需要过的“关卡”也就越多。1.2 iptables模式最难受的两个地方线性匹配与全量刷新iptables模式的问题主要集中在两点。第一是线性匹配。一个数据包进入netfilter的NAT链之后会顺着规则一条一条往下匹配直到命中。这有点像排队过安检队伍越长平均等待时间就越长。虽然内核在匹配上做了不少优化但规则太多时平均匹配次数依旧会随Service数量线性上涨。当集群里有上千个Service每个Service背后又有多个Endpoint时NAT表里可能积累数千甚至上万条规则转发延迟和CPU开销会明显跟着上涨。第二是全量刷新。早期版本的kube-proxy在任意Service变化时会重建所有iptables规则后来的版本虽然做了部分优化但整体上仍然是“规则集重写”的模式。每一次规则重写都是把几千条链拆了再装在高并发集群里很容易出现瞬时抖动。Service变化越频繁这种抖动就越明显。社区里被反复讨论的一个案例是这样的压测时集群Service数量到800左右网络吞吐还能接受到1500之后新连接建立的成功率开始波动部分节点日志里能看到连接跟踪表满的报错。这就是线性规则和连接跟踪表两个问题叠加到了一起单纯加节点规格很难根治。1.3 什么时候该动了三个信号不是所有集群都需要切IPVS。10个Service的集群iptables和IPVS体验不出差别。但出现下面几个信号的时候就该认真考虑切换了观测面板显示节点CPU在Service变化期间出现尖峰或者压测时网络转发延迟随Service数量明显上升。节点上的iptables规则数量达到几千条以上或者系统日志里频繁看到netfilter相关表满、丢包的提示。集群正在或准备扩大规模或者应用层有大量短连接请求需要更低的建连延迟和更稳定的转发表现。如果你已经对上了其中一两条继续看下一节——IPVS的转发模型是如何绕开这些痛点的。2. IPVS能替代iptables的核心底气哈希查找与调度算法2.1 IPVS的本质Linux内核里的四层负载均衡转发器IPVS全称是IP Virtual Server是Linux内核自带的一个四层负载均衡模块也是LVS项目的核心部分。早年在传统物理机时代做负载均衡方案时IPVS就是很常用的底层技术。它能直接处理基于IP和端口的流量转发效率非常高。kube-proxy的IPVS模式做的事情本质上就是把Service当成一个“虚拟服务器”、把后端的Pod当成“真实服务器”来管理。kube-proxy通过内核的netlink接口把Service和Endpoint同步成一组IPVS虚拟服务器规则。数据包到达节点后IPVS在内核态直接完成负载均衡决策和转发不需要逐条匹配用户态规则。2.2 规则的组织方式链表和哈希表决定了数量拐点iptables底层规则存储在链上靠顺序遍历。IPVS则把虚拟服务器放在哈希表里数据包根据目的IP和目的端口直接计算哈希一次定位到对应的虚拟服务器查表的开销基本是常数级。这个差异就是“数量拐点”的来源。iptables模式在Service数量少的时候没什么问题可一旦规则集膨胀线性查表开销就变成实实在在的转发延迟。IPVS模式直接从数据结构层面解决了这个问题。这也是为什么Kubernetes官方很早就把IPVS模式称作大规模集群的推荐方案。一个可以参考的经验数据是当Service数量到了几千级别IPVS模式下的转发延迟通常比iptables模式有可感知的改善具体数值取决于节点规格和流量模型但趋势是明确的。2.3 不只是rrIPVS带来的调度策略和会话亲和能力iptables模式的转发逻辑很直白从Endpoints里选一个目标做DNAT基本属于静态转发。IPVS则带了一整套调度算法在Kubernetes里可以通过kube-proxy配置指定。调度算法说明适用场景rr轮询请求平均分配给每个后端后端处理能力相近时使用kube-proxy默认wrr加权轮询按权重分配流量后端规格不一时可通过权重倾斜流量lc最少连接选当前活跃连接数最少的后端长连接为主、连接分布不均衡时wlc加权最少连接权重与连接数都需要考虑时sh源地址哈希同一源IP固定到同一后端需要基于源IP的会话保持时dh目标地址哈希目标地址维度有亲和需求的场景kube-proxy默认使用rr在大多数场景下简单够用。如果业务的连接时长差异很大比如有些连接几秒就结束有些连接保持很久可以试试lc类算法。这个能力是iptables模式没有的。切换IPVS后如果发现后端Pod负载不均匀优先检查调度算法是不是选对了。除此之外IPVS还能针对虚拟服务器配置持久连接。kube-proxy暴露的配置没那么细但通过IPVS本身的会话保持能力可以实现基于源IP的连接亲和。相比iptables模式只能靠单一DNAT决策这里的选择余地大了很多。2.4 一个容易误解的点切到IPVS不等于彻底告别iptables这是很多资料不会强调、但你必须知道的一点kube-proxy切换到IPVS模式后节点上仍然会保留一部分iptables规则。因为IPVS只负责负载均衡和转发像地址伪装、SNAT、对外部流量策略为Local的Service做健康检查、处理LoadBalancer类型的Service等场景仍然需要iptables参与。严格说IPVS模式是“IPVS做主体iptables做辅助”。所以切换后如果有同事问“为什么切了IPVS还有这么多iptables规则”不要奇怪。判断切换是否成功的标准是核心Service的负载均衡和转发决策是否已经由IPVS虚拟服务器承担而不是看节点上有没有iptables规则。如果用ipvsadm看到的规则数和集群Service数量对得上那基本就是有效切换。3. 动手切换前先把内核和集群的底摸清楚3.1 内核版本与模块红线IPVS是内核模块不是kube-proxy里自带的用户态实现。内核层面不支持怎么配都白搭。先确认内核版本。一般建议内核版本不低于4.19当前主流发行版基本都满足。老内核不是不能用但部分模块命名和功能表现有差异比如连接跟踪模块在3.x内核下是nf_conntrack_ipv44.19之后统一为nf_conntrack。实际操作中要按当前系统的模块名来加载。如果集群里有节点还跑着旧内核建议先升级内核到4.19或更高版本再做切换。其次确认内核模块是否存在常用的几个是ip_vs、ip_vs_rr、ip_vs_wrr、ip_vs_sh、nf_conntrack。可以在节点上执行modprobe命令逐个验证。如果某个模块加载失败多半是内核没编译该模块或发行版精简过内核这时候需要先解决内核模块问题否则kube-proxy启动后会自动回退到iptables模式而且不会出现特别刺眼的报错。3.2 kube-proxy三种部署形态下的切换入口切换方式取决于kube-proxy是怎么部署的主流有三种kubeadm部署kube-proxy以DaemonSet运行配置集中在名为kube-proxy的ConfigMap里。只需要修改ConfigMap中的mode字段再滚动重启DaemonSet就能生效。二进制部署kube-proxy通常作为systemd服务运行启动参数在unit文件里把--proxy-modeiptables改成--proxy-modeipvs重启服务即可。云厂商托管集群如果控制面不开放kube-proxy配置通常只能依赖云厂商提供的开关或工单支持不建议自己去动节点上的kube-proxy文件否则可能被托管侧覆盖。动手之前先花两分钟确认自己的集群属于哪一类。不少朋友照着网上的命令跑去改ConfigMap结果自己是二进制部署的集群改完根本不起作用。反过来也有。这一步排查成本很低但很重要。3.3 切换前排雷清单与回滚预案切换前确认几件事所有节点内核版本一致避免部分节点模块加载失败导致行为不一致。记录当前iptables模式下网络是否正常留存基线和异常日志样本方便切换后对比。排查集群里有没有手工添加的iptables规则比如自定义的NodePort映射、额外的转发规则这些在切换后可能被辅助iptables规则影响。准备回滚方案IPVS模式切回iptables方式和切换一致只需把mode改回来再重启。只要不是老版本里已知的配置迁移问题回滚是安全的。还有一点切换不是零风险的。最稳妥的做法是先挑一两个非核心节点手动改kube-proxy配置观察一段时间再全量滚动。所有验证动作都建议放在业务低峰期同时保持监控面板打开重点盯kube-proxy日志、内核dmesg、节点网络延迟三个维度。4. 实操从iptables模式切到IPVS模式的完整过程4.1 kubeadm集群改ConfigMap是正路如果你的集群是用kubeadm搭建的kube-proxy的配置都在ConfigMap里。先把当前配置导出来看一遍kubectl -n kube-system get configmap kube-proxy -o yaml找到config.conf里的mode字段默认情况可能是空值或iptables。把它改成ipvs。如果想指定调度算法可以在ipvs相关配置段里加一行scheduler: rr。改完之后滚动重启kubectl -n kube-system rollout restart daemonset kube-proxy等所有Pod重新起来之后随便进一个kube-proxy Pod看日志日志里出现Using ipvs Proxier之类的字样说明已经切到IPVS模式。这里有个经验kube-proxy不会热加载ConfigMap的变化必须重启进程rollout restart这步不能省。另外在比较老的kubeadm版本里ConfigMap的字段结构和现在不完全一样改之前先确认字段路径不要凭记忆硬改。4.2 二进制部署改启动参数二进制部署的kube-proxy通常由systemd管理配置文件里会有一串启动参数。找到--proxy-modeiptables替换为--proxy-modeipvs。如果之前没有显式指定过proxy-mode默认就是iptables模式直接加上--proxy-modeipvs即可。改完重启systemctl restart kube-proxy用journalctl -u kube-proxy查看最近的日志确认启动参数已经生效。这里有个容易踩的坑有些二进制的部署脚本会把kube-proxy的启动参数写在一个单独的配置文件里unit文件只是负责加载。只改了unit文件但没改配置文件重启后参数又被配置文件覆盖了看起来改了实际没生效。改之前先确认参数到底是从哪里读的。4.3 模块加载与开机持久化这一步建议在切换kube-proxy之前完成在所有节点上执行modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_wrr modprobe ip_vs_sh同时确保nf_conntrack模块可用。为了让节点重启后依然生效需要写入模块加载配置。常规做法是创建文件比如/etc/modules-load.d/ipvs.conf每行一个模块名ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack配置文件写好后不同发行版的加载机制略有差异但一般systemd会在启动时自动加载。这步看起来不起眼但特别重要。不少集群切完IPVS后一切正常一重启节点kube-proxy因为模块没加载静默回退到iptables模式排查起来非常费劲。4.4 重启kube-proxy后的第一眼确认重启完成后到任意节点上执行ipvsadm -Ln输出里应该能看到一堆虚拟服务条目目标地址主要是ClusterIPRoutes指向各个后端PodIP。再看kube-proxy日志正常切换后会有IPVS相关初始化信息。如果日志里出现cant initialize ipvs之类的报错多半是模块没加载成功kube-proxy已经悄悄回退到iptables模式。这时候先去排查模块而不是反复重启进程。5. 切换后的验证清单以及那些容易翻车的隐藏问题5.1 规则、连通性、日志三步验证法切换完之后不要急着收工先把验证跑一遍。第一步验证IPVS规则。ipvsadm -Ln的输出里每个Service都应该对应一个虚拟服务器条目条目的Routes数量等于Ready的后端Pod数量。如果某个Service的规则缺失检查该Service的Selector是否匹配到了Pod。第二步验证连通性。创建一个简单的测试Service带几个后端Pod从集群内发起请求确认能正常往返。重点测三种访问路径ClusterIP访问、NodePort访问、同节点和跨节点的Pod访问。无论走哪条路径最终都应该能到达后端Pod。第三步查日志和内核状态。看kube-proxy日志有没有异常看dmesg有没有模块或连接跟踪相关的故障。如果节点上开了监控检查网络转发相关指标在切换前后有没有明显变化。5.2 隐藏问题一调度算法选不对热点Pod接不住流量默认rr调度算法是“轮询”的但不一定“合理”。如果后端Pod规格不一致比如有的Pod是4核有的Pod是2核rr会按请求数量均匀分发不考虑Pod的处理能力。结果就是小规格Pod被打满大规格Pod在空转。想解决这个问题可以切到带权重概念的算法让大规格Pod承担更多流量。需要注意kube-proxy对权重配置的支持在不同版本间有差异新版支持通过配置或注解调整老版本可能要手动改ipvsadm规则但手动改完之后一旦kube-proxy触发同步周期改动会被覆盖。所以优先选择kube-proxy本身支持的权重配置方式不要依赖手工改IPVS规则。5.3 隐藏问题二local模式的Service会改变健康检查响应方式externalTrafficPolicy: Local的Service在iptables模式下健康检查通常是kube-proxy通过一个专用端口对外暴露的。切换IPVS之后这个机制依然存在但健康检查探活的目标端口和响应行为可能发生变化具体表现为健康检查端口不再指向原来的NodePort而是由kube-proxy动态分配的高端口。这个变化在部分负载均衡器配置下会导致健康检查失败进而影响Service的流量入口。切换前建议检查集群内所有externalTrafficPolicy为Local的Service确认它们的健康检查配置在IPVS模式下依然有效。如果用的是自建的负载均衡器调整后端健康检查端口指向新的探活口就可以解决。5.4 隐藏问题三L2负载均衡方案下的ARP与strictARP如果你的集群里跑了基于L2模式的负载均衡方案比如通过ARP宣告VIP地址的那类组件切换到IPVS模式后要注意ARP行为。IPVS模式下kube-proxy对ARP的处理逻辑和iptables模式存在差异如果相关组件要求开启strictARP而在iptables模式下一直没开切换后可能出现VIP不通、同网段设备访问异常之类的问题。解决办法是确认自用的L2方案是否有strictARP要求有的话在节点上开启对应内核参数并保持配置一致。这类问题和kube-proxy本身没有直接关系但在切换过程中容易被误伤提前排查能省不少事。5.5 切换后如何判断是否真的“值了”切换不是一个“做完就结束”的动作而是优化动作最好用数据说话。建议切换后持续观察一到两周重点看三个指标Service数量增长后转发延迟是否保持平稳而不是像以前一样跟着规则数一起涨。节点CPU里软中断和网络处理开销是否下降尤其在大量短连接场景下。压测新建连接成功率是否更稳定连接跟踪相关的丢包日志是否消失。我的体感是集群Service达到一定规模之后切IPVS收益非常明显。如果集群还很小切换更多是提前布局为后续扩容做准备。无论哪种情况IPVS都是当前Kubernetes大规模集群路由转发的主流方向早切早适应。最后再分享一个实操中容易忽略的细节切换完成后的第一次节点重启最好有人在旁边盯着。因为模块加载、kube-proxy启动顺序之间偶尔会有微妙的时间差。万一发现回退到iptables模式在modules-load配置确认无误之后多半是kube-proxy启动早于模块就绪调整一下系统服务的启动依赖顺序就能根治。这个坑不深但遇到了确实会折腾一阵子。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑