kube-proxy 的 userspace 模式

userspacekube-proxy 最早的工作模式(K8s v1.2 之前的默认模式),核心特点是:所有 Service 的请求转发逻辑,都在 kube-proxy 进程的“用户空间”完成,而非内核空间。简单说,kube-proxy 不只是“规则配置器”,更是“请求转发器”——所有访问 Service 的请求,都要经过它的手。

一、核心原理(先搞懂两个关键概念)

在讲流程前,先明确两个基础概念,避免理解混乱:

  1. 用户空间 vs 内核空间
    • 内核空间:操作系统内核运行的区域,处理网络、硬件、进程调度等核心操作,速度极快;
    • 用户空间:普通进程(如 kube-proxy、nginx)运行的区域,要访问硬件/网络必须通过内核,有额外开销。
  2. Service 的 ClusterIPuserspace 模式下,kube-proxy 会为每个 Service 绑定一个 ClusterIP,并且在节点上监听这个 ClusterIP + Port。

二、完整工作流程(可视化+通俗解释)

我们以“集群内 Pod 访问 Service(ClusterIP:80)”为例,拆解每一步:

在这里插入图片描述

逐步拆解(通俗版)

  1. 请求发起:集群内一个 Pod 想访问 Service,发起请求 10.96.0.1:80(Service 的 ClusterIP + Port);
  2. 内核转发:请求先到节点内核,内核发现这个 ClusterIP:80 被 kube-proxy 进程监听,于是把请求转发到用户空间的 kube-proxy 进程;
  3. kube-proxy 选 Pod:kube-proxy 进程维护了一份“Service → 后端 Pod”的映射表(通过监听 K8s API Server 获取 Pod 变化),它会用随机策略选一个健康的 Pod;
  4. 转发请求:kube-proxy 把请求从用户空间再送回内核空间,转发到选中的 PodIP + targetPort;
  5. 响应返回:Pod 处理完请求后,把响应按原路返回(Pod → 内核 → kube-proxy → 内核 → 客户端 Pod)。

关键补充:端口监听细节

userspace 模式下,kube-proxy 会做两件关键的监听操作:

  • 监听 Service 的 ClusterIP:Port:比如 Service 的 ClusterIP 是 10.96.0.1、Port 是 80,kube-proxy 就会在节点上监听 10.96.0.1:80
  • 监听本地回环端口(可选):为了兼容,有时会监听 127.0.0.1:随机端口,再映射到 ClusterIP。

三、优缺点(为什么现在几乎不用)

✅ 优点(仅2个,且都是“历史优势”)

  1. 兼容性极致:支持所有 Linux 内核版本,无需任何内核模块(iptables/ipvs 都需要内核支持);
  2. 逻辑简单易理解:转发流程是“线性的”,排查问题时直接看 kube-proxy 日志就能定位(比如哪个 Pod 被选中、转发是否失败)。

❌ 缺点(核心问题,也是被淘汰的原因)

  1. 性能极差:请求要“内核→用户空间→内核”来回切换,额外开销大,QPS(每秒请求数)远低于 iptables/ipvs 模式;
  2. 单点故障:kube-proxy 进程是转发的核心,一旦进程挂掉,该节点上的 Service 就无法访问(iptables/ipvs 模式下,规则已在核内,kube-proxy 挂了不影响);
  3. 负载均衡策略单一:只有“随机”一种策略,无法实现轮询、加权、最少连接等灵活策略;
  4. 端口占用:kube-proxy 需要为每个 Service 监听一个端口,节点端口资源容易耗尽;
  5. 延迟高:用户空间和内核空间的上下文切换,会增加请求的响应延迟。

五、适用场景(几乎无实用场景)

  • 仅用于极老旧的 Linux 内核(不支持 iptables/ipvs 模块);
  • 仅用于学习/测试,理解 kube-proxy 的原始设计逻辑;
  • 生产环境绝对不推荐使用,哪怕是小集群,也优先用 iptables 模式。

总结

  1. userspace 模式的核心是kube-proxy 进程在用户空间完成所有请求转发,是最早期的实现方式;
  2. 最大问题是性能差、有单点故障、策略单一,现在已被 iptables/ipvs 完全取代;
  3. 理解它的价值在于:对比后能更清楚 iptables/ipvs 模式的优势,以及 K8s 网络组件的演进逻辑。

核心记住:userspace 是“过去式”,只需了解原理,无需在生产环境使用。

kube-proxy 的 iptables 模式

从 v1.2 开始的默认模式(直到 ipvs 模式出现后仍作为基础默认),核心特点是:kube-proxy 不再直接转发请求,而是作为“规则写手”,在节点内核中配置 iptables 规则,由内核直接完成 Service 请求的转发和负载均衡。简单说,kube-proxy 只“写规则”,不“处理请求”——所有转发都在内核空间完成,性能和可靠性大幅提升。

