news 2026/10/24 1:14:41

Kubernetes Service 底层:iptables 与 IPVS 模式原理及排错指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes Service 底层:iptables 与 IPVS 模式原理及排错指南

之前排查一个业务 Service 偶发超时的时候,我在节点上把iptables-save导出来,光是KUBE-SVC-*和KUBE-SEP-*相关的规则就刷了好几屏。集群规模其实不大,几十个 Service,但后端 Pod 一多,iptables 模式的规则数量就呈线性膨胀。那会儿才开始认真琢磨 Service 底层的 Iptables 与 IPVS 到底差在哪。后来把 kube-proxy 切到 IPVS 模式,一条ipvsadm -Ln就能扫完所有转发关系,超时和规则同步的问题也随之消失。

这篇不打算从头讲怎么搭集群,重点是把 Service 流量进节点之后的底层路径拆开:iptables 模式做了哪些动作,IPVS 模式又是怎么换了一套打法;为什么大家总说"后端一多、规则一大就选 IPVS";以及线上排错时,怎么从规则层反向定位问题。适合正在被 Service 流量问题折磨、或者想把 kube-proxy 原理彻底搞清楚的读者。

1. 先搞明白:Service 与 kube-proxy 在流量链路里的真实角色

1.1 Service 解决的并不是"负载均衡"

很多人一提到 Service,第一反应是"负载均衡"。这个理解不算错,但容易把思路带偏。Service 最核心的使命,是给一组动态变化的 Pod 提供一个稳定的访问入口。

Pod 在 Kubernetes 里是"短命"的,滚动更新、故障重启、扩缩容都会让 Pod IP 变化。如果客户端直接记 Pod IP,那 Pod 一换地址服务就断了。Service 的 ClusterIP 是一个虚拟 IP,它不挂在任何网卡上,也不属于任何真实负载均衡器,它存在的意义就是让客户端只认这个不变的入口,至于后面 Pod IP 怎么变,那是控制面的事。

所以 Service 的底层,本质上是一个"流量重定向"问题:怎么把发往 ClusterIP:Port 的包,改写为发往某个 PodIP:Port,并且让回程流量也能原路返回。理解了这一点,再看 iptables 和 IPVS 的差异,就看的是同一件事:谁来负责这个重定向,以及怎么重定向得又快又稳。

1.2 kube-proxy 真正的身份:规则生成器,而不是代理

这是我在排查问题过程中最想纠正的一个误区。很多流量链路图画得好像有个"proxy"进程在中间转发流量,实际上 kube-proxy 并不转发数据包,它只是一个规则同步器。

kube-proxy 通过 watch 机制监听 API Server 里的 Service 和 Endpoints/EndpointSlice 变化。一旦 Service 或后端 Pod 有变化,它就把最新状态翻译成节点本地的转发规则。在 iptables 模式下,这些规则是 netfilter 的 nat 表规则;在 IPVS 模式下,这些规则是内核 IPVS 模块里的虚拟服务器和真实服务器条目。

也就是说,真正的数据包转发发生在 Linux 内核的 netfilter 框架里,kube-proxy 只负责"配置内核"。这也是为什么 kube-proxy 挂了,已生成的规则短时间内还能继续工作,直到下次规则需要变更。

1.3 userspace、iptables、IPVS:三条不同年代的实现路线

Kubernetes 早期还有一种 userspace 模式,那才是真正意义上的用户态代理:kube-proxy 监听随机端口,把流量在用户态转发给后端 Pod。这种方式实现简单,但每次收发包都要在用户态和内核态之间切换,性能很差,Kubernetes 1.14 之后已经彻底移除了。

后来出现 iptables 模式,用内核态的 netfilter 做 DNAT,性能比 userspace 高了一个量级。但 iptables 规则是链表式的,线性匹配,Service 和后端越多,规则越庞大,匹配开销越明显。

IPVS 模式则把负载均衡逻辑放进了内核的 IPVS 模块。IPVS 是 Linux 内核自带的四层负载均衡框架,用哈希表组织 virtual server 和 real server,查找复杂度接近 O(1),还天然支持多种调度算法。Kubernetes 在 1.11 之后把 IPVS 模式提升为稳定特性,目前也是大规模集群最推荐的方式。

