kube-proxy的三种工作模式详解
kube-proxy 的 userspace 模式
userspace 是 kube-proxy 最早的工作模式(K8s v1.2 之前的默认模式),核心特点是:所有 Service 的请求转发逻辑,都在 kube-proxy 进程的“用户空间”完成,而非内核空间。简单说,kube-proxy 不只是“规则配置器”,更是“请求转发器”——所有访问 Service 的请求,都要经过它的手。
一、核心原理(先搞懂两个关键概念)
在讲流程前,先明确两个基础概念,避免理解混乱:
- 用户空间 vs 内核空间:
- 内核空间:操作系统内核运行的区域,处理网络、硬件、进程调度等核心操作,速度极快;
- 用户空间:普通进程(如 kube-proxy、nginx)运行的区域,要访问硬件/网络必须通过内核,有额外开销。
- Service 的 ClusterIP:
userspace模式下,kube-proxy 会为每个 Service 绑定一个 ClusterIP,并且在节点上监听这个 ClusterIP + Port。
二、完整工作流程(可视化+通俗解释)
我们以“集群内 Pod 访问 Service(ClusterIP:80)”为例,拆解每一步:

逐步拆解(通俗版)
- 请求发起:集群内一个 Pod 想访问 Service,发起请求
10.96.0.1:80(Service 的 ClusterIP + Port); - 内核转发:请求先到节点内核,内核发现这个 ClusterIP:80 被 kube-proxy 进程监听,于是把请求转发到用户空间的 kube-proxy 进程;
- kube-proxy 选 Pod:kube-proxy 进程维护了一份“Service → 后端 Pod”的映射表(通过监听 K8s API Server 获取 Pod 变化),它会用随机策略选一个健康的 Pod;
- 转发请求:kube-proxy 把请求从用户空间再送回内核空间,转发到选中的 PodIP + targetPort;
- 响应返回: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个,且都是“历史优势”)
- 兼容性极致:支持所有 Linux 内核版本,无需任何内核模块(iptables/ipvs 都需要内核支持);
- 逻辑简单易理解:转发流程是“线性的”,排查问题时直接看 kube-proxy 日志就能定位(比如哪个 Pod 被选中、转发是否失败)。
❌ 缺点(核心问题,也是被淘汰的原因)
- 性能极差:请求要“内核→用户空间→内核”来回切换,额外开销大,QPS(每秒请求数)远低于 iptables/ipvs 模式;
- 单点故障:kube-proxy 进程是转发的核心,一旦进程挂掉,该节点上的 Service 就无法访问(iptables/ipvs 模式下,规则已在核内,kube-proxy 挂了不影响);
- 负载均衡策略单一:只有“随机”一种策略,无法实现轮询、加权、最少连接等灵活策略;
- 端口占用:kube-proxy 需要为每个 Service 监听一个端口,节点端口资源容易耗尽;
- 延迟高:用户空间和内核空间的上下文切换,会增加请求的响应延迟。
五、适用场景(几乎无实用场景)
- 仅用于极老旧的 Linux 内核(不支持 iptables/ipvs 模块);
- 仅用于学习/测试,理解 kube-proxy 的原始设计逻辑;
- 生产环境绝对不推荐使用,哪怕是小集群,也优先用 iptables 模式。
总结
userspace模式的核心是kube-proxy 进程在用户空间完成所有请求转发,是最早期的实现方式;- 最大问题是性能差、有单点故障、策略单一,现在已被 iptables/ipvs 完全取代;
- 理解它的价值在于:对比后能更清楚 iptables/ipvs 模式的优势,以及 K8s 网络组件的演进逻辑。
核心记住:userspace 是“过去式”,只需了解原理,无需在生产环境使用。
kube-proxy 的 iptables 模式
从 v1.2 开始的默认模式(直到 ipvs 模式出现后仍作为基础默认),核心特点是:kube-proxy 不再直接转发请求,而是作为“规则写手”,在节点内核中配置 iptables 规则,由内核直接完成 Service 请求的转发和负载均衡。简单说,kube-proxy 只“写规则”,不“处理请求”——所有转发都在内核空间完成,性能和可靠性大幅提升。
一、核心原理(先搞懂两个关键)
- iptables 是什么:Linux 内核的包过滤/转发工具,规则基于“链(Chain)”和“表(Table)”组织,运行在内核空间,处理网络请求的速度极快;
- kube-proxy 的角色:监听 K8s API Server,实时同步 Service 和 Pod 的变化(比如 Pod 新增/删除、Service 端口变更),并动态更新节点上的 iptables 规则,确保规则始终和集群状态一致。
二、完整工作流程(流程图+逐步拆解)
我们以“集群内 Pod 访问 Service(ClusterIP:80)”为例,先看可视化流程图,再拆解每一步:
逐步拆解(通俗版,无专业术语)
- 请求发起:集群内一个 Pod 发起请求
10.96.0.1:80(Service 的 ClusterIP + Port),请求先到达所在节点的内核; - 内核匹配 iptables 规则:内核在
PREROUTING链中找到针对这个 Service(10.96.0.1:80)的 iptables 规则; - 负载均衡选 Pod:iptables 规则内置了负载均衡逻辑(默认随机,可配置轮询),从 Service 关联的 Pod 列表中选一个健康的 Pod;
- DNAT 地址转换:iptables 把请求的目标地址从
ClusterIP:80转换为选中的PodIP:targetPort(比如 10.200.1.10:80); - 请求转发到 Pod:内核直接把转换后的请求转发到目标 Pod,全程不经过用户空间的 kube-proxy 进程;
- 响应返回: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 中的 mode 为 iptables:
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 等链,说明配置生效。
五、优缺点(为什么是主流,又有哪些不足)
✅ 优点(核心优势)
- 性能远高于 userspace:全程在内核空间处理,无“内核-用户空间”切换,QPS 提升 10 倍以上;
- 无单点故障:iptables 规则在内核中生效,即使 kube-proxy 进程挂掉,已配置的规则依然能正常转发请求;
- 无需额外依赖(基础):几乎所有 Linux 发行版都内置 iptables,无需加载额外内核模块(ipvs 需要
ip_vs模块); - 配置自动化:kube-proxy 自动同步规则,无需手动维护。
❌ 缺点(主要问题)
- 规则膨胀问题:集群内 Service/Pod 数量多(比如上千个)时,iptables 规则会暴增(每条规则是链式匹配),内核查找规则的耗时会变长;
- 负载均衡策略简单:只有“随机”和“轮询”两种,不支持加权、最少连接等高级策略;
- 故障排查复杂:iptables 规则是链式嵌套的,出问题时需要逐层排查(比如
KUBE-SERVICES → KUBE-SVC-XXXX → KUBE-SEP-YYYY),对新手不友好; - 无健康检查:iptables 不会主动检查 Pod 是否健康,即使 Pod 挂了,规则仍会转发请求到该 Pod,导致请求失败(需配合 kubelet 的存活探针,Pod 挂掉后 kube-proxy 才会删除对应规则)。
六、关键细节(新手易踩坑)
- SNAT/DNAT 作用:
- DNAT:修改请求的目标地址(ClusterIP → PodIP),是核心转发逻辑;
- SNAT:修改请求的源地址(PodIP → 节点IP),确保 Pod 能正常返回响应(避免跨节点 Pod 访问时的路由问题)。
- 规则更新时机:当 Service/Pod 发生变化(比如扩缩容、删除),kube-proxy 会在 10 秒内(默认)更新 iptables 规则;
- 与网络插件的配合:Calico/Flannel 等网络插件负责 Pod 之间的网络连通,iptables 负责 Service 转发,两者缺一不可。
总结
iptables模式的核心是kube-proxy 配置内核 iptables 规则,由内核完成请求转发和负载均衡,是目前 K8s 中小规模集群的默认选择;- 优势是性能高、无单点故障、兼容性好,缺点是规则膨胀、策略简单、排查复杂;
- 流程图核心逻辑:请求 → 内核 → 匹配 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. 逐步拆解(通俗版)
- 数据包初始状态:客户端 Pod(Pod1,IP=10.200.1.1)发起请求,数据包的「源地址=10.200.1.1:54321」、「目标地址=10.96.0.1:80」(Service 的 ClusterIP:Port);
- 进入内核 PREROUTING 链:数据包到达节点内核,先经过
PREROUTING链(iptables 的核心链,负责修改目标地址); - 执行 DNAT 转换:iptables 匹配到该 Service 的规则,把数据包的「目标地址」从
10.96.0.1:80改为10.200.1.2:80(后端 Pod2 的 IP:targetPort); - 转发到目标 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. 逐步拆解(通俗版)
- DNAT 之后:数据包已被 DNAT 改为目标=Pod2IP:80,接下来进入
POSTROUTING链(iptables 负责修改源地址的链); - 执行 SNAT 转换:iptables 检测到「客户端 Pod 和目标 Pod 不在同一节点」,把数据包的「源地址」从
10.200.1.1(Pod1 IP)改为192.168.60.123(节点 IP); - Pod2 接收并响应:Pod2 收到数据包(源=节点 IP),处理后生成响应数据包,目标地址是「节点 IP」;
- 反向 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」的完整流程图,这是生产环境最常见的场景:
四、新手易踩坑的关键点
- SNAT 不是必须的:若客户端 Pod 和目标 Pod 在同一节点,K8s 会跳过 SNAT(直接通信),只有跨节点访问时才会触发;
- MASQUERADE 是动态 SNAT:
MASQUERADE会自动使用节点的出口网卡 IP 作为源地址,比固定--to-source更灵活(适合节点有多个 IP 的场景); - K8s 自动管理规则:无需手动配置 DNAT/SNAT,kube-proxy 会根据 Service/Pod 的变化自动更新 iptables 规则;
- 排查思路:若 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,完成闭环 |
核心总结
- DNAT 只改目标地址:步骤2、3都是修改数据包的「目标地址」,核心是把外部请求的 NodePort → Service ClusterIP → Pod IP,实现请求的层层转发;
- SNAT 只改源地址:步骤4修改数据包的「源地址」,核心是让 Pod 响应能精准回传给节点,而不是直接发向外部客户端(避免响应迷路);
- 反向转换是“原路返回”:步骤5、6是响应的反向地址恢复,和请求的地址转换完全对称,确保响应能回到最初的客户端。
五、集群内 Pod 访问 Service 的完整流程图(标注源/目标地址)
场景前提
- 客户端 Pod(Pod1):
IP=10.200.1.10,位于节点 A(192.168.60.123) - Service:
ClusterIP=10.96.0.1,port=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 会被跳过,流程更简单:
- Pod1 请求 →
10.96.0.1:80→ 内核 DNAT 到10.200.1.20:80 - 源地址保持
10.200.1.10,直接转发到 Pod2 - 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:80、10.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); - 查看命令:
示例输出(对应 ClusterIPipvsadm -Ln # 查看所有 ipvs 虚拟服务和真实服务器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 中的 mode 为 ipvs,并启用 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 |
六、常见问题与排查
-
ipvs 规则未生成
- 检查节点是否加载
ip_vs模块:lsmod | grep ip_vs; - 检查 kube-proxy 日志,是否有
ipvs相关报错。
- 检查节点是否加载
-
Service 访问失败,但 ipvs 规则存在
- 检查 Pod 是否健康:
kubectl get pods -n <namespace>; - 检查网络插件(如 Calico/Flannel)是否正常,确保 Pod 之间能通信。
- 检查 Pod 是否健康:
-
切换 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:80、10.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:Port或NodeIP:NodePort时,直接根据内置规则选一个 Pod 转发,无需额外的“先到 ClusterIP 再到 Pod”的步骤。
二、ipvs 模式下的转发逻辑(以外部访问 NodePort 为例)
结合之前的流程图,来拆解 为什么不用显式转发到 ClusterIP:Port:
场景前提
- NodePort:
30080→ 绑定到虚拟服务10.96.0.1:80 - 虚拟服务策略:轮询
完整转发步骤(无 ClusterIP 中转)
- 客户端请求到达节点:数据包
源=192.168.1.100,目标=192.168.60.123:30080 - 内核匹配 NodePort 与虚拟服务的绑定关系
ipvs 内核模块发现:节点IP:30080是虚拟服务10.96.0.1:80的外部暴露端口,直接关联到这个虚拟服务。
→ 这一步跳过了「把 NodePort 转发到 ClusterIP」的显式操作,因为 NodePort 和 ClusterIP 属于同一个虚拟服务。 - 虚拟服务执行负载均衡选 Pod
虚拟服务根据轮询策略,选中后端真实服务器10.200.1.2:80。 - 直接 DNAT 到 PodIP:Port
ipvs 内核模块直接将数据包的目标地址从192.168.60.123:30080改为10.200.1.2:80→ 一步到位,无需经过 ClusterIP 中转。 - SNAT 转发(跨节点时):源地址改为节点 IP,确保响应能回传。
核心关键点
ipvs 模式下,NodePort 和 ClusterIP 是同一个虚拟服务的两个不同访问入口,而非两个独立的转发节点——这就是它能跳过 ClusterIP 中转的根本原因。
三、对比 iptables 模式:为什么 iptables 需要两次 DNAT?
iptables 模式必须走 NodePort → ClusterIP → PodIP 的两步 DNAT,核心原因是 iptables 没有“虚拟服务”的概念,只能通过链式规则逐层转发:
- iptables 先创建
NodePort对应的规则链,把请求转发到ClusterIP:Port; - 再创建
ClusterIP:Port对应的规则链,把请求转发到PodIP:Port; - 两层规则是独立的,必须逐层匹配,无法直接关联。
而 ipvs 的虚拟服务把 NodePort、ClusterIP、PodIP 三者封装在同一个规则集合中,天然支持“一步转发”,这也是 ipvs 性能远超 iptables 的核心原因。
四、补充:集群内 Pod 访问 Service 时,ClusterIP 怎么工作?
当集群内 Pod 直接访问 ClusterIP:Port 时:
- Pod 发送请求
源=Pod1IP,目标=ClusterIP:Port; - 内核 ipvs 模块直接匹配到对应的虚拟服务;
- 虚拟服务选一个 Pod,直接 DNAT 到
PodIP:Port; - 全程无任何中转,转发效率是最高的。
→ 此时 ClusterIP 是 Pod 访问的“直接入口”,但依然是虚拟服务的标识,而非中转 IP。
总结
- ipvs 模式下 ClusterIP 没有消失,而是变成了内核态虚拟服务的标识,绑定了 NodePort 和后端 Pod 列表;
- 跳过 ClusterIP 中转的核心:NodePort 和 ClusterIP 属于同一个虚拟服务,ipvs 可以直接关联 NodePort 到 PodIP,无需链式转发;
- 和 iptables 的本质区别:ipvs 是“面向服务的规则集合”,iptables 是“面向端口的链式规则”,前者天然支持一步转发,后者必须逐层匹配。
、补充:集群内 Pod 访问 Service 时,ClusterIP 怎么工作?
当集群内 Pod 直接访问 ClusterIP:Port 时:
- Pod 发送请求
源=Pod1IP,目标=ClusterIP:Port; - 内核 ipvs 模块直接匹配到对应的虚拟服务;
- 虚拟服务选一个 Pod,直接 DNAT 到
PodIP:Port; - 全程无任何中转,转发效率是最高的。
→ 此时 ClusterIP 是 Pod 访问的“直接入口”,但依然是虚拟服务的标识,而非中转 IP。
总结
- ipvs 模式下 ClusterIP 没有消失,而是变成了内核态虚拟服务的标识,绑定了 NodePort 和后端 Pod 列表;
- 跳过 ClusterIP 中转的核心:NodePort 和 ClusterIP 属于同一个虚拟服务,ipvs 可以直接关联 NodePort 到 PodIP,无需链式转发;
- 和 iptables 的本质区别:ipvs 是“面向服务的规则集合”,iptables 是“面向端口的链式规则”,前者天然支持一步转发,后者必须逐层匹配。
简单记:ipvs 把 NodePort + ClusterIP + PodIP 打包成一个“转发黑盒”,外部请求进来直接出到 Pod,中间没有多余步骤。
更多推荐



所有评论(0)