在 Kubernetes 中保留 SourceIP 地址并分配负载

Preserve SourceIP address in Kubernetes and distribute the load

在多节点集群中,我们想要公开处理 UDP 流量的服务。有两个要求:

  1. 我们希望服务由多个 pods(可能 运行 在不同节点上)备份,以便水平扩展。
  2. 服务需要客户端的 UDP 源 IP 地址(即应该使用 DNAT 而不是 SNAT)

这可能吗?

我们目前在 externalTrafficPolicy: local 中使用 NodePort 服务。这会强制执行 DNAT,但只有请求节点上的 pod 运行 正在接收流量。 似乎没有办法将负载分散到多个 mnode 上的多个 pods。

我已经看过 this Kubernetes tutorial and also this article here

问题

我觉得在面对实际问题之前需要进行一些解释,以便理解为什么 事情没有按预期工作:

通常使用 NodePort 时会在集群中的每个节点上公开一个端口。当调用 node1:port 时,流量将(与 ClusterIP 类型相同)转发到与 selector 匹配的一个 Pod,而不管该 Pod 在 node1 或另一个节点。

棘手的部分来了。 使用 externalTrafficPolicy: Local 时,到达没有 Pod 的节点的包将被丢弃。 也许下图以更易于理解的方式解释了该行为。

NodePort 默认为 externalTrafficPolicy: Cluster:

package --> node1 --> forwards to random pod on any node (node1 OR node2 OR ... nodeX)

NodePortexternalTrafficPolicy: Local:

package --> node1 --> forwards to pod on node1 (if pod exists on node1)

package --> node1 --> drops package (if there is no pod on node1)

因此,本质上要在使用 externalTrafficPolicy: Local 时能够正确分配负载,需要解决两个主要问题:

  1. 每个节点上必须有一个 Pod 运行 才能不丢弃包
  2. 客户端必须将包发送到多个节点才能分配负载

解决方法

使用 DaemonSet 可以很容易地解决第一个问题。它将确保 Pod 的一个实例在集群中的每个节点上运行。

或者,也可以使用简单的 Deployment,手动管理 replicas 并确保在节点 by using podAntiAffinity 之间正确分配。这种方法需要更多的维护工作,因为 replicas 必须手动调整,但如果你想在每个节点上拥有不止 1 个 Pod,它会很有用。

现在开始第二期。 最简单的解决方案是让客户端自己实现逻辑,并以循环原则向所有节点发送请求,但是,这不是一个非常实用的 and/or 现实的方法。

通常在使用NodePort的时候,前面还有某种负载均衡器来分配负载(这里不考虑Kubernetes服务类型LoadBalancer)。这似乎是多余的,因为默认情况下 NodePort 无论如何都会在所有 Pods 之间分配流量,但是,被请求的节点仍然会获得流量,然后会发生另一跳。此外,如果始终只有同一个节点被寻址,一旦该节点出现故障(无论出于何种原因),流量将永远不会到达 Pods 中的任何一个。因此,出于这些(以及许多其他原因),负载均衡器应该 始终 NodePort 结合使用。要解决此问题,只需配置负载均衡器以保留原始客户端的源 IP。

此外,根据您 运行 使用的云,您有机会配置服务类型 LoadBalancer 而不是 NodePort(这基本上是一个NodePort 服务 + 前面描述的负载均衡器),用 externalTrafficPolicy: Local 配置它并解决前面描述的第一个问题,你实现了你想要做的。