【Kubernetes】深入浅出 kube-proxy IPVS 模式工作原理与原生网络仿真

admin / Kubernetes / 已更新 2026-09-28 / 1562 字 · 约 19 分钟 / 825 次阅读 /

一、kube-proxy 转发模式演进与性能对比

在 Kubernetes 集群中,Service 是对一组提供相同业务能力 Pod 的抽象,提供统一的集群内虚拟 IP(ClusterIP)和负载均衡。而运行在各节点上的 kube-proxy 组件正是 Service 逻辑的具体实现者。

kube-proxy 在演进过程中主要经历三种模式:

+--------------------------------------------------------------------------+
| 1. userspace 模式 (早期淘汰)                                              |
|    客户端 Pod -> 内核 iptables -> 用户态 kube-proxy 代理 -> 内核 -> 目标 Pod|
|    [致命瓶颈: 两次内核态与用户态数据拷贝,高并发下 CPU 负载极高]            |
+--------------------------------------------------------------------------+
                                    │
                                    ▼
+--------------------------------------------------------------------------+
| 2. iptables 模式 (中小型集群标准)                                         |
|    客户端 Pod -> 内核 iptables (PREROUTING 逐行链式匹配 DNAT) -> 目标 Pod    |
|    [致命瓶颈: 线性复杂度 O(N),万级 Service 规则下规则链刷新延迟严重增加]    |
+--------------------------------------------------------------------------+
                                    │
                                    ▼
+--------------------------------------------------------------------------+
| 3. IPVS 模式 (大规模企业级首选)                                           |
|    客户端 Pod -> 内核 Netfilter -> IPVS 模块 (基于 Hash Map O(1) 匹配)      |
|    结合 ipset 进行连接伪装,保障万级规则下刷新延迟极低,连接不断开          |
+--------------------------------------------------------------------------+

kube-proxy 核心模式关键特性对比

对比维度 userspace 模式 iptables 模式 IPVS 模式
转发机制 用户态进程转发代理 内核 Netfilter 规则链匹配 内核 LVS / IPVS 虚拟服务器
时间复杂度 $O(1)$(但上下文切换重) $O(N)$ 线性链式扫描 $O(1)$ 哈希表查找
规则更新性能 慢 全量覆盖写入(锁竞争严重) 增量原子刷新(0 掉线)
负载均衡算法 轮询/随机 借助 statistic 模块随机/权重 支持 rr, wrr, lc, wlc, sh, dh 等 8 种算法
适用规模 已弃用 < 1,000 个 Service 10,000+ 超大规模 Service 集群

为了脱离 K8s 庞杂的组件封装,直击本质,本文将在 Linux 宿主机上,完全不用 Docker 和 Kubernetes,仅使用底层 iproute2、ipvsadm、ipset 与 iptables 纯手工模拟还原整个 kube-proxy IPVS 转发闭环!


二、纯手工构建底层容器网络仿真环境

我们要模拟的 Kubernetes Service 目标如下:

apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  clusterIP: 10.100.100.100
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080

最终目标:通过 curl 10.100.100.100:8080,无论从宿主机、同节点其他 Pod、还是服务自身 Pod,都能被平滑负载均衡。

1. 开启内核转发与创建虚拟网桥

# 启用内核 IPv4 转发
sysctl -w net.ipv4.ip_forward=1

# 创建 Linux 虚拟网桥模拟集群节点内部网络
ip link add dev bridge_home type bridge
ip addr add 10.0.0.1/24 dev bridge_home
ip link set bridge_home up

2. 创建两个独立的 Network Namespace 模拟 Pod

创建 netns_dustin(IP: 10.0.0.11)与 netns_leah(IP: 10.0.0.21):

# 1. 创建命名空间 netns_dustin 并打通 veth pair
ip netns add netns_dustin
ip link add dev veth_dustin type veth peer name veth_ns_dustin
ip link set dev veth_dustin master bridge_home up
ip link set dev veth_ns_dustin netns netns_dustin

