在 Kubernetes 中保留 SourceIP 地址并分配负载
Preserve SourceIP address in Kubernetes and distribute the load
在多节点集群中,我们想要公开处理 UDP 流量的服务。有两个要求:
- 我们希望服务由多个 pods(可能 运行 在不同节点上)备份,以便水平扩展。
- 服务需要客户端的 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)
NodePort
与 externalTrafficPolicy: 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
时能够正确分配负载,需要解决两个主要问题:
- 每个节点上必须有一个 Pod 运行 才能不丢弃包
- 客户端必须将包发送到多个节点才能分配负载
解决方法
使用 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
配置它并解决前面描述的第一个问题,你实现了你想要做的。
在多节点集群中,我们想要公开处理 UDP 流量的服务。有两个要求:
- 我们希望服务由多个 pods(可能 运行 在不同节点上)备份,以便水平扩展。
- 服务需要客户端的 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)
NodePort
与 externalTrafficPolicy: 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
时能够正确分配负载,需要解决两个主要问题:
- 每个节点上必须有一个 Pod 运行 才能不丢弃包
- 客户端必须将包发送到多个节点才能分配负载
解决方法
使用 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
配置它并解决前面描述的第一个问题,你实现了你想要做的。