externalTrafficPolicy 到底改变了什么?为什么设成 Local 能保留客户端 IP?代价是什么?健康检查端口是怎么回事?这篇用控制面节点当作集群外的客户端,逐个看后端看到的地址、被丢弃的包和健康检查的返回码。
控制面 → worker:31575(有后端):client=10.244.1.1 rc=0(用时 0.1 秒) 控制面 → worker2:31575(没有后端,要转发到别的节点):client=172.18.0.4 rc=0(用时 0.1 秒) 对照:控制面节点的 IP 是 172.18.0.3;后端看到的不是它——kube-proxy 对集群外来源的 NodePort 流量做了 SNAT
后端 Pod 只在 worker(172.18.0.2)上,客户端是控制面节点 172.18.0.3。默认的 externalTrafficPolicy: Cluster 下,访问 worker:31575 后端看到 client=10.244.1.1(节点上的 Pod 网关地址),访问没有后端的 worker2:31575 看到 client=172.18.0.4(worker2 自己)——请求被转到别的节点。两种情况后端看到的都不是 172.18.0.3:kube-proxy 对集群外来源的 NodePort 流量做了 SNAT,保证回包原路返回。
Service s-local:externalTrafficPolicy=Local;healthCheckNodePort=None(NodePort 类型不分配,LoadBalancer 才有) 控制面 → worker(有后端):client=172.18.0.3 rc=0(用时 0.1 秒)——后端看到的是真实的客户端 172.18.0.3 控制面 → worker2(没有本地后端):rc=28(用时 3.1 秒)——Local 不会转发给别的节点的后端 worker2 的 filter 表里这个 Service 的规则:['-A KUBE-EXTERNAL-SERVICES -p tcp -m addrtype --dst-type LOCAL -m tcp --dport 32459 -j DROP']
改成 Local 后,访问有后端的 worker,后端看到真实的 client=172.18.0.3;访问没有本地后端的 worker2 则 3.1 秒后超时(curl rc=28)。worker2 的 filter 表里这个 NodePort 对应 -A KUBE-EXTERNAL-SERVICES -p tcp -m addrtype --dst-type LOCAL --dport … -j DROP:包直接被丢弃。代价就是流量必须落在有后端的节点上。
Service s-lb(LoadBalancer + Local):healthCheckNodePort=31273 curl worker:31273/ → localEndpoints=1 HTTP=200 curl worker2:31273/ → localEndpoints=0 HTTP=503 curl control-plane:31273/ → localEndpoints=0 HTTP=503 删掉后端 Pod 之后 worker:31273 → localEndpoints=0 HTTP=503(没有本地后端:返回 503,负载均衡器据此把这个节点摘掉)
LoadBalancer + Local 的 Service 会分配 healthCheckNodePort(NodePort 类型不会)。访问 worker 的这个端口返回 localEndpoints=1、HTTP 200;worker2 和 control-plane 返回 localEndpoints=0、HTTP 503;删掉后端 Pod 后 worker 也变成 503。云上的负载均衡器用它判断该把流量发到哪些节点,避免打到会 DROP 的节点上。
Cluster 模式下经过 worker2(没有后端)的连接在 worker2 的 conntrack 里:
tcp 6 119 TIME_WAIT src=172.18.0.3 dst=172.18.0.4 sport=52092 dport=31575 src=10.244.1.178 dst=172.18.0.4 sport=80 dport=4458 [ASSURED] mark=0 use=1
tcp 6 102 TIME_WAIT src=172.18.0.3 dst=172.18.0.4 sport=36830 dport=31575 src=10.244.1.177 dst=172.18.0.4 sport=80 dport=24444 [ASSURED] mark=0 use=1
含义:CP→worker2:31575 先在 worker2 被 DNAT 成 Pod IP:80,再 SNAT 成 worker2 的地址,跨节点送到 worker 上的 Pod;回包原路返回——请求在集群内绕了一圈Cluster 模式经过没有后端的 worker2 时,它的 conntrack 里原方向是 src=172.18.0.3 dst=172.18.0.4 dport=31575,回复方向是 src=10.244.1.178(Pod IP)dst=172.18.0.4:入口被 DNAT 成 Pod IP,回复被 SNAT 成 worker2 的地址,请求从 worker2 跨节点到 worker 上的 Pod 再原路返回——多绕了一跳,也丢了源 IP。