三种模式可以简单用下面这张表对照:

模式转发主体查找方式负载均衡算法现状
userspacekube-proxy 用户态端口映射表轮询已移除
iptables内核 netfilter链表线性遍历仅随机概率稳定但规则膨胀
IPVS内核 IPVS 模块哈希表rr / wrr / lc / sh 等大规模优先

2. Iptables 模式:一条 Service 流量怎么被"链式翻译"到 Pod

2.1 先认准 nat 表,以及它挂载的钩子点

iptables 模式里,Service 相关的所有关键规则都在nat表里。Linux 内核在处理网络包时,会在固定路径上调用 netfilter 的钩子函数,其中和转发关系最密切的是这样一段路径:

  • 包进入节点后,先经过PREROUTING钩子,这是 nat 表修改目标地址的最佳位置;
  • 然后内核做路由决策,判断这个包是发给本机进程还是需要转发;
  • 如果是转发,经过FORWARD钩子;
  • 出节点前还会经过POSTROUTING钩子,这里适合做源地址伪装。

kube-proxy 做的事情,就是把自定义链挂到这些钩子上。你执行iptables -t nat -S的时候,会看到类似下面的跳转关系:

-A PREROUTING -m addrtype --dst-type LOCAL -j KUBE-SERVICES -A OUTPUT -m addrtype --dst-type LOCAL -j KUBE-SERVICES -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING

注意这里有两条KUBE-SERVICES跳转,一条在PREROUTING,一条在OUTPUT。这是因为访问 ClusterIP 的流量有两类来源:外部进入节点的包走PREROUTING,而节点本机进程访问 ClusterIP 的包会直接从OUTPUT钩子出来。如果只挂一条,就会出现"外面能通、本机却不通"的诡异现象。

2.2 KUBE-SERVICE / KUBE-SVC / KUBE-SEP:三级链的接力

一条 Service 的 iptables 规则,实际上是三个层级的链在接力。我在节点上模拟了一条典型规则,拆开看就非常清晰。

第一级是KUBE-SERVICES链,它的作用是按 ClusterIP:Port 做门卫匹配:

-A KUBE-SERVICES -d 10.96.102.6/32 -p tcp --dport 80 -j KUBE-SVC-XXXXXX

意思很直白:只要是发往10.96.102.6:80的包,就跳转到属于这个 Service 的KUBE-SVC-XXXXXX链。

第二级是KUBE-SVC-XXXXXX链,它实现负载均衡。iptables 本身没有传统 LB 那种调度算法,kube-proxy 用的是statistic模块,通过概率分配到不同后端。假如这个 Service 有 3 个后端,规则长这样:

-A KUBE-SVC-XXXXXX -m statistic --mode random --probability 0.3333 -j KUBE-SEP-AAAAAA -A KUBE-SVC-XXXXXX -m statistic --mode random --probability 0.5000 -j KUBE-SEP-BBBBBB -A KUBE-SVC-XXXXXX -j KUBE-SEP-CCCCCC

第一条规则以 1/3 概率把包丢给第一个后端;没被选中的包继续走第二条,以 1/2 概率丢给第二个后端;前两条都没命中的包,最后必然落到第三个后端。这种"概率递减"的设计,保证了每个后端分到的流量大致相等。从数学上看,三个后端分别收到 1/3、1/3、1/3,非常巧妙,但本质上也说明一个问题:iptables 模式不支持加权调度,所有后端权重是等价的。

第三级是KUBE-SEP-XXXXXX链,它执行真正的 DNAT,把包的目的地址改成 Pod IP:

-A KUBE-SEP-AAAAAA -p tcp -m tcp -j DNAT --to-destination 172.20.1.5:8080

到这里,一个发往 ClusterIP 的包,目标地址已经被改写为某个具体 Pod 的 IP 和端口。接下来内核重新做路由决策,把包转发到对应节点。

注意:如果你在 Service 上开了sessionAffinity: ClientIP,KUBE-SVC链的实现会从上面的随机概率换成recent模块,基于源 IP 做固定后端选择。这也是 iptables 模式"会话保持"的底层来源。

2.3 DNAT 之后的隐形功臣:conntrack

