1. 为什么要把 kube-proxy 从 iptables 换成 IPVS集群规模一旦过千个 Serviceiptables 模式下的规则链会膨胀到几千甚至上万条每次 Service 变更都要全量刷新规则CPU 毛刺和请求延迟抖动几乎是家常便饭。我在几个中等规模集群上实测过Service 数量到 800 左右时kube-proxy 的规则同步耗时从毫秒级涨到秒级滚动发布期间偶尔能看到连接建立延迟飙到 200ms 以上。这不是配置问题是 iptables 线性匹配模型的先天限制——每个包都要从上到下遍历规则链规则越多单包处理成本越高。IPVSIP Virtual Server走的是另一条路。它在内核里维护哈希表把 Service 的 ClusterIP 和端口映射成虚拟服务后端 Pod 作为真实服务器挂在下面转发决策是 O(1) 的哈希查找跟后端数量基本无关。kube-proxy 在 IPVS 模式下不再写大量 iptables 规则做 DNAT而是通过 netlink 接口调用 IPVS 的增删改查规则同步变成增量操作。这个差异在 Service 规模上去之后非常明显同样 800 个 ServiceIPVS 模式下同步耗时稳定在几十毫秒而且支持多种调度算法不只是轮询。需要说清楚的是IPVS 模式并不是把 iptables 完全扔掉。kube-proxy 仍然会用少量 iptables 规则处理 SNAT、Masquerade 以及 NodePort 的部分场景只是不再承担主要的负载均衡职责。所以切换之前要确认内核模块、ipset、ipvsadm 这些基础件到位否则 kube-proxy 起不来或者行为异常。适合谁来参考这份内容正在运维中等以上规模集群、遇到 Service 规则同步慢或连接抖动的人准备新搭集群、想一步到位选对模式的以及已经切了 IPVS 但发现某些场景行为跟预期不一致、想搞清楚边界的人。下面我按“设计思路—前置检查—实操切换—验证—排障”的顺序把整个过程拆开讲参数和命令都可以直接抄。2. 切换前的整体设计与关键取舍2.1 模式选型的底层逻辑kube-proxy 支持 userspace、iptables、ipvs、nftables 几种模式不同版本支持范围有差异userspace 早已不推荐nftables 在新版本里逐步成熟但生态还在过渡。iptables 是长期默认胜在稳定、行为可预期、排障资料多IPVS 胜在规模性和调度能力。选型时我一般看三个维度Service 数量、是否需要高级调度、团队对 IPVS 的熟悉程度。Service 数量低于 300 且没有特殊调度需求iptables 完全够用没必要折腾。超过 500 且增长趋势明显IPVS 的收益开始显现。如果业务里有长连接、需要最少连接数调度来均衡后端压力IPVS 的lcleast connection、wrrweighted round robin这些算法是 iptables 给不了的。但代价是排障时要多一层认知ipvsadm -Ln看虚拟服务和真实服务器conntrack看连接跟踪两者要结合着看。还有一个容易被忽略的点IPVS 模式下 kube-proxy 默认会把--ipvs-strict-arp打开新版本默认 true它会为每个 Service 的 ExternalIP 配置 ARP 响应抑制避免 ARP 冲突。这个行为在裸金属环境里很关键在云上一般没影响但如果你的环境里有自定义 ARP 逻辑要留意。2.2 调度算法的选择考量IPVS 支持 rr、wrr、lc、wlc、sh、dh、sed、nq 等算法kube-proxy 通过--ipvs-scheduler指定默认是 rr。我的经验是无状态短请求服务用 rr 就够后端机器规格不一致时用 wrr配合权重长连接多的场景用 lc 或 wlc让新连接往负载轻的后端走源地址哈希 sh 适合需要会话保持但又不想用 SessionAffinity 的场景。这里有个坑kube-proxy 的--ipvs-scheduler是全局配置所有 Service 共用同一个算法不能按 Service 单独指定除非用 Service 的sessionAffinity配合但那是另一套机制。所以选算法要看集群里占主导的业务类型不要为了个别服务牺牲整体。2.3 回滚路径的设计切换之前必须想好怎么退回去。我的做法是保留原 iptables 模式的 kube-proxy 配置ConfigMap 或启动参数切换后用ipvsadm -Ln确认虚拟服务生成正常观察 24 小时无异常再清理旧规则。回滚时把模式改回 iptables重启 kube-proxy它会重建 iptables 规则但要注意 IPVS 留下的虚拟服务和 ipset 需要手动清理否则可能残留脏数据。具体清理命令后面排障部分会给。3. 前置检查内核模块与工具链是否就位3.1 内核模块确认IPVS 依赖一组内核模块不同内核版本模块名略有差异。在目标节点上执行lsmod | grep -E ip_vs|nf_conntrack正常应该看到ip_vs、ip_vs_rr、ip_vs_wrr、ip_vs_lc、ip_vs_wlc、ip_vs_sh、ip_vs_sed、ip_vs_nq等。如果缺失用modprobe加载modprobe -- ip_vs modprobe -- ip_vs_rr modprobe -- ip_vs_wrr modprobe -- ip_vs_lc modprobe -- ip_vs_wlc modprobe -- ip_vs_sh modprobe -- ip_vs_sed modprobe -- ip_vs_nq modprobe -- nf_conntrack要持久化的话写进/etc/modules-load.d/ipvs.conf每行一个模块名。注意nf_conntrack在新内核里可能叫nf_conntrack或已被内建lsmod看不到不代表没编译进去用modinfo nf_conntrack确认。提示如果节点用的是精简版操作系统镜像可能连ip_vs模块都没打包需要装对应的内核模块包。这一步不做kube-proxy 启动时会报 “cant set sysctl” 或直接 crash。3.2 ipset 与 ipvsadm 工具ipset 用于管理 IP 集合IPVS 模式下 kube-proxy 用它来组织后端地址ipvsadm 是排障时看虚拟服务表的必备工具。检查which ipset ipvsadm ipset --version ipvsadm --version没有就装Debian 系apt-get install -y ipset ipvsadmRHEL 系yum install -y ipset ipvsadm。装完确认ip_vs相关 sysctl 可写sysctl -w net.ipv4.vs.conntrack1这个参数让 IPVS 使用 conntrack 做连接跟踪kube-proxy 在 IPVS 模式下会依赖它处理 SNAT 场景。持久化写到/etc/sysctl.d/下。3.3 版本兼容性核对kube-proxy 的 IPVS 支持在不同版本有行为差异比如--ipvs-strict-arp的默认值、--ipvs-exclude-cidrs的引入、以及和--masquerade-all的交互。切换前用kube-proxy --help | grep ipvs看当前版本支持哪些参数别照搬网上旧版本的配置。我遇到过某版本里--ipvs-scheduler改了算法但没生效排查半天发现是参数名拼写和文档不一致以--help输出为准。4. 实操切换从配置修改到生效验证4.1 修改 kube-proxy 配置如果集群用 kubeadm 管理kube-proxy 的配置在kube-system命名空间下的kube-proxyConfigMap 里。先备份kubectl -n kube-system get configmap kube-proxy -o yaml kube-proxy-backup.yaml然后编辑 ConfigMap把mode从空默认 iptables改成ipvs并按需设置调度算法data: config.conf: |- apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: ipvs ipvs: scheduler: rr strictARP: true excludeCIDRs: []strictARP在裸金属环境建议 true云环境按需。excludeCIDRs用于排除某些网段不走 IPVS一般留空。改完 ConfigMap 不会自动生效需要重启 kube-proxy。用 DaemonSet 的话直接滚动重启kubectl -n kube-system rollout restart daemonset kube-proxy kubectl -n kube-system rollout status daemonset kube-proxy如果是二进制部署改/var/lib/kube-proxy/config.conf后systemctl restart kube-proxy。4.2 确认虚拟服务生成重启后等几十秒在任意节点执行ipvsadm -Ln应该能看到类似输出IP Virtual Server version 1.2.1 (size4096) Prot LocalAddress:Port Scheduler Flags - RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.96.0.1:443 rr - 192.168.1.10:6443 Masq 1 0 0 TCP 10.96.0.10:53 rr - 10.244.1.5:53 Masq 1 0 0每个 Service 的 ClusterIP 对应一个虚拟服务后端 Pod 作为 RemoteAddress 挂在下面Masq表示做了 SNAT。如果ipvsadm -Ln是空的说明 kube-proxy 没成功切到 IPVS 或者没连上 API Server去看 kube-proxy 日志kubectl -n kube-system logs -l k8s-appkube-proxy --tail100重点找 “Using ipvs Proxier” 这类启动日志以及有没有模块加载失败、ipset 创建失败的报错。4.3 验证 Service 连通性先测 ClusterIP 访问kubectl run test-curl --imagecurlimages/curl --rm -it --restartNever -- \ curl -s -o /dev/null -w %{http_code}\n http://service-name.namespace.svc.cluster.local:port再测 NodePort 和 ExternalIP如果有。NodePort 在 IPVS 模式下走的是 iptables 的 KUBE-NODEPORTS 链做 DNAT 到 IPVS 虚拟服务行为跟 iptables 模式略有不同要单独验证。我一般会起一个临时 nginx Deployment 加 Service分别用 ClusterIP、NodePort、headless 三种方式访问确认都通再继续。4.4 观察规则同步与连接跟踪切换后重点看两个指标kube-proxy 的sync_proxy_rules_duration_seconds规则同步耗时和节点的 conntrack 表使用率。IPVS 模式下同步耗时应该明显低于 iptables 模式如果反而变高检查是不是 ipset 操作频繁或者 API Server 压力大。conntrack 表用conntrack -C看当前条目数对比sysctl net.netfilter.nf_conntrack_max使用率超过 70% 要考虑调大上限否则新连接会被丢弃。5. 常见问题与排查技巧实录5.1 切换后 Service 访问超时最常见的原因是内核模块没加载全或者 ipset 没装。kube-proxy 日志里会有 “Failed to create ipset” 或 “cant initialize ipvs” 之类的报错。按第 3 节的检查清单逐项过一遍。另一个原因是strictARP在裸金属环境没开导致 ExternalIP 的 ARP 响应冲突表现为部分节点访问 ExternalIP 时通时不通。把strictARP: true加上重启 kube-proxy 即可。5.2 NodePort 行为异常IPVS 模式下 NodePort 的实现依赖 iptables 的 KUBE-NODEPORTS 链如果之前 iptables 模式留下的规则没清理干净可能出现 DNAT 目标错误。排查时先看iptables -t nat -L KUBE-NODEPORTS -n -v确认规则指向的虚拟服务地址正确。必要时手动清理旧规则停掉 kube-proxyiptables -t nat -F和iptables -t nat -X清空 nat 表生产环境慎用先确认没有其他组件依赖再重启 kube-proxy 让它重建。5.3 连接跟踪表打满IPVS 模式下每个连接都会进 conntrack 表长连接多、QPS 高的集群容易打满。表现是新连接建立失败、日志里出现 “nf_conntrack: table full, dropping packet”。调大net.netfilter.nf_conntrack_max和net.netfilter.nf_conntrack_buckets同时缩短nf_conntrack_tcp_timeout_established默认 5 天可以调到几小时让空闲连接尽快回收。调参要结合业务连接生命周期别一刀切。5.4 调度算法不生效改了--ipvs-scheduler但ipvsadm -Ln里显示的算法没变先确认 kube-proxy 真的重启了、ConfigMap 挂载生效了。有些部署方式下 ConfigMap 更新后 Pod 内文件有延迟kubectl exec进去cat /var/lib/kube-proxy/config.conf确认内容。另外注意算法名拼写wlc不是wclsed不是sed这个容易看错。现象可能原因排查命令处理ipvsadm 输出为空模块未加载/ipset 缺失lsmod | grep ip_vs、which ipset加载模块、安装工具Service 访问超时strictARP 未开/规则残留ipvsadm -Ln、iptables -t nat -L -n开 strictARP、清理旧规则NodePort 不通KUBE-NODEPORTS 链异常iptables -t nat -L KUBE-NODEPORTS -n -v重启 kube-proxy 重建新连接失败conntrack 表满conntrack -C、sysctl net.netfilter.nf_conntrack_max调大上限、缩短超时算法未变配置未生效/拼写错cat /var/lib/kube-proxy/config.conf确认重启、核对拼写5.5 回滚时的清理要点决定退回 iptables 模式时先把 mode 改回去、重启 kube-proxy然后清理 IPVS 残留ipvsadm -C ipset destroyipvsadm -C清空所有虚拟服务ipset destroy清空所有集合不带参数会删所有谨慎。如果集群里还有其他组件用 ipset先ipset list看清楚再删。清理完再确认 iptables 规则重建正常Service 访问恢复。6. 规模上去之后的调优经验6.1 ipset 与规则同步的平衡IPVS 模式下 kube-proxy 用 ipset 组织后端地址Service 数量大时 ipset 条目会很多。ipset list看集合大小如果单个集合条目过万同步时的 netlink 操作会变慢。这时候可以调--ipvs-sync-period默认 30s和--ipvs-min-sync-period让同步更平滑避免集中刷新造成毛刺。但别调太大否则 Service 变更生效延迟明显。6.2 与 CNI 的协同IPVS 做的是 Service 层的负载均衡Pod 之间的直接通信走 CNI 的网络路径两者互不干扰。但要注意 CNI 如果也用了 iptables 或 ipset可能和 kube-proxy 的规则有交叉。我遇到过 CNI 的 masquerade 规则和 kube-proxy 的 SNAT 规则顺序冲突导致 Pod 访问外部地址时源地址不对。排查时用iptables -t nat -L -n -v看规则顺序确认 kube-proxy 的链在 CNI 链之前或之后符合预期。这个没有通用答案要看具体 CNI 实现。6.3 监控指标的关注点切换后把 kube-proxy 的 metrics 接进监控重点看sync_proxy_rules_duration_seconds的 P99、sync_proxy_rules_last_timestamp_seconds的间隔、以及network_programming_duration_seconds。IPVS 模式下这些指标应该比 iptables 模式更平稳。另外节点层面加 conntrack 使用率、ip_vs 连接数的监控提前发现容量问题。我一般会在切换后一周内每天看一眼这些曲线确认没有缓慢劣化的趋势。6.4 混合模式的过渡策略大规模集群不建议一次性全量切换。可以按节点池分批先在一个非核心节点池把 kube-proxy 切到 IPVS观察业务无异常后逐步扩大。kube-proxy 是节点级组件不同节点用不同模式在过渡期是允许的Service 的 ClusterIP 在每个节点上由本地 kube-proxy 负责转发模式不一致不影响跨节点通信只是各节点转发路径不同。这个策略能有效控制风险出问题只影响一个节点池。7. 我个人在实际操作中的几点体会切换本身不复杂复杂的是切换前后的确认工作。我踩过最深的坑是内核模块没持久化节点重启后ip_vs没自动加载kube-proxy 起不来整个节点上的 Service 访问全挂。所以modules-load.d那一步千万别省。另一个体会是strictARP在裸金属环境必须开云上可以按需这个参数的行为差异在不同环境里表现完全不同切换前先确认自己的环境类型。还有一点IPVS 的排障思路和 iptables 不一样别一上来就iptables -L找规则先ipvsadm -Ln看虚拟服务表再看 conntrack最后才看 iptables 的辅助规则。顺序搞反了会浪费很多时间。调度算法也别频繁换选定一个观察一段时间频繁切换会让连接分布出现短期抖动反而不好判断效果。