在一个多节点集群中,我们想公开一个处理UDP流量的服务。有两个要求:
- 我们希望服务由多个pod(可能在不同节点上运行)备份,以便水平扩展。
- 服务需要客户端的UDP源IP地址(即应该使用DNAT而不是SNAT)
这可能吗?
我们目前使用NodePort
服务和externalTrafficPolicy: local
服务。这将强制DNAT,但只有在被请求节点上运行的pod才会接收流量。似乎没有办法将负载分散到多个mnode上的多个pod上。
我已经看过这个Kubernetes教程和这篇文章了。
问题
我觉得在面对实际问题之前有必要做一些解释,以便理解为什么事情不像预期的那样工作:
通常使用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
withexternalTrafficPolicy: 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
,并通过使用podAntiAffinity
确保节点之间的适当分布。这种方法需要更多的努力来维护,因为replicas
必须手动调整,但如果您希望在每个节点上拥有不止一个Pod,则可能很有用。
现在来看第二个问题。最简单的解决方案是让客户端自己实现逻辑,并以轮询原则向所有节点发送请求,然而,这不是一个非常实际和/或现实的方法。
通常当使用NodePort
时,它前面仍然有某种负载均衡器来分配负载(这里不考虑Kubernetes服务类型LoadBalancer
)。这似乎是多余的,因为默认情况下NodePort
将在所有pod上分发流量,然而,被请求的节点仍然获得流量,然后发生另一个跳。此外,如果始终只有同一个节点被寻址,那么一旦该节点宕机(无论出于何种原因),流量将永远无法到达任何pod。因此,出于这些原因(以及许多其他原因),负载均衡器应该始终与NodePort
结合使用。要解决这个问题,只需将负载均衡器配置为保留原始客户端的源IP。
此外,根据您运行的云,您有可能能够配置服务类型LoadBalancer
而不是NodePort
(基本上是NodePort
服务+前面的负载均衡器,如上所述),用externalTrafficPolicy: Local
配置它并解决前面描述的第一个问题,您实现了想要做的事情。