DNAT 做完之后,有一个很容易被忽略的问题:回程包怎么回到客户端?

IP 协议本身是无状态的,如果只做 DNAT,Pod 收到的包源地址还是客户端地址,Pod 回包时直接以 PodIP 作为源地址发出去。客户端收到的数据明明是自己访问 VIP 得到的,源地址却变成了一个内网 Pod IP,这个连接根本对不上。

救场的是 netfilter 的 conntrack(连接跟踪)模块。当一个包经过 nat 表完成 DNAT 后,conntrack 会在连接表里记录"原始方向是 VIP 到客户端,应答方向是 Pod 到客户端"这种映射。后续同一连接的回程包,会在POSTROUTING阶段被还原目标地址,让客户端看到的依然是 ClusterIP。

这也带来了一个常见的故障点:conntrack 表是有限大小的,默认值通常在十几万到几十万条之间。如果集群里短连接特别多,或者有异常连接没释放,conntrack 表可能被打满。这时候新连接无法建表,Service 就会出现明显的随机超时。用cat /proc/sys/net/netfilter/nf_conntrack_count对比nf_conntrack_max,就能快速判断是不是这个原因。

2.4 NodePort 场景为什么离不开 MASQUERADE

ClusterIP 只能在集群内被访问,如果要从集群外访问 Service,就得靠 NodePort 或 LoadBalancer。NodePort 模式的链路里,还有一个容易被忽略的动作:MASQUERADE,也就是源地址伪装。

当外部客户端访问NodeIP:NodePort时,目标地址会先被改写为 PodIP,源地址还是客户端公网 IP。如果这个 Pod 在其他节点上,包会跨节点转发。问题出在回程:Pod 回包时,它的默认网关是所在节点的 CNI 网桥,包会直接发回给客户端。可客户端怎么知道 PodIP 是哪个网段?它根本不会路由这个包。

kube-proxy 的解决办法,是在部分场景下对包打标记,并在POSTROUTING阶段做 MASQUERADE。节点会把包源地址改成自己的 IP,回程流量先回到节点,再由节点利用 conntrack 还原成客户端地址后转发回去。代价就是客户端看到的源 IP 变成了节点 IP,完整的源 IP 透传就丢了。

如果希望保留客户端源 IP,可以把 Service 的externalTrafficPolicy设置为Local,这样流量只会被分到本节点的后端 Pod,回程直接从本节点的 Pod 出去,不需要跨节点,也就不需要做 MASQUERADE。代价是本节点没有后端 Pod 时,外部访问会直接失败。这是一个典型的"保源 IP 还是保高可用"的取舍。

3. IPVS 模式:把负载均衡逻辑搬进内核态的代价与收益

3.1 Virtual Server 与 Real Server:IPVS 眼中的 Service 和 Pod

IPVS 是 Linux 内核自带的四层负载均衡框架,它把"分发流量"这件事下沉到了内核态。在 IPVS 的视角里,一个 Service 就是一个 Virtual Server(虚拟服务器),每个后端 Pod 就是一个 Real Server(真实服务器)。

kube-proxy 在 IPVS 模式下,会通过 netlink 接口与内核 IPVS 模块通信。你创建了一个 ClusterIP 为10.96.102.6的 Service,kube-proxy 就在内核里创建一个绑定到10.96.102.6:80的 virtual server;每来一个后端 Pod,就往这个 virtual server 下添加一条 real server 条目。

用ipvsadm查看时,输出非常直观:

IP Virtual Server version 1.2.1 Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.96.102.6:80 rr -> 172.20.1.5:8080 Masq 1 0 0 -> 172.20.1.6:8080 Masq 1 0 0 -> 172.20.1.7:8080 Masq 1 0 0

Forward这列显示的Masq,表示 IPVS 使用的是 NAT 模式,也就是会改写目标地址再转发。IPVS 收到一个发往 ClusterIP 的包后,通过哈希表定位到对应的 virtual server,再根据调度算法选出一个 real server,做 DNAT,然后把包交给网络栈继续转发。整个查表过程都在内核态,不会反复遍历成百上千条 iptables 规则。