一、核心原理(先搞懂两个关键)

  1. iptables 是什么:Linux 内核的包过滤/转发工具,规则基于“链(Chain)”和“表(Table)”组织,运行在内核空间,处理网络请求的速度极快;
  2. kube-proxy 的角色:监听 K8s API Server,实时同步 Service 和 Pod 的变化(比如 Pod 新增/删除、Service 端口变更),并动态更新节点上的 iptables 规则,确保规则始终和集群状态一致。

二、完整工作流程(流程图+逐步拆解)

我们以“集群内 Pod 访问 Service(ClusterIP:80)”为例,先看可视化流程图,再拆解每一步:
在这里插入图片描述

逐步拆解(通俗版,无专业术语)

  1. 请求发起:集群内一个 Pod 发起请求 10.96.0.1:80(Service 的 ClusterIP + Port),请求先到达所在节点的内核;
  2. 内核匹配 iptables 规则:内核在 PREROUTING 链中找到针对这个 Service(10.96.0.1:80)的 iptables 规则;
  3. 负载均衡选 Pod:iptables 规则内置了负载均衡逻辑(默认随机,可配置轮询),从 Service 关联的 Pod 列表中选一个健康的 Pod;
  4. DNAT 地址转换:iptables 把请求的目标地址从 ClusterIP:80 转换为选中的 PodIP:targetPort(比如 10.200.1.10:80);
  5. 请求转发到 Pod:内核直接把转换后的请求转发到目标 Pod,全程不经过用户空间的 kube-proxy 进程;
  6. 响应返回:Pod 处理完请求后返回响应,内核通过 iptables 的 POSTROUTING 链做 SNAT 转换(可选,把源IP替换为节点IP,确保 Pod 能收到响应),最终把响应返回给客户端 Pod。

补充:外部访问 NodePort 的流程(更贴近实际场景)

如果是外部客户端访问 节点IP:NodePort,流程仅多一步“匹配 INPUT 链规则”:

外部客户端 → 节点IP:NodePort → 节点内核 → INPUT 链匹配 NodePort 规则 → PREROUTING 链匹配 Service 规则 → DNAT 到 PodIP → 目标Pod

三、iptables 规则的具体内容(直观理解)

kube-proxy 会为每个 Service 创建两类核心规则,你可以通过 iptables-save | grep <Service名称> 查看,比如:

# 查看针对 myapp Service 的 iptables 规则
iptables-save | grep myapp

核心规则示例(简化版):

# 1. Service 入口规则(匹配 ClusterIP:80)
-A KUBE-SERVICES -d 10.96.0.1/32 -p tcp -m tcp --dport 80 -j KUBE-SVC-XXXXXX

# 2. 负载均衡规则(随机选 Pod)
-A KUBE-SVC-XXXXXX -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-YYYYYY
-A KUBE-SVC-XXXXXX -j KUBE-SEP-ZZZZZZ

# 3. DNAT 转换规则(指向 PodIP:80)
-A KUBE-SEP-YYYYYY -s 10.200.1.10/32 -j DNAT --to-destination 10.200.1.10:80
-A KUBE-SEP-ZZZZZZ -s 10.200.1.11/32 -j DNAT --to-destination 10.200.1.11:80
  • KUBE-SERVICES:所有 Service 的入口链;
  • KUBE-SVC-XXXXXX:单个 Service 的负载均衡链;
  • KUBE-SEP-YYYYYY:单个 Pod 的 DNAT 链(SEP = Endpoint)。

四、配置/验证 iptables 模式(实操步骤)

1. 确认/配置 iptables 模式

K8s 默认就是 iptables 模式,若被修改,可按以下步骤恢复:

步骤1:编辑 kube-proxy 的 ConfigMap
kubectl edit configmap kube-proxy -n kube-system

修改 config.conf 中的 modeiptables

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "iptables"  # 关键:指定为 iptables 模式
clusterCIDR: "10.200.0.0/16"  # 替换为你的集群 CIDR
iptables:
  masqueradeAll: true  # 开启 SNAT 转换
步骤2:重启 kube-proxy 生效
kubectl delete pods -n kube-system -l k8s-app=kube-proxy

2. 验证是否生效

# 1. 查看 kube-proxy 日志,确认模式
kubectl logs -n kube-system -l k8s-app=kube-proxy | grep "Using iptables proxy mode"

# 2. 查看节点上的 iptables 规则(能看到 K8s 相关链)
iptables-save | grep KUBE-