ip netns exec netns_dustin ip link set dev lo up
ip netns exec netns_dustin ip link set dev veth_ns_dustin up
ip netns exec netns_dustin ip addr add 10.0.0.11/24 dev veth_ns_dustin
ip netns exec netns_dustin ip route add default via 10.0.0.1

# 2. 创建命名空间 netns_leah 并打通 veth pair
ip netns add netns_leah
ip link add dev veth_leah type veth peer name veth_ns_leah
ip link set dev veth_leah master bridge_home up
ip link set dev veth_ns_leah netns netns_leah

ip netns exec netns_leah ip link set dev lo up
ip netns exec netns_leah ip link set dev veth_ns_leah up
ip netns exec netns_leah ip addr add 10.0.0.21/24 dev veth_ns_leah
ip netns exec netns_leah ip route add default via 10.0.0.1

# 3. 允许网桥二层流量转发
iptables -t filter -A FORWARD -i bridge_home -j ACCEPT
iptables -t filter -A FORWARD -o bridge_home -j ACCEPT

在两个网络空间分别启动 Python HTTP 模拟业务服务:

# 后台启动两处业务服务
ip netns exec netns_dustin python3 -m http.server 8080 &
ip netns exec netns_leah python3 -m http.server 8080 &

# 验证两个网络命名空间能够互通
ip netns exec netns_leah curl -I http://10.0.0.11:8080
ip netns exec netns_dustin curl -I http://10.0.0.21:8080

三、配置 IPVS 虚拟服务(Virtual Service)

安装 Linux 虚拟服务器调试工具:

apt-get install -y ipvsadm ipset

1. 创建基于 ClusterIP 的 Virtual Service 并挂载 Real Server

# 1. 注册 10.100.100.100:8080 虚拟服务,指定轮询 (rr) 调度
ipvsadm --add-service --tcp-service 10.100.100.100:8080 --scheduler rr

# 2. 挂载真实后端 netns_dustin (10.0.0.11),采用 masquerading (NAT) 模式
ipvsadm --add-server \
  --tcp-service 10.100.100.100:8080 \
  --real-server 10.0.0.11:8080 \
  --masquerading

在宿主机上执行验证:

curl http://10.100.100.100:8080
# 宿主机访问成功!流量已由 IPVS 自动 NAT 并转发给 netns_dustin

四、破解跨空间访问阻断:dummy 接口与网桥三层调用

此时如果进入另一个容器空间 netns_leah 访问该 VIP:

ip netns exec netns_leah curl http://10.100.100.100:8080
# ❌ 请求卡死超时!

1. 为什么需要绑定 dummy 接口?

在 Linux 网络栈中,当网桥(bridge_home)接收到发往一个并不存在于任何本端网卡上的 IP(10.100.100.100)时,网桥不会触发 IPVS 处理。

Kubernetes 的绝妙方案:在宿主机上创建一个 dummy 接口,将 ClusterIP 绑定到该接口上(K8s 中由 kube-ipvs0 承担):

# 创建 dummy 伪网卡并绑定 ClusterIP
ip link add dev dustin-ipvs0 type dummy
ip addr add 10.100.100.100/32 dev dustin-ipvs0

2. 开启 bridge-nf-call-iptables 解决同节点二层短路

[!WARNING] 同节点通信致命陷阱:Pod 发出的数据包在进入 Linux 网桥时原本默认只走纯二层转发,不会上浮进入 Netfilter/conntrack 三层跟踪。这就导致请求包被 DNAT 转换为 PodIP,而目标 Pod 回包时发现源在同一网桥,直接二层回包,绕过了 conntrack 反向 NAT,造成客户端收到不匹配的源 IP 而 RST 断开!

通过开启 bridge-nf-call-iptables,强制网桥上的二层流量透传经过 iptables/conntrack 链路:

modprobe br_netfilter
sysctl -w net.bridge.bridge-nf-call-iptables=1