IPVS 本身也维护自己的连接状态,回复包会按照连接记录原路还原目标地址,所以它不像 iptables 模式那样完全依赖 conntrack 来做 DNAT 的反向还原。但在 Kubernetes 的具体实现里,kube-proxy 依然会在边缘场景保留一部分 iptables 规则,比如KUBE-MARK-MASQ打标记和KUBE-POSTROUTING做 SNAT,来处理 NodePort、外部流量等特殊情况。

3.2 内核模块与启动校验:为什么有时候配置了 ipvs 却还是 iptables

IPVS 模式虽然好,但它依赖内核模块。切 IPVS 模式最常踩的坑,就是内核没加载ip_vs模块,导致 kube-proxy 静默回退到 iptables。

kube-proxy 在启动时会检查当前内核是否支持 IPVS,如果检测不到需要的内核模块,它会打一条日志并退回 iptables。很多人配置了--proxy-mode=ipvs,却始终看到KUBE-SVC规则在增长,就是模块没加载导致的。

在节点上执行下面几条命令,就能确认模块状态:

lsmod | grep ip_vs modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_wrr modprobe ip_vs_sh

建议在节点初始化阶段就把这些模块加进去,并写入模块加载配置,让机器重启后也能自动加载。常用的模块包括ip_vs、ip_vs_rr、ip_vs_wrr、ip_vs_lc、ip_vs_sh、ip_vs_dh等。具体的加载配置方式,取决于节点的发行版和初始化工具。

我用过一个比较省心的方式是:在安装 kube-proxy 之前,用一段 DaemonSet 或启动脚本统一执行modprobe,再确认/proc/modules里能看到ip_vs,最后才允许 kube-proxy 容器启动。这样能避免集群建好之后才发现模式不对的尴尬。

3.3 调度算法:rr、wrr、lc、sh 的真实差距

IPVS 最吸引人的地方,除了性能,还有调度算法的多样性。Kubernetes 里可以通过 kube-proxy 的--ipvs-scheduler参数指定默认调度算法,常见的几种:

  • rr:轮询,每个后端轮流接收请求,这是 Kubernetes 的默认选择。
  • wrr:加权轮询,可以为不同后端设置不同的权重,适合后端规格不一致的场景。但 Kubernetes Service 层面的 Endpoint 没有暴露权重概念,所以这项能力在标准用法里发挥空间有限。
  • lc:最少连接,把新连接分给当前活跃连接数最少的后端。听起来比轮询聪明,但如果后端处理连接速度差距大,反而可能因为"连接数少=处理快"的假设不成立,导致负载失衡。
  • sh:源地址哈希,同一源 IP 的请求总被分到同一个后端,相当于一种底层的会话保持手段。

个人建议,如果没有特殊需求,默认rr就够了。Kubernetes 调整负载的粒度在 Service 和 ReplicaSet 层面,通常不会要求数据面去感知后端规格差异。真要做会话保持,正确的做法是设置 Service 的sessionAffinity: ClientIP,kube-proxy 会为 IPVS virtual server 配置 persistent 模板,比手工换调度算法更可控。

sessionAffinity在 IPVS 模式下,会体现为ipvsadm输出里的 flags 和 timeout。例如设置sessionAffinityConfig.clientIP.timeoutSeconds后,相同源 IP 的连接会被持续调度到同一个 real server,直到超时时间过期。这个机制在 iptables 模式下只能靠recent模块模拟,可配置性和可观测性都差不少。

3.4 IPVS 的边界:它也不是万能的

IPVS 再强,也只是一个四层转发框架,它有自己明确的边界。

第一,IPVS 不理解 HTTP、TLS、URL 这类七层信息。它只看协议、IP、端口,所以做不了基于路径的转发,也做不了 TLS 终止。这些工作得交给 Ingress Controller 或服务网格。

第二,IPVS 模式在 Kubernetes 里仍然会依赖一部分 iptables 规则。kube-proxy 需要维护KUBE-FORWARD链来保证转发链路的默认策略正确,也需要用KUBE-MARK-MASQ和KUBE-POSTROUTING处理特定场景的源地址伪装。所以 IPVS 模式不是彻底甩开 iptables,而是把最核心、最耗时的负载均衡逻辑从 iptables 链式匹配换成了哈希表。

