kube-proxy 的 ipvs 和 nftables 模式到底和 iptables 有什么不同?Service 在内核里长什么样?规则量差多少?这篇各起一个集群,读 /proc/net/ip_vs 和 nft 规则集,并对比 300 个 Service 之后的规则数量与生效时间。
集群 kx-ipvs:kube-proxy 配置 mode=ipvs
Service web 的 ClusterIP 10.96.33.226(十六进制 0A6021E2),3 个后端 Pod IP 末尾 ['1.2', '1.3', '1.4']
/proc/net/ip_vs 里这个 Service 的条目(0050 是端口 80,调度器 rr = 轮询;Masq = NAT 转发;后端地址也是十六进制):
TCP 0A6021E2:0050 rr
-> 0AF40104:0050 Masq 1 0 0
-> 0AF40103:0050 Masq 1 0 0
-> 0AF40102:0050 Masq 1 0 0
节点上多了一个 dummy 网卡 kube-ipvs0,上面挂着所有 Service 的 ClusterIP:3 个地址;类型:12: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default link/eth
iptables nat 表里 KUBE-SEP 链数:0(ipvs 模式下 DNAT 不再靠 iptables 链)
300 个新连接在三个后端的分布:[100, 100, 100](IPVS 默认调度器 rr 轮询,几乎完全均匀)
再创建 300 个 Service(同一组后端),kube-ipvs0 网卡上新增的 ClusterIP 数(秒, 数量)的变化:[(1, 300)];全部 300 个到齐用了 1.2 秒
最后一个 Service m299 从 apply 起到真的能访问(wget 返回后端主机名)用了 13.8 秒
此时 iptables-save 总行数:85(规则数基本不随 Service 数增长)Service web 在 /proc/net/ip_vs 里是 TCP 0A6021E2:0050 rr(0050 是端口 80 的十六进制,rr 是轮询调度器),下面三个真实服务器,转发方式 Masq(NAT)。节点上多了 dummy 网卡 kube-ipvs0,所有 ClusterIP 都挂在上面,让内核把它们当成本机地址;iptables 的 nat 表里 KUBE-SEP 链数是 0。300 个新连接在三个后端分成 100/100/100——轮询比概率抽签均匀得多。
集群 kx-nft:kube-proxy 配置 mode=nftables
nft list tables:['table ip nat', 'table ip mangle', 'table ip filter', 'table ip6 mangle', 'table ip6 nat', 'table ip6 filter', 'table ip kube-proxy', 'table ip6 kube-proxy', 'table inet kindnet-network-policies']
含 ClusterIP 10.96.45.66 的行:
10.96.45.66 }
10.96.45.66 . tcp . 80 : goto service-GVNVBQGN-k66/web/tcp/,
和 web 有关的链:['service-GVNVBQGN-k66/web/tcp/']
chain service-GVNVBQGN-k66/web/tcp/:meta l4proto tcp dnat ip to numgen random mod 3 map { 0 : 10.244.1.2 . 80, 1 : 10.244.1.3 . 80, 2 : 10.244.1.4
300 个新连接在三个后端的分布:[94, 99, 107](随机)
最后一个 Service m299 从 apply 起到真的能访问(wget 返回后端主机名)用了 15.0 秒
再创建 300 个 Service,kube-proxy 表里新 Service 数(秒, 数量)的变化:[(0, 300)];全部 300 个到齐用了 0.1 秒;kube-proxy 表现在共 1799 行nftables 模式的 kind 集群里多了 table ip kube-proxy。Service web 在入口的 map 里是 10.96.45.66 . tcp . 80 : goto service-GVNVBQGN-k66/web/tcp/(一次查表,不是逐条规则匹配),这条链里是 dnat ip to numgen random mod 3 map { 0 : 10.244.1.2 . 80, 1 : 10.244.1.3 . 80, 2 : 10.244.1.4 . 80 }:用随机数当下标去 map 里取后端。300 个连接分布 94/99/107,同样是随机。
iptables 模式(集群 kx-main;最后一个 Service m299 从 apply 起到真的能访问用了 14.5 秒):新 Service 入口规则数(秒, 数量)的变化:[(0, 600)];全部 300 个到齐用了 0.1 秒;iptables-save 总行数 139 → 4639(+4500,约 15.0 行/Service,每个 Service 有 3 个后端) 对比:ipvs 模式 iptables 总行数 85;nftables 模式 kube-proxy 表 1799 行(用 map,查找不是线性的)
同样再创建 300 个 Service(每个 3 个后端):ipvs 模式下 iptables-save 总行数仍是 85;nftables 的 kube-proxy 表共 1799 行;iptables 模式 iptables-save 总行数从 139 变成 4639,多了 4500 行,约 15 行/Service。但从 apply 到最后一个 Service m299 真的能访问,ipvs 13.8 秒、nftables 15.0 秒、iptables 14.5 秒——几乎一样,说明这个规模下瓶颈在控制面(300 个 Service 的 EndpointSlice 要一个个生成),而不是 kube-proxy 写规则。真正的差别在几千上万个 Service 时,本文没有测。