外部访问k8s Loadbalancer类型service可以避免extra hop吗
intro
k8s的service类型主要分为这几种:
-
ClusterIP。 通过一个集群内部IP暴露服务。
-
NodePort。 在每个Node的IP地址的静态端口侦听(Exposes the Service on each Node's IP at a static port (the NodePort))。
-
LoadBalancer。使用Loadbalancer来对外暴露服务(Exposes the Service externally using an external load balancer. Kubernetes does not directly offer a load balancing component; you must provide one, or you can integrate your Kubernetes cluster with a cloud provider.)。
其中的ClusterIP和NodePort两种比较容易理解,并且都是k8s内置功能,所以相对直观。偏偏最为有用的LoadBalancer模式需要依赖云服务供应商(cloud provider),看起来有点复杂。
在LoadBalancer模式模式下,可以大致理解为
-
k8s自动在后端集群开启NodePort类型Service,也就是所有后端Node的NodePort都是可用状态。
-
Loadbalancer(非k8s内置组件)可以感知到所有Node列表,以及该Service在Node上的NodePort。
这也是标准Loadbalancer的主题功能:从cluster所有的Node中选择一个并将消息转发到该Node的NodePort上。
但是这里有一个和NodePort模式相同的问题:Loadbalancer选择的Node上可能并没有运行Service对应的POD,此时就需要Node再次转发。
这看起来多了额外的一次转发,那么这次extra hop可以避免吗?
如何避免second hop
网上关于这个问题的讨论不少,最终万源归宗,还是回到k8s官方文档的说明。
文档明确说明了可以通过配置.spec.externalTrafficPolicy为缺省的Cluster或者Local类型,并且配置为Local类型可以避免第二跳(avoids a second hop),这也意味着Loadbalancer需要知道Service对应的Pod在哪些Node,从而可以精准的将流量发送到这些Node上。
- .spec.externalTrafficPolicy - denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. There are two available options: Cluster (default) and Local. Cluster obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading. Local preserves the client source IP and avoids a second hop for LoadBalancer and NodePort type Services, but risks potentially imbalanced traffic spreading.
如何知道哪些Node上有Service对应的Pod在运行
这属实有些“灯下黑”了,在externalTrafficPolicy说明的下面,紧接着就有另外一个.spec.healthCheckNodePort配置项。
这个选项可以配置节点健康检查的节点端口(这个和Service提供端口NodePort不同)。Loadbalancer可以通过检测所有Node的该端口是否健康来确定哪些Node上运行了该类型的Service,进而在为指定类型Service做负载均衡时,只把负载发送到healthCheckNodePort端口健康的那些Node的NodePort上。
将externalTrafficPolicy设置为Local时自动激活该特性。如果没有配置.spec.healthCheckNodePort值,服务控制器会从集群的NodePort区间分配一个。
- .spec.healthCheckNodePort - specifies the health check node port (numeric port number) for the service. If you don't specify healthCheckNodePort, the service controller allocates a port from your cluster's NodePort range.
You can configure that range by setting an API server command line option, --service-node-port-range. The Service will use the user-specified healthCheckNodePort value if you specify it, provided that the Service type is set to LoadBalancer and externalTrafficPolicy is set to Local.
Setting externalTrafficPolicy to Local in the Service manifest activates this feature.
externalTrafficPolicy=Local的隐患
从前面的讨论可以知道:当一个Node上只要有一个Service的Pod在运行,那么这个Node就会开启healthCheckNodePort端口;反过来说,一个Node上运行100个该Service的Pod也一样。
这就造成了一个明显可见的问题:可能造成负载不均衡。假设该后端只有两个Node,Node A运行10个Pod,Node B运行1一个,这个信息Loadbalancer并不清楚,只会认为两个Node是同等负载能力,从而造成Node A空闲而Node B过载。
并且因为这个检查是Loadbalancer反向检查(check),可能会存在延迟。
该问题的一个英文讨论
Is it good to have externalTrafficPolicy Cluster or Local is better?
集群中总共有8个节点,但是只有4个有web进程。但是外部loadbalancer把消息发送了所有的8个节点,即使这些节点没有运行web进程。
这意味着进入系统的流量将会经过一个额外的hop才能到达正确的目标pod。
I just realized that I have 8 nodes and only 4 replicas of the web process.... and the external load balancer is sending traffic to all the 8 nodes, even those without a web process (web pod).
This means a second "hop" of the incoming traffic to be redirected to a correct pod.
I know that I can change externalTrafficPolicy to Local to avoid this. But is it better?
Or are there some advantages in keeping the default behavior (externalTrafficPolicy: Cluster)?
outro
externalTrafficPolicy设置为Cluster/Local的效果看起来比较直观,但是在Local模式下Loadbalancer如何精确知道哪些Pod上有目标Servic在运行,这个问题的实现讨论却不多。
具体明确下k8s的实现思路,可以更直观的理解Local可能造成负载不均衡的内在原因及k8s的一些实现原理。
浙公网安备 33010602011771号