# 3. 测试 Service 访问(集群内 Pod 中)
kubectl run -it --rm test-pod --image=busybox:1.35 -- sh
# 进入 Pod 后执行
wget -qO- myapp.default.svc.cluster.local:80

如果日志出现 Using iptables proxy mode,且 iptables-save 能看到 KUBE-SERVICES/KUBE-SVC-XXXX 等链,说明配置生效。

五、优缺点(为什么是主流,又有哪些不足)

✅ 优点(核心优势)

  1. 性能远高于 userspace:全程在内核空间处理,无“内核-用户空间”切换,QPS 提升 10 倍以上;
  2. 无单点故障:iptables 规则在内核中生效,即使 kube-proxy 进程挂掉,已配置的规则依然能正常转发请求;
  3. 无需额外依赖(基础):几乎所有 Linux 发行版都内置 iptables,无需加载额外内核模块(ipvs 需要 ip_vs 模块);
  4. 配置自动化:kube-proxy 自动同步规则,无需手动维护。

❌ 缺点(主要问题)

  1. 规则膨胀问题:集群内 Service/Pod 数量多(比如上千个)时,iptables 规则会暴增(每条规则是链式匹配),内核查找规则的耗时会变长;
  2. 负载均衡策略简单:只有“随机”和“轮询”两种,不支持加权、最少连接等高级策略;
  3. 故障排查复杂:iptables 规则是链式嵌套的,出问题时需要逐层排查(比如 KUBE-SERVICES → KUBE-SVC-XXXX → KUBE-SEP-YYYY),对新手不友好;
  4. 无健康检查:iptables 不会主动检查 Pod 是否健康,即使 Pod 挂了,规则仍会转发请求到该 Pod,导致请求失败(需配合 kubelet 的存活探针,Pod 挂掉后 kube-proxy 才会删除对应规则)。

六、关键细节(新手易踩坑)

  1. SNAT/DNAT 作用
    • DNAT:修改请求的目标地址(ClusterIP → PodIP),是核心转发逻辑;
    • SNAT:修改请求的源地址(PodIP → 节点IP),确保 Pod 能正常返回响应(避免跨节点 Pod 访问时的路由问题)。
  2. 规则更新时机:当 Service/Pod 发生变化(比如扩缩容、删除),kube-proxy 会在 10 秒内(默认)更新 iptables 规则;
  3. 与网络插件的配合:Calico/Flannel 等网络插件负责 Pod 之间的网络连通,iptables 负责 Service 转发,两者缺一不可。

总结

  1. iptables 模式的核心是kube-proxy 配置内核 iptables 规则,由内核完成请求转发和负载均衡,是目前 K8s 中小规模集群的默认选择;
  2. 优势是性能高、无单点故障、兼容性好,缺点是规则膨胀、策略简单、排查复杂
  3. 流程图核心逻辑:请求 → 内核 → 匹配 iptables 规则 → DNAT 到 PodIP → 目标Pod,全程无用户空间参与。

核心记住:iptables 模式是“内核接管转发,kube-proxy 只维护规则”,这是它和 userspace 模式最本质的区别。

七、DNAT 和 SNAT

先明确核心定位

DNAT(Destination NAT,目标地址转换)和 SNAT(Source NAT,源地址转换)是 Linux 内核 iptables 的核心功能,运行在内核空间,本质是修改网络数据包的「目标地址/端口」或「源地址/端口」,解决 K8s 集群中「请求能找到 Pod」和「响应能回传给客户端」的核心问题。

简单总结:

  • DNAT:改「数据包要去哪里」→ 让请求能转发到正确的 Pod;
  • SNAT:改「数据包来自哪里」→ 让响应能回到正确的客户端。

一、DNAT(目标地址转换)

1. 核心定义与作用

  • 定义:修改网络数据包的「目标 IP 地址」和/或「目标端口」,把原本发往 A 地址的数据包,转发到 B 地址。
  • 核心作用:在 K8s 中,把发往「Service 的 ClusterIP:Port」的请求,转换为发往「后端 Pod 的 PodIP:targetPort」——这是 Service 能找到 Pod 的核心。

2. 工作流程(K8s 场景+流程图)

以「集群内 Pod 访问 Service(ClusterIP:80)」为例,DNAT 的完整流程如下:
在这里插入图片描述

3. 逐步拆解(通俗版)

  1. 数据包初始状态:客户端 Pod(Pod1,IP=10.200.1.1)发起请求,数据包的「源地址=10.200.1.1:54321」、「目标地址=10.96.0.1:80」(Service 的 ClusterIP:Port);
  2. 进入内核 PREROUTING 链:数据包到达节点内核,先经过 PREROUTING 链(iptables 的核心链,负责修改目标地址);
  3. 执行 DNAT 转换:iptables 匹配到该 Service 的规则,把数据包的「目标地址」从 10.96.0.1:80 改为 10.200.1.2:80(后端 Pod2 的 IP:targetPort);
  4. 转发到目标 Pod:内核根据修改后的目标地址,把数据包转发到 Pod2。

