ClusterIP 是怎么转发到 Pod 的?iptables 里到底有哪几条规则?负载均衡是轮询吗?没有后端时会怎样?这篇直接在节点上读 iptables-save 和 conntrack,把一个 Service 从入口到 DNAT 的每一条规则列出来。
入口:KUBE-SERVICES 里匹配 10.96.165.10:80 的规则 → 跳到 KUBE-SVC-AXS6JBVWTHKDNMN2 KUBE-SERVICES 里这条规则:KUBE-SERVICES -d 10.96.165.10/32 -p tcp -m tcp --dport 80 -j KUBE-SVC-AXS6JBVWTHKDNMN2 KUBE-SVC-AXS6JBVWTHKDNMN2 ! -s 10.244.0.0/16 -d 10.96.165.10/32 -p tcp -m tcp --dport 80 -j KUBE-MARK-MASQ KUBE-SVC-AXS6JBVWTHKDNMN2 -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-ZH6EKVWWUW7HGFRS KUBE-SVC-AXS6JBVWTHKDNMN2 -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-LLOQELPUZPPOYHAC KUBE-SVC-AXS6JBVWTHKDNMN2 -j KUBE-SEP-HNWNDCAU6HSM3PPX KUBE-SEP-ZH6EKVWWUW7HGFRS -s 10.244.1.158/32 -j KUBE-MARK-MASQ KUBE-SEP-ZH6EKVWWUW7HGFRS -p tcp -m tcp -j DNAT --to-destination 10.244.1.158:80 KUBE-SEP-LLOQELPUZPPOYHAC -p tcp -m tcp -j DNAT --to-destination 10.244.2.196:80 KUBE-SEP-HNWNDCAU6HSM3PPX -p tcp -m tcp -j DNAT --to-destination 10.244.2.197:80 整个 nat 表有 102 行,其中和 k65/web 有关的 14 行
节点 nat 表里 Service web 的规则:KUBE-SERVICES 里匹配 -d 10.96.165.10 --dport 80 的规则跳到 KUBE-SVC-AXS6JBVWTHKDNMN2;SVC 链先有一条 ! -s 10.244.0.0/16 的规则打上 KUBE-MARK-MASQ(集群外来源要做源地址转换),再有三条 -j KUBE-SEP-…,前两条带 statistic --mode random --probability(0.333 和 0.5),最后一条无条件;每个 KUBE-SEP 链里是 DNAT --to-destination Pod IP:80,第一个 SEP 另有一条对 Pod 自己的 -s Pod IP -j KUBE-MARK-MASQ。整个 nat 表 102 行,和这个 Service 有关的 14 行。
2 个副本:KUBE-SVC-AXS6JBVWTHKDNMN2 里依次的 probability = ['0.50000000000', '(无条件)'](从前到后依次是 1/2,最后一条无条件)
5 个副本:KUBE-SVC-AXS6JBVWTHKDNMN2 里依次的 probability = ['0.20000000019', '0.25000000000', '0.33333333349', '0.50000000000', '(无条件)'](从前到后依次是 1/5, 1/4, 1/3, 1/2,最后一条无条件)
回到 3 个副本,从 cli 发 300 个新连接到 ClusterIP,按后端 Pod 统计:[('r429k', 99), ('xwtmm', 105), ('zmzx5', 96)](每个连接各自抽签,期望 100 左右)2 个副本时 SVC 链的概率依次是 0.5、无条件;5 个副本依次是 0.2、0.25、0.333、0.5、无条件。第 i 条是 1/(n-i+1),保证每个后端总体概率都是 1/n。从 cli 发 300 个新连接,三个后端分别收到 99、105、96 个。每个连接独立抽签,短期内可能不均匀;一个连接建立后由 conntrack 固定在同一个后端。
scale 到 4 个副本:新 Pod 就绪后,iptables 里 KUBE-SVC-AXS6JBVWTHKDNMN2 的 SEP 数从 3 变 4 用了 1.5 秒(从 scale 算起,包含 Pod 启动和就绪) 副本数 0:nat 表里 k65/web 的 SEP 规则 0 行;filter 表里出现 REJECT 规则:['KUBE-EXTERNAL-SERVICES -p tcp -m addrtype --dst-type LOCAL -m tcp --dport 32517 -j REJECT --reject-with icmp-port-unreachable', 'KUBE-SERVICES -d 10.96.165.10/32 -p tcp -m tcp --dport 80 -j REJECT --reject-with icmp-port-unreachable'] 访问 ClusterIP:["wget: can't connect to remote host (10.96.165.10): Connection refused", 'exit=1'];几乎立刻被拒绝(用时 1.1 秒,含 kubectl exec 开销),而不是等超时
scale 到 4 个副本后,从 scale 算起 1.5 秒(含 Pod 启动和就绪)SVC 链里的 SEP 数就从 3 变成 4。副本数降到 0 后,nat 表里不再有这个 Service 的 SEP 规则,filter 表里则出现 REJECT --reject-with icmp-port-unreachable(ClusterIP 与 NodePort 各一条),所以访问 ClusterIP 几乎立即得到 Connection refused,而不是卡到超时。
NodePort 入口:KUBE-NODEPORTS -d 127.0.0.0/8 -p tcp -m tcp --dport 32517 -m nfacct --nfacct-name localhost_nps_accepted_pkts -j KUBE-EXT-AXS6JBVWTHKDNMN2; KUBE-NODEPORTS -p tcp -m tcp --dport 32517 -j KUBE-EXT-AXS6JBVWTHKDNMN2 KUBE-SERVICES 末尾:KUBE-SERVICES -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS conntrack 表里 ClusterIP 的连接(这个节点上):['tcp 6 108 TIME_WAIT src=10.244.1.159 dst=10.96.165.10 sport=56100 dport=80 src=10.244.1.160 dst=10.244.1.159 sport=80 dport=56100 [ASSURED] mark=0 use=1', 'tcp 6 108 TIME_WAIT src=10.244.1.159 dst=10.96.165.10 sport=56568 dport=80 src=10.244.1.158 dst=10.244.1.159 sport=80 dport=56568 [ASSURED] mark=0 use=1']
NodePort 的入口:KUBE-SERVICES 的最后一条是 -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS,发往本机地址的包进入 KUBE-NODEPORTS,--dport 32517 跳到 KUBE-EXT-AXS6JBVWTHKDNMN2(再接同一条 SVC 链),回环地址 127.0.0.0/8 另有一条。conntrack -L 里可以看到一个连接:原方向 dst=10.96.165.10 dport=80,回复方向 src=10.244.1.160——DNAT 的映射记在 conntrack 里,回包自动改写回 ClusterIP。