第三,IPVS 对协议的支持受内核版本限制。比如 SCTP 支持要看内核是否满足条件,Windows 节点也不支持 IPVS。在混合操作系统集群里,i ptables 模式仍是兜底选项。

第四,IPVS 模式解决的是"数据面调度性能"问题,解决不了"控制面数据同步"的延迟、DNS 缓存、以及 CNI 网络本身的丢包。曾经见过一个集群切了 IPVS 之后依然有超时,最后定位到是 conntrack 表加nf_conntrack_dying_timeout参数的问题,和数据面调度方式无关。排查时要清醒:IPVS 不是万能药,它只负责跨节点转发的效率和调度质量。

4. 从性能开撕:全量刷新的 iptables 和增量更新的 IPVS

4.1 规则数量怎么算:后端数量导致的链膨胀

为什么 iptables 模式在规则一多之后就扛不住?要回答这个问题,得先量化一下规则数量。

一个普通的 ClusterIP Service,每增加一个后端 Pod,就要新增至少两条规则:一条在KUBE-SVC链上参与概率选择,一条在独立的KUBE-SEP链上执行 DNAT。如果集群里有 200 个 Service,平均每个 5 个后端,光这两类链上的规则就有 2000 条,再算上KUBE-SERVICES入口规则、KUBE-MARK-MASQ标记规则,几千条规则是常态。

netfilter 的 iptables 匹配是典型的线性遍历:包进入KUBE-SERVICES链后,一条一条匹配目标地址,命中了再跳到KUBE-SVC链,接着又是几条规则逐条匹配。规则越多,平均匹配次数越多,留给包转发的 CPU 开销就越大。虽然现代内核有一些规则缓存优化,但规则频繁变化时,缓存命中率会明显下降。

下面这张表可以直观展示增长趋势:

Service 数平均后端数预计核心转发规则量(约)查找方式
503300+线性遍历
20052000+线性遍历
5001010000+线性遍历
任意任意取决于 virtual server 数量哈希查找

IPVS 则完全不同。每个 Service 在内核里对应一个 virtual server,IPVS 使用哈希表按"协议+目标地址+端口"直接定位 virtual server,再在 real server 集合中选择目标。这个查找过程不随全局 Service 数量线性增长,所以大规模场景下优势极其明显。

4.2 iptables-restore 的全量同步,正是大规模集群的痛点

规则多只是静态层面的问题,动态层面还有一个更隐蔽的痛点:iptables 模式的规则同步是全量刷新。

kube-proxy 每次检测到 Service 或 Endpoints 变化,都会基于当前集群状态重新生成一份完整的 iptables 规则集,然后用iptables-restore一次性写入内核。注意,这里重建的不是某一条规则,而是所有 Service 相关的全套规则。

假设集群里同时滚动更新几十个 Deployment,Endpoints 频繁增删,kube-proxy 就会陷入高频的全量规则重建。规则总量越大,单次iptables-restore耗时越长。在极端情况下,一轮同步还没完成,下一轮变化又来了,kube-proxy 只能不断追赶,表现为规则持续不稳定,偶发出现短暂空窗期。

IPVS 模式的同步逻辑不一样。kube-proxy 在同步时会做 diff,只对新增或删除的 virtual server、real server 通过 netlink 做增量更新。后端 Pod 变化时,不需要重建全部转发规则,只是往已有的 virtual server 下添加或摘除一个 real server 条目。这种增量语义在大规模集群里意义重大,尤其适合频繁发布、快速扩缩容的业务。

4.3 IPVS 模式的可维护性优势不只是性能

除了性能,IPVS 带给运维的另一个好处是"可读性"。

iptables 模式的规则只能通过iptables-save或iptables -t nat -nL KUBE-SVC-XXXXXX来查看,规则链名称是随机 hash,想看一个 Service 对应哪些后端,得在KUBE-SERVICES、KUBE-SVC、KUBE-SEP三层之间来回跳。如果规则被其他组件或 CNI 插件混入,梳理起来会更加痛苦。

IPVS 模式下的ipvsadm -Ln输出非常直观:VIP 表示 Service,VIP 下面缩进列出的就是所有后端 Pod,ActiveConn 和 InActConn 还能实时反映每个后端的连接分布。定位问题的时候,一眼就能看清"这个 Service 现在有哪些后端、连接均衡不均衡"。