4. K8s 中的实际表现

可以通过以下命令查看 DNAT 规则(对应 Service 的转发):

# 查看 K8s 生成的 DNAT 规则(替换为你的 Service 相关链名)
iptables-save | grep "DNAT --to-destination"

示例输出(核心是 --to-destination 指向 PodIP:Port):

-A KUBE-SEP-XXXXXX -j DNAT --to-destination 10.200.1.2:80

二、SNAT(源地址转换)

1. 核心定义与作用

  • 定义:修改网络数据包的「源 IP 地址」和/或「源端口」,把原本来自 A 地址的数据包,伪装成来自 B 地址。
  • 核心作用:在 K8s 中,解决「Pod 响应能回传给客户端」的问题——尤其是跨节点访问时,若不修改源地址,目标 Pod 会直接把响应发回客户端 Pod 的 IP,可能因网络策略/路由问题无法到达,SNAT 会把源地址改为「节点的 IP」,确保响应能原路返回。

2. 工作流程(K8s 场景+流程图)

承接上面 DNAT 的场景,SNAT 发生在数据包转发到目标 Pod 之前,完整流程如下:
在这里插入图片描述

3. 逐步拆解(通俗版)

  1. DNAT 之后:数据包已被 DNAT 改为目标=Pod2IP:80,接下来进入 POSTROUTING 链(iptables 负责修改源地址的链);
  2. 执行 SNAT 转换:iptables 检测到「客户端 Pod 和目标 Pod 不在同一节点」,把数据包的「源地址」从 10.200.1.1(Pod1 IP)改为 192.168.60.123(节点 IP);
  3. Pod2 接收并响应:Pod2 收到数据包(源=节点 IP),处理后生成响应数据包,目标地址是「节点 IP」;
  4. 反向 SNAT 恢复:节点内核收到响应后,把响应的「目标地址」从节点 IP 改回 Pod1 IP,最终转发给客户端 Pod。

4. K8s 中的实际表现

可以通过以下命令查看 SNAT 规则:

# 查看 K8s 生成的 SNAT 规则
iptables-save | grep "MASQUERADE"  # MASQUERADE 是 SNAT 的特殊形式(自动用节点IP)

示例输出(MASQUERADE 等价于动态 SNAT,用节点的出口 IP 替换源地址):

-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE

三、DNAT + SNAT 组合流程(完整闭环)(外部客户端访问 NodePort Service)

为了更清晰看到两者的配合,这里给出「外部客户端访问 NodePort Service」的完整流程图,这是生产环境最常见的场景:
在这里插入图片描述

四、新手易踩坑的关键点

  1. SNAT 不是必须的:若客户端 Pod 和目标 Pod 在同一节点,K8s 会跳过 SNAT(直接通信),只有跨节点访问时才会触发;
  2. MASQUERADE 是动态 SNATMASQUERADE 会自动使用节点的出口网卡 IP 作为源地址,比固定 --to-source 更灵活(适合节点有多个 IP 的场景);
  3. K8s 自动管理规则:无需手动配置 DNAT/SNAT,kube-proxy 会根据 Service/Pod 的变化自动更新 iptables 规则;
  4. 排查思路:若 Service 访问失败,先查 DNAT 规则是否指向正确的 PodIP,再查 SNAT 是否正常(比如跨节点访问时是否做了源地址转换)。

关键地址变化说明(对应流程图步骤)

为了方便对比,用表格整理每一步的地址变化核心逻辑:

步骤 链/操作 源地址(Source IP) 目标地址(Destination IP) 地址是否变化 变化原因
初始 外部请求 192.168.1.100(客户端) 192.168.60.123:30080(节点IP+NodePort) ❌ 无变化 客户端发起的原始数据包
1 INPUT链:匹配NodePort规则 192.168.1.100 192.168.60.123:30080 ❌ 无变化 仅匹配规则,不修改地址
2 PREROUTING链:第一次DNAT 192.168.1.100 10.96.0.1:80(Service ClusterIP) ✅ 目标地址变 把 NodePort 映射到 Service 的 ClusterIP
3 PREROUTING链:第二次DNAT 192.168.1.100 10.200.1.2:80(Pod IP) ✅ 目标地址变 把 ClusterIP 映射到后端 Pod 的实际 IP
4 POSTROUTING链:SNAT 192.168.60.123(节点IP) 10.200.1.2:80 ✅ 源地址变 让 Pod 响应能找到回传的节点
响应 Pod 生成响应 10.200.1.2(Pod IP) 192.168.60.123(节点IP) ❌ 无变化 Pod 按收到的数据包源地址(节点IP)回包
5 反向DNAT 10.200.1.2 192.168.60.123:30080 ✅ 目标地址变 把节点IP恢复为节点IP+NodePort
6 反向SNAT 10.200.1.2 192.168.1.100(客户端IP) ✅ 目标地址变 把节点IP恢复为原始客户端IP,完成闭环

