先记住这个答案
当 Service 的 externalTrafficPolicy 为 Cluster(默认)时,kube-proxy 可以跨节点转发流量,转发时执行 SNAT,将源 IP 改为节点地址,客户端 IP 丢失。设为 Local 后,流量只转发到本节点上的后端 Pod,不再跨节点,因此无需 SNAT,保留原始源 IP。代价是若某节点没有本地后端,外部流量经该节点进入时会被丢弃,通常需要云负载均衡器配合健康检查,且各节点后端数不均会导致流量倾斜。
- Local 保留源 IP,Cluster 跨节点时改写
- Local 抑制跨节点转发但负载可能不均
- 需要外部健康检查配合否则会丢流量
两种策略的包转发与源 IP 变化
Cluster 模式下,kube-proxy 通过 iptables 或 IPVS 随机选择一个后端 Pod。如果该 Pod 不在当前节点,数据包必须二次跨节点转发。此时 kube-proxy 会执行 SNAT,把源 IP 改写为当前节点的 IP,这样应答包才能正常返回,但客户端原始地址丢失。
Local 模式下,kube-proxy 只将流量转发给本节点上的 Pod,若本节点无后端则直接丢弃(或由外部负载均衡器避免发送至此节点)。由于没有二次转发,数据包仅做 DNAT 不改变源 IP,客户端 IP 得以保留。这两种行为是内核 NAT 规则差异的直接体现。
需要真实客户端 IP 的日志分析服务
假设集群有 3 个节点,一个 Deployment 运行了 6 个副本,ReplicaSet 均匀调度但随后人工修改导致节点 A 有 4 个 Pod,节点 B 和 C 各 1 个。Service 类型为 LoadBalancer,云厂商 LB 健康检查每 2 秒探测节点上是否有可用后端。若设置 externalTrafficPolicy: Cluster,后端日志中客户端 IP 全为节点 IP,无法区分真实用户。
改为 Local 后,云 LB 健康检查发现三个节点都有可用后端,因此仍向全部节点均匀分发流量(如每个节点各约 1/3)。由于节点 A 有 4 个 Pod,它分到的流量由这 4 个 Pod 均摊,每个 Pod 约获得总流量的 1/12;节点 B 和 C 各只有 1 个 Pod,每个 Pod 各获得约 1/3,导致各 Pod 之间的负载明显倾斜。尽管节点 A 的每个 Pod 压力减小,但整体资源利用不均,源 IP 得以保留,日志分析需求得到满足。
Local 模式的失效条件与代价
Local 模式并非总能保留源 IP。如果外部流量不是通过负载均衡器,而是直接访问节点 NodePort,而该节点没有 Pod,数据包会被 Kubernetes 直接丢弃。即使有外部负载均衡器,如果它不执行健康检查,同样会将流量转发到无 Pod 的节点,导致连接失败。
此外,Local 模式会放大节点故障的影响:当某个节点上的所有 Pod 被驱逐,该节点在健康检查中变为不健康,负载均衡器必须摘除它,但摘除需要时间,期间会有丢包。因此,Local 模式在高可用设计上要求更严格,通常在节点数量大于副本数量且每个节点至少有 1 个副本时才能稳定工作。
容易答错的地方
- 认为 Cluster 模式必然丢失源 IP
- 实际上,Cluster 模式只有发生跨节点转发时才 SNAT。如果随机选择恰好选中本机上的 Pod,数据包仅做 DNAT,源 IP 不变。所以源 IP 是否丢失取决于后端是否在本地,并非绝对。
- 以为 Local 模式必然不会丢包
- Local 模式本身不提供健康检查能力。如果外部流量直接到达没有后端的节点,这些包会被静默丢弃。必须依赖外部负载均衡器的健康检查或自建机制过滤无效节点,否则 Service 的可用性下降。
面试官还会怎么问?
如何验证源 IP 是否被保留?
在 Pod 内运行抓包或检查访问日志,对比入口 IP 与客户端出口 IP。若一致则保留;若变成节点 IP 或负载均衡器 IP,则发生了 SNAT。也可用 kubectl exec 进入容器执行 tcpdump 观察包。
为什么 Local 模式会导致流量不均衡?
云负载均衡器健康检查只区分节点是否有后端,无法感知 Pod 数。由于流量在健康节点间按节点均匀分发,当各节点 Pod 数差异大时,每个节点获得的流量大致相同,因此 Pod 多的节点上每个 Pod 分到的流量较少,Pod 少的节点上每个 Pod 分到的流量较多,造成 Pod 间的负载不均衡,而不是节点间的总流量倾斜。
既要保留源 IP 又要均衡负载该怎么做?
一种方案是使用 externalTrafficPolicy: Local 并配合拓扑分布约束,尽量让每个节点 Pod 数一致。另一种是引入如 Ingress-nginx 的 enable-ssl-passthrough 或使用 L4 LB 直接转发,但更可靠的是在应用层传递 X-Forwarded-For,并在 LB 层做一致性哈希,但这需要额外的组件支持。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。