一、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 的全量链式扫描浓缩为内存哈希表匹配,实现万级规则下的极高并发吞吐。