当然,IPVS 模式也不是完全不需要看 iptables。前面说过,KUBE-FORWARD、KUBE-MARK-MASQ、KUBE-POSTROUTING这些链依然存在。只是它们数量固定且规则简单,不会再随 Service 数量膨胀,排查压力小得多。

4.4 生产选型建议:到底该用哪个模式

如果你的集群还很小,Service 只有几十个,iptables 模式完全够用。它部署最简单,不需要关注内核模块,排错资料也最多。这个阶段直接硬切 IPVS,反而可能因为模块加载、调度参数、旧文档里的命令习惯等问题多花不少时间。

一旦集群规模开始增长,或者你明显感觉到 Service 规则数量已经到了几千条、后端发布频繁导致规则同步压力大,就该考虑迁移 IPVS。Kubernetes 官方也明确倾向 IPVS 模式作为大规模集群的推荐方案。

还有一个常见选择是让 CNI 插件或 Kubernetes 发行版默认启用 IPVS。很多云厂商和发行版已经默认这么做。如果你自己管理集群,最稳妥的路线是:部署初期就加载好ip_vs模块,kube-proxy 直接指定--proxy-mode=ipvs,同时保留 iptables 相关的基础检查能力,这样既拿到性能红利,又不会失去故障排查手段。

5. 线上排错实战:从一条 Service 不通反推到底层

5.1 第一步:确认 kube-proxy 当前运行模式

Service 不通的时候,先别急着看应用日志,第一步要确认 kube-proxy 到底工作在什么模式。因为同样的症状,在不同模式下的排查路径完全不同。

看 kube-proxy 的启动参数是最直接的方式:

kubectl -n kube-system get pod -l component=kube-proxy -o yaml

关注--proxy-mode参数。如果没显式配置,kube-proxy 默认会走 iptables 模式(在支持 IPVS 且模块加载的情况下,需要显式指定才启用 IPVS,默认仍偏向 iptables)。

再用命令验证实际状态:

ipvsadm -L -n | head

如果有输出,说明节点正在用 IPVS 模式;如果提示No such file or directory或者没有任何 virtual server,大概率还是 iptables 模式。对照iptables -t nat -S | grep KUBE-SVC | wc -l,也能做个交叉验证:规则数很多而 IPVS 输出为空,就是 iptables 模式无疑。

常见误区:kube-proxy 设置了--proxy-mode=ipvs,但实际仍在跑 iptables。这多半是内核模块没加载。按前文 3.2 节的方法确认lsmod | grep ip_vs。

5.2 手撕 iptables 规则:从 KUBE-SERVICE 一路追到 KUBE-SEP

如果确认是 iptables 模式,排错的核心动作就是"顺链摸瓜"。先找到 Service 对应的 ClusterIP:

kubectl get svc <service-name> -o jsonpath='{.spec.clusterIP}'

然后查KUBE-SERVICES链,找到匹配这个 ClusterIP 的跳转规则和KUBE-SVC链名:

iptables -t nat -nL KUBE-SERVICES | grep 10.96.102.6

输出了类似KUBE-SVC-XXXXXX的链名之后,再查这条链:

iptables -t nat -nL KUBE-SVC-XXXXXX

这一步能看到概率规则和KUBE-SEP链。接着把这些KUBE-SEP-YYYYYY链全部列出来,确认每个链里 DNAT 的目标地址和端口是不是对应的 Pod IP 和 containerPort:

iptables -t nat -nL KUBE-SEP-YYYYYY

整个链条走完,你就能判断问题是出在路由层面,还是规则层面。如果规则本身没问题,再继续往路由和转发层排查:

sysctl net.ipv4.ip_forward ip route get 172.20.1.5

这里有个容易忽略的细节:Service 的targetPort是容器端口,不是 Pod 的监听端口。如果规则里 DNAT 的端口和实际监听端口不一致,Service 当然不通,但kubectl get endpoints看起来却一切正常。

5.3 用 ipvsadm 看虚拟服务与真实后端

如果确认是 IPVS 模式,就用ipvsadm来排查。

先看有没有对应的 virtual server:

ipvsadm -L -n | grep 10.96.102.6