核心总结

  1. DNAT 只改目标地址:步骤2、3都是修改数据包的「目标地址」,核心是把外部请求的 NodePort → Service ClusterIP → Pod IP,实现请求的层层转发;
  2. SNAT 只改源地址:步骤4修改数据包的「源地址」,核心是让 Pod 响应能精准回传给节点,而不是直接发向外部客户端(避免响应迷路);
  3. 反向转换是“原路返回”:步骤5、6是响应的反向地址恢复,和请求的地址转换完全对称,确保响应能回到最初的客户端。

五、集群内 Pod 访问 Service 的完整流程图(标注源/目标地址)

场景前提

  • 客户端 Pod(Pod1):IP=10.200.1.10,位于节点 A(192.168.60.123
  • Service:ClusterIP=10.96.0.1port=80,关联后端 Pod2
  • 后端 Pod(Pod2):IP=10.200.2.20,位于节点 B(192.168.60.124
  • 集群域名后缀:cluster.local
    在这里插入图片描述
一、关键步骤的地址变化说明
步骤 操作逻辑 源地址 目标地址 地址变化原因
初始 Pod1 发起请求 10.200.1.10 10.96.0.1:80 直接访问 Service 的 ClusterIP,无 NodePort 参与
1 PREROUTING 匹配规则 10.200.1.10 10.96.0.1:80 仅匹配规则,不修改地址
2 DNAT 转换 10.200.1.10 10.200.2.20:80 把 Service 地址映射到后端 Pod 实际 IP
3 跨节点 SNAT 192.168.60.123 10.200.2.20:80 仅跨节点时触发:防止 Pod2 响应迷路;同节点访问时跳过此步,源地址保持 Pod1 IP
响应 Pod2 生成响应 10.200.2.20 192.168.60.123 Pod2 按收到的源地址(节点A IP)回包
4 反向 DNAT 10.200.2.20 10.96.0.1:80 内核根据 conntrack 表,把目标地址从节点A IP 恢复为 Service ClusterIP
5 反向 SNAT 10.200.2.20 10.200.1.10 内核根据 conntrack 表,把目标地址从 Service ClusterIP 恢复为 Pod1 IP
二、集群内访问 vs 外部访问 核心差异对比
对比维度 集群内 Pod 访问 Service 外部客户端访问 NodePort
访问入口 Service 的 ClusterIP:port 节点的 NodeIP:NodePort
初始目标地址 ClusterIP(如 10.96.0.1:80) 节点 IP+NodePort(如 192.168.60.123:30080)
DNAT 次数 1 次(ClusterIP → PodIP) 2 次(NodePort→ClusterIP→PodIP)
SNAT 触发条件 跨节点访问时触发;同节点访问跳过 必触发(外部客户端和 Pod 一定跨节点)
反向 DNAT 作用 恢复 Service ClusterIP 恢复节点 IP+NodePort(补端口)
核心依赖 CoreDNS 解析 Service 域名(如 myapp.default.svc.magedu.local 节点端口暴露 + iptables 规则
三、补充:同节点 Pod 访问的简化流程(无 SNAT)

如果 Pod1 和 Pod2 在同一节点,步骤 3 的 SNAT 会被跳过,流程更简单:

  1. Pod1 请求 → 10.96.0.1:80 → 内核 DNAT 到 10.200.1.20:80
  2. 源地址保持 10.200.1.10,直接转发到 Pod2
  3. Pod2 响应直接发回 10.200.1.10,无需反向 SNAT/DNAT

这个简化流程的核心是:同节点 Pod 通信无需经过节点 IP 中转,减少地址转换开销。

kube-proxy 的 ipvs 模式

ipvs 模式是 kube-proxy 三种工作模式中性能最优的一种,专门为大规模 Kubernetes 集群设计。它基于 Linux 内核的 IP Virtual Server(IPVS)模块实现 Service 负载均衡,核心逻辑是内核态直接转发请求,相比 iptables 模式,在 Service/Pod 数量庞大时,规则查找效率和转发性能有数量级的提升。

一、核心原理与前置条件

1. 核心定位

ipvs 模式下,kube-proxy 的角色依然是规则管理器,不直接参与请求转发:

  • kube-proxy 监听 K8s API Server,同步 Service 和 Pod 的变化;
  • 调用内核 ipvs 模块,在节点上创建 虚拟服务(Virtual Service)真实服务器(Real Server) 规则;
  • 请求到达节点后,内核态的 ipvs 直接完成负载均衡和转发,无需用户态参与,性能远超 iptables

2. 前置条件

节点内核必须加载 ipvs 相关模块(大部分 K8s 发行版已预装):

# 检查 ipvs 模块是否加载
lsmod | grep ip_vs
# 常见模块:ip_vs、ip_vs_rr(轮询)、ip_vs_wrr(加权轮询)、ip_vs_sh(源哈希)

若未加载,可手动安装并加载:

# 安装 ipvsadm 工具(用于管理 ipvs 规则)
yum install -y ipvsadm
# 加载模块
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr

3. 核心优势 vs iptables 模式

特性 ipvs 模式 iptables 模式
规则存储结构 哈希表(O(1) 查找效率) 链式规则(O(n) 查找效率)
负载均衡策略 轮询、加权轮询、最少连接等 仅随机、轮询
大规模集群适配 支持万级 Pod/Service 规则膨胀后性能急剧下降
会话保持 支持(如 sh 策略) 不支持
依赖内核模块 需要 ip_vs 系列模块 内置 iptables,无需额外模块

二、工作流程(以集群外访问 NodePort 为例,附地址变化)

场景前提

  • 外部客户端 IP:192.168.1.100
  • 节点 IP:192.168.60.123,NodePort:30080
  • Service ClusterIP:10.96.0.1,Port:80
  • 后端 Pod 列表:10.200.1.2:8010.200.1.3:80
  • 负载均衡策略:轮询(rr)

完整流程图(标注源/目标地址)

在这里插入图片描述

步骤拆解(含地址变化)

1. 客户端发起请求

外部客户端发送请求到 节点IP:30080,数据包初始状态:

  • 源地址:192.168.1.100
  • 目标地址:192.168.60.123:30080
2. 内核匹配 NodePort 规则

数据包进入节点内核,INPUT 链匹配到 30080 端口对应的 ipvs 虚拟服务规则,地址无变化

3. ipvs 负载均衡选 Pod

ipvs 虚拟服务(对应 Service 的 ClusterIP:80)根据预设策略(轮询),从真实服务器列表中选中一个 Pod(如 10.200.1.2:80)。

4. DNAT 转换目标地址

ipvs 执行 DNAT 操作,将数据包的目标地址从 192.168.60.123:30080 改为 10.200.1.2:80,源地址保持不变。

5. SNAT 转换源地址(跨节点时触发)

数据包进入 POSTROUTING 链,ipvs 执行 SNAT 操作,将源地址从 192.168.1.100 改为节点 IP 192.168.60.123,确保 Pod 响应能回传到节点。

6. 转发到目标 Pod

内核将修改后的数据包转发到选中的 Pod,Pod 接收并处理请求。

7. 响应包原路返回(反向转换)
  • Pod 生成响应包,源地址是 Pod IP,目标地址是节点 IP;
  • 内核通过 ipvs 连接跟踪表,依次执行反向 DNAT(补 NodePort 端口)反向 SNAT(恢复客户端 IP),最终响应包回到客户端。

三、ipvs 核心组件:虚拟服务与真实服务器

ipvs 的规则由 Virtual Service(VS)Real Server(RS) 组成,kube-proxy 会自动维护这两个组件的映射关系。

1. 虚拟服务(VS)

  • 对应 Kubernetes 的 Service,每个 Service 对应一个 ipvs 虚拟服务;
  • 配置内容:ClusterIP + Port、负载均衡策略(如 rr、wrr);
  • 查看命令:
    ipvsadm -Ln  # 查看所有 ipvs 虚拟服务和真实服务器
    
    示例输出(对应 ClusterIP 10.96.0.1:80):
    TCP  10.96.0.1:80 rr
      -> 10.200.1.2:80  Masq    1      0          0
      -> 10.200.1.3:80  Masq    1      0          0
    

2. 真实服务器(RS)

  • 对应 Service 后端的 Pod,每个 Pod 对应一个 ipvs 真实服务器;
  • 配置内容:PodIP + targetPort
  • 当 Pod 扩缩容或发生故障时,kube-proxy 会自动增删真实服务器。

四、ipvs 模式的配置与验证

1. 配置 kube-proxy 为 ipvs 模式

步骤1:编辑 kube-proxy ConfigMap
kubectl edit configmap kube-proxy -n kube-system

修改 config.conf 中的 modeipvs,并启用 ipvs 相关配置:

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"  # 关键:指定 ipvs 模式
ipvs:
  scheduler: "rr"  # 负载均衡策略:轮询(可选 rr/wrr/lc/wlc/sh)
  strictARP: true  # 开启严格 ARP,提升转发效率
clusterCIDR: "10.200.0.0/16"  # 替换为你的集群 CIDR
步骤2:重启 kube-proxy 使配置生效
kubectl delete pods -n kube-system -l k8s-app=kube-proxy

2. 验证配置是否生效

方式1:查看 kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy | grep "Using ipvs proxy mode"

若日志中出现 Using ipvs proxy mode,说明模式切换成功。

方式2:查看 ipvs 规则
ipvsadm -Ln | grep 10.96.0.1  # 替换为你的 Service ClusterIP

若能看到对应的虚拟服务和真实服务器,说明规则已正确生成。

五、ipvs 与 iptables 模式核心差异对比

对比维度 ipvs 模式 iptables 模式
规则实现 基于内核 ipvs 模块,哈希表存储 基于内核 iptables 模块,链式规则存储
负载均衡策略 丰富(rr/wrr/lc/wlc/sh 等) 简单(仅随机、轮询)
转发性能 高(O(1) 查找效率) 中(O(n) 查找效率,n 为规则数)
大规模集群适配 适合(万级 Pod/Service 无压力) 不适合(规则膨胀后性能下降)
会话保持 支持(sh 策略基于源 IP 绑定) 不支持
依赖组件 需要 ipvsadm 工具和 ip_vs 模块 无需额外组件,内置 iptables

六、常见问题与排查

  1. ipvs 规则未生成

    • 检查节点是否加载 ip_vs 模块:lsmod | grep ip_vs
    • 检查 kube-proxy 日志,是否有 ipvs 相关报错。
  2. Service 访问失败,但 ipvs 规则存在

    • 检查 Pod 是否健康:kubectl get pods -n <namespace>
    • 检查网络插件(如 Calico/Flannel)是否正常,确保 Pod 之间能通信。
  3. 切换 ipvs 模式后性能无提升

    • 确认负载均衡策略是否合适(如大规模集群推荐 wrr);
    • 检查是否开启 strictARP:该配置可减少 ARP 广播,提升转发效率。

七、补充

ipvs 模式下跳过了「显式转发到 ClusterIP:Port」的步骤,直接将请求映射到后端 PodIP:Port——但这并不代表 ClusterIP 没用了,而是 ipvs 把 ClusterIP:Port 封装成了内核态的虚拟服务(Virtual Service),所有转发逻辑都在虚拟服务内部完成,无需额外的地址转换步骤。

下面我用「原理拆解+对比 iptables 模式」的方式,讲清楚 ipvs 是怎么做到的:

一、先明确核心:ipvs 中 ClusterIP 是「虚拟服务标识」,不是转发中间件

在 ipvs 模式下,ClusterIP:Port 的本质是 ipvs 虚拟服务的唯一标识,而非一个需要实际转发的“中间 IP 地址”。

1. kube-proxy 做的关键操作(提前构建映射关系)

当你创建一个 Service 时,kube-proxy 会做两件事:

  • 步骤1:在节点内核中创建一个 ipvs 虚拟服务,这个虚拟服务的“标识”就是 ClusterIP:Port(比如 10.96.0.1:80)。
  • 步骤2:为这个虚拟服务绑定后端 真实服务器(Real Server),也就是 Service 关联的所有 Pod 的 PodIP:targetPort(比如 10.200.1.2:8010.200.1.3:80)。
  • 步骤3:为虚拟服务配置负载均衡策略(如轮询、最少连接)。

你可以用 ipvsadm -Ln 直接看到这个映射关系:

ipvsadm -Ln
# 典型输出
TCP  10.96.0.1:80 rr  # 虚拟服务:ClusterIP:Port + 策略(rr=轮询)
  -> 10.200.1.2:80  Masq    1      0          0  # 真实服务器1:Pod1 IP
  -> 10.200.1.3:80  Masq    1      0          0  # 真实服务器2:Pod2 IP

2. 关键结论:虚拟服务 = ClusterIP + 规则 + 后端 Pod

ipvs 的虚拟服务不是一个“实体 IP”,而是一组内核态的转发规则集合,它的核心作用是:

当请求匹配 ClusterIP:PortNodeIP:NodePort 时,直接根据内置规则选一个 Pod 转发,无需额外的“先到 ClusterIP 再到 Pod”的步骤。

二、ipvs 模式下的转发逻辑(以外部访问 NodePort 为例)

结合之前的流程图,来拆解 为什么不用显式转发到 ClusterIP:Port

场景前提

  • NodePort: 30080 → 绑定到虚拟服务 10.96.0.1:80
  • 虚拟服务策略:轮询

完整转发步骤(无 ClusterIP 中转)

  1. 客户端请求到达节点:数据包 源=192.168.1.100,目标=192.168.60.123:30080
  2. 内核匹配 NodePort 与虚拟服务的绑定关系
    ipvs 内核模块发现:节点IP:30080 是虚拟服务 10.96.0.1:80外部暴露端口,直接关联到这个虚拟服务。
    这一步跳过了「把 NodePort 转发到 ClusterIP」的显式操作,因为 NodePort 和 ClusterIP 属于同一个虚拟服务。
  3. 虚拟服务执行负载均衡选 Pod
    虚拟服务根据轮询策略,选中后端真实服务器 10.200.1.2:80
  4. 直接 DNAT 到 PodIP:Port
    ipvs 内核模块直接将数据包的目标地址从 192.168.60.123:30080 改为 10.200.1.2:80一步到位,无需经过 ClusterIP 中转
  5. SNAT 转发(跨节点时):源地址改为节点 IP,确保响应能回传。

核心关键点

ipvs 模式下,NodePortClusterIP同一个虚拟服务的两个不同访问入口,而非两个独立的转发节点——这就是它能跳过 ClusterIP 中转的根本原因。

三、对比 iptables 模式:为什么 iptables 需要两次 DNAT?

iptables 模式必须走 NodePort → ClusterIP → PodIP 的两步 DNAT,核心原因是 iptables 没有“虚拟服务”的概念,只能通过链式规则逐层转发:

  1. iptables 先创建 NodePort 对应的规则链,把请求转发到 ClusterIP:Port
  2. 再创建 ClusterIP:Port 对应的规则链,把请求转发到 PodIP:Port
  3. 两层规则是独立的,必须逐层匹配,无法直接关联。

而 ipvs 的虚拟服务把 NodePort、ClusterIP、PodIP 三者封装在同一个规则集合中,天然支持“一步转发”,这也是 ipvs 性能远超 iptables 的核心原因。

四、补充:集群内 Pod 访问 Service 时,ClusterIP 怎么工作?

当集群内 Pod 直接访问 ClusterIP:Port 时:

  1. Pod 发送请求 源=Pod1IP,目标=ClusterIP:Port
  2. 内核 ipvs 模块直接匹配到对应的虚拟服务;
  3. 虚拟服务选一个 Pod,直接 DNAT 到 PodIP:Port
  4. 全程无任何中转,转发效率是最高的。

→ 此时 ClusterIP 是 Pod 访问的“直接入口”,但依然是虚拟服务的标识,而非中转 IP。

总结

  1. ipvs 模式下 ClusterIP 没有消失,而是变成了内核态虚拟服务的标识,绑定了 NodePort 和后端 Pod 列表;
  2. 跳过 ClusterIP 中转的核心:NodePort 和 ClusterIP 属于同一个虚拟服务,ipvs 可以直接关联 NodePort 到 PodIP,无需链式转发;
  3. 和 iptables 的本质区别:ipvs 是“面向服务的规则集合”,iptables 是“面向端口的链式规则”,前者天然支持一步转发,后者必须逐层匹配。

、补充:集群内 Pod 访问 Service 时,ClusterIP 怎么工作?
当集群内 Pod 直接访问 ClusterIP:Port 时:

  1. Pod 发送请求 源=Pod1IP,目标=ClusterIP:Port
  2. 内核 ipvs 模块直接匹配到对应的虚拟服务;
  3. 虚拟服务选一个 Pod,直接 DNAT 到 PodIP:Port
  4. 全程无任何中转,转发效率是最高的。

→ 此时 ClusterIP 是 Pod 访问的“直接入口”,但依然是虚拟服务的标识,而非中转 IP。

总结

  1. ipvs 模式下 ClusterIP 没有消失,而是变成了内核态虚拟服务的标识,绑定了 NodePort 和后端 Pod 列表;
  2. 跳过 ClusterIP 中转的核心:NodePort 和 ClusterIP 属于同一个虚拟服务,ipvs 可以直接关联 NodePort 到 PodIP,无需链式转发;
  3. 和 iptables 的本质区别:ipvs 是“面向服务的规则集合”,iptables 是“面向端口的链式规则”,前者天然支持一步转发,后者必须逐层匹配。

简单记:ipvs 把 NodePort + ClusterIP + PodIP 打包成一个“转发黑盒”,外部请求进来直接出到 Pod,中间没有多余步骤。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