# 再次验证从 netns_leah 访问
ip netns exec netns_leah curl -I http://10.100.100.100:8080
# ✅ 响应 HTTP/1.0 200 OK,连通成功!

五、破解发卡弯(Hairpin)通信:自我访问死锁

现在测试最后一种极限场景:Pod 自己访问自己的 ClusterIP 服务:

ip netns exec netns_dustin curl http://10.100.100.100:8080
# ❌ 仍然连接超时!

1. 发卡弯原理(Hairpin / 反折流量)

当流量源端和目的端处于同一个网桥端口时,根据标准以太网交换规则,从一个端口收到的报文绝不允许原路反射发回该端口。此时必须在网桥端口开启 Hairpin(发卡弯) 或开启网桥混杂模式(Promiscuous Mode):

# 方式 1:对特定网桥端口开启发夹弯
brctl hairpin bridge_home veth_dustin on

# 方式 2:直接将网桥置为混杂模式 (K8s 常用方案)
ip link set bridge_home promisc on

2. 配合 IPVS 内核 conntrack 连接追踪

仅开启混杂模式还不够,IPVS 必须让连接跟踪感知:

sysctl -w net.ipv4.vs.conntrack=1

六、生产级性能关键:ipset 集合化 SNAT 优化

当 Pod 访问自身时,为了让回包能够原路经由网桥 NAT 返回,必须进行源地址伪装(MASQUERADE)。如果每个 Pod 都在 iptables 里写一条规则,随着 Pod 增多将再次跌入 iptables 性能泥潭。

K8s 的工业级解法:ipset 集合查找

# 1. 创建哈希集合 DUSTIN-LOOP-BACK (存储 dst_ip, dst_port, src_ip)
ipset create DUSTIN-LOOP-BACK hash:ip,port,ip

# 2. 将 Endpoint 添加进集合
ipset add DUSTIN-LOOP-BACK 10.0.0.11,tcp:8080,10.0.0.11

# 3. 仅需配置一条极简 iptables 规则匹配集合!
iptables -t nat -A POSTROUTING \
  -m set --match-set DUSTIN-LOOP-BACK dst,dst,src \
  -j MASQUERADE

自此,自我访问完全打通:

ip netns exec netns_dustin curl -I http://10.100.100.100:8080
# ✅ 响应 HTTP 200 OK!自我访问完美通畅!

七、扩容多后端:完整负载均衡验证

为虚拟服务挂载第二个 Pod 后端 netns_leah 并注入 ipset:

# 1. IPVS 注册第二个后端
ipvsadm --add-server \
  --tcp-service 10.100.100.100:8080 \
  --real-server 10.0.0.21:8080 \
  --masquerading

# 2. ipset 同步注册
ipset add DUSTIN-LOOP-BACK 10.0.0.21,tcp:8080,10.0.0.21

终极轮询调度验证

# 查看当前 IPVS 转发拓扑
ipvsadm -ln

# 循环发送 4 次请求测试负载均衡
for i in {1..4}; do curl -s http://10.100.100.100:8080 | grep -o "Directory listing.*"; done

调度器严格按照 Round-Robin (rr) 在 10.0.0.11 和 10.0.0.21 之间轮流切换,至此全套完整的 kube-proxy IPVS + dummy + ipset 核心实现全部复现成功!

[!TIP] 生产深度启示: 1. IPVS 负责将四层流量以 $O(1)$ 的速度转发给 Endpoint; 2. dummy 接口(kube-ipvs0) 负责在宿主机端承载 ClusterIP,促使网桥流量进入本地三层栈; 3. bridge-nf-call-iptables 确保网桥二层流量调用三层 conntrack,避免同节点回包短路; 4. ipset 将 iptables 的全量链式扫描浓缩为内存哈希表匹配,实现万级规则下的极高并发吞吐。

admin

admin

云原生架构 · 全栈开发

感谢阅读本文!记录系统架构思考与实战复盘。专注于 Python、Django、PostgreSQL 与云原生容器化工程实践。