再看该 VIP 下的 real server 列表:

ipvsadm -L -n --sort | grep 10.96.102.6 -A 5

重点关注三列:

  • Weight:后端权重。Kubernetes 默认权重是 1,如果异常变成 0,说明该后端被摘除,可能是 readiness 探针挂了。
  • ActiveConn:当前活跃连接数。如果某个后端数值异常高,可能是热点后端。
  • InActConn:非活跃连接数。这个值一般不用太纠结,但如果持续增长,说明连接没被正确关闭。

ipvsadm还支持实时连接表查看:

ipvsadm -l -n -c | grep 10.96.102.6

这个命令能看到当前连接表中,源 IP 到 VIP、VIP 到 real server 的映射关系。配合它,可以判断某个客户端 IP 是不是被会话保持固定到了某个后端,也能检查连接超时参数是否合理。

还有一个小技巧:IPVS 模式下如果某个 Service 设了externalTrafficPolicy: Cluster,客户端源 IP 会被伪装成节点 IP。这时不要依赖 Pod 侧日志里的 client IP 来排查,因为那已经不是真实客户端地址了。

5.4 高频故障根因梳理

Service 流量不通,根因往往集中在几个固定位置。我在不同集群里反复见过这几类问题,列出来供大家对照:

第一,ip_forward未开启。iptables 和 IPVS 模式都依赖内核转发。检查/proc/sys/net/ipv4/ip_forward是否为 1。CNI 插件一般会设置,但有些手动装好的节点容易漏。

第二,CNI 网络路由不可达。规则把目标改成了 PodIP,但节点到 PodIP 的路由依赖 CNI 插件的路由表和网络策略。在源节点执行ping <podIP>或ip route get <podIP>,能快速判断是不是 CNI 层的问题。

第三,conntrack 表满。短连接多、异常连接未清理时,会出现 Service 随机超时。处理方法除了调大nf_conntrack_max,还要分析应用层的连接复用是否合理。

第四,本机进程访问 ClusterIP 不通,但外部访问正常。这时优先检查OUTPUT链上的KUBE-SERVICES跳转是否被其他防火墙规则拦截了。kube-proxy 的规则优先级和用户自定义规则冲突时,会出现这种"内外表现不一致"的奇怪现象。

第五,IPVS 模式会话保持不生效。检查 Service 是否设置了sessionAffinity: ClientIP,以及sessionAffinityConfig.clientIP.timeoutSeconds是否满足预期。同时确认 kube-proxy 启动参数里没有把--ipvs-scheduler改成与预期冲突的算法。

第六,NodePort 访问不通但 ClusterIP 正常。重点检查KUBE-NODEPORTS链规则、节点安全组是否放通端口,以及externalTrafficPolicy是否为Local导致某些节点没有后端而拒绝连接。

这些根因里的前两条,即使从 iptables 换成 IPVS 也不会消失,因为它俩本来就不属于调度规则的问题。这正好说明一件事:理解底层机制的意义,不只是为了选型,更是为了在故障时能快速判断"问题到底是不是 kube-proxy 引起的"。

最后再分享一个我自己的习惯:每次新建集群,我会先在节点上跑一遍lsmod | grep ip_vs,然后在第一台节点上临时把 kube-proxy 切到 IPVS 模式,用ipvsadm确认虚拟服务器列表能正常生成,再决定后续节点怎么初始化。这个动作看起来简单,但能提前过滤掉大量"看似配了 IPVS 实际还在 iptables"的隐性坑。排查 Service 流量问题时,我也建议各位养成先确认数据面模式、再看规则链、最后才查应用层的习惯。数据面的规则是死的,一层层追下去,问题基本都能浮出水面。

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

840Dsl STEP7 ToolBox 4.8.07组态与NC变量映射避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:14:35

C#温室监控上位机源码:Modbus RTU采集、SQLite存储与报警联动

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:13:46

运放版图设计:差分对与电流镜的匹配技巧及LVS避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:13:44

Linux stat命令全解析:从inode到时间戳的元数据详情

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:12:06

EasyClick工程化实践:多AI IDE并行开发与UI业务分离架构

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 1:12:05

电力电子技术复习指南:晶闸管、整流电路与逆变失败核心考点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华