之前介绍过一些 Ingress 使用,比如 Ingress SSL 透传、Ingress 的多租户。从 Demo 看起来是创建 Ingress 之后,就能从集群外访问服务了。实际上除了 Ingress 的作用以外,还有 Kubernetes Service 和负载均衡器(Load Balancer)参与(当 Service
类型为 LoadBalancer
时)。
这篇文章就来介绍了 Kubernetes LoadBalancer Service 和两个比较典型的负载均衡器的工作原理。
LoadBalancer Service
Service
是 Kubernetes 中的资源类型,用来将一组 Pod 的应用作为网络服务公开。每个 Pod 都有自己的 IP,但是这个 IP 的生命周期与 Pod 生命周期一致,也就是说 Pod 销毁后这个 IP 也就无效了(也可能被分配给其他的 Pod 使用)。而 Service
的 IP(ClusterIP
) 则是在创建之后便不会改变,Service 与 Pod 之前通过 userspace 代理
、iptables
和 ipvs 代理
等手段关联。
LoadBalancer
是 Service
四种类型中的一种,其他三种是 ClusterIP
、NodePort
、ExternalName
。
LoadBalancer
的工作需要搭配第三方的负载均衡器来完成。当我们安装 Ingress 控制器时,会创建一个类型为 LoadBalancer
的 Service
。新创建的 Service
的 EXTERNAL-IP
状态是 pending
,假如没有负载均衡器的话,会一直处于 pending
状态:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
fsm-ingress-pipy-controller LoadBalancer 10.43.188.208 <pending> 80:30508/TCP 8s
在 Demo 中用的是 K3s(默认提供了负载均衡器),这个 Service
会获得 EXTERNAL-IP
,192.168.1.12
和 192.168.1.13
是集群中两个节点的 IP,当我们使用该 IP 地址访问时,流量会进入到 Ingress 控制器的 pod,然后路由到配置对应的 pod 中。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
fsm-ingress-pipy-controller LoadBalancer 10.43.188.208 192.168.1.12,192.168.1.13 80:30508/TCP 47s
如何实现的呢?
K3s 服务端的 controller 运行时会监听集群中 Service
的变更,如果 Service
类型是 LoadBalancer
便为其创建一个 Daemonset
,传入 Service
的 ClusterIP
、Protocol
、Port
等信息。剩下的工作就由这个 Daemonset
来完成了。
而这个 Daemonset
使用的是 klipper-lb 的镜像。
Klipper LB
klipper-lb
是 K3s 的一个开源负载均衡器的实现,实现很简单:使用主机端口(pod.spec.containers.ports.hostPort
与 Service
的端口号相同)来接收流量,并使用 iptables 将接收的流量转发到 Service
的 ClusterIP
。
Pod 启动时使用通过环境变量注入的 Service
的信息,创建 iptables 的 NAT 规则。
![0500b986d9737b43ec378c5c099550b7.png](https://img-blog.csdnimg.cn/img_convert/0500b986d9737b43ec378c5c099550b7.png)
代码量很少:
#!/bin/sh
set -e -x
trap exit TERM INT
for dest_ip in ${DEST_IPS}
do
if echo ${dest_ip} | grep -Eq ":"
then
if [ `cat /proc/sys/net/ipv6/conf/all/forwarding` != 1 ]; then
exit 1
fi
ip6tables -t nat -I PREROUTING ! -s ${dest_ip}/128 -p ${DEST_PROTO} --dport ${SRC_PORT} -j DNAT --to [${dest_ip}]:${DEST_PORT}
ip6tables -t nat -I POSTROUTING -d ${dest_ip}/128 -p ${DEST_PROTO} -j MASQUERADE
else
if [ `cat /proc/sys/net/ipv4/ip_forward` != 1 ]; then
exit 1
fi
iptables -t nat -I PREROUTING ! -s ${dest_ip}/32 -p ${DEST_PROTO} --dport ${SRC_PORT} -j DNAT --to ${dest_ip}:${DEST_PORT}
iptables -t nat -I POSTROUTING -d ${dest_ip}/32 -p ${DEST_PROTO} -j MASQUERADE
fi
done
if [ ! -e /pause ]; then
mkfifo /pause
fi
</pause
那除了 klipper-lb 以外,今天介绍下另一种负载均衡器 metallb。
Metal LB
MetalLB 是裸机 Kubernetes 集群的负载均衡器实现,使用标准路由协议。
注意: MetalLB 目前还是 beta 阶段。
在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer(上)- Layer2
在 Kubernetes 集群中使用 MetalLB 作为 LoadBalancer(下)- BGP
关于 Metal LB,在上面的文章介绍过。其有两种工作模式,这里通过 BGP 的工作模式来进行说明。
与 klipper-lb 不同的时,我们需要预先配置好可用的 IP 地址段。当创建 LoadBalancer
类型的 Service
时,metallb 会从地址池中为其分配 IP 地址。
以 Daemonset 方式运行在各个节点的 speaker
组件会监听 Service
的变更,如果该 Service
已经分配了 IP 地址(service.status.loadBalancer.ingress
),speaker
在完成一些列的校验工作后会向路由器声明该地址。
路由接收到 speaker
的广播后,会更新路由表:使用 speaker
的节点进行该 IP 的路由。
当请求该 IP 地址时,因为无法找到 IP 地址对应的 MAC 地址(没有 ARP 广播),便将请求转给路由器。路由器根据路由表中的记录进行路由,转发到对应的节点上。后续的流程,就是 iptables/kubeproxy 的工作了。
![34b8e68f67f3b15ad624d297c7a5305c.png](https://img-blog.csdnimg.cn/img_convert/34b8e68f67f3b15ad624d297c7a5305c.png)
可能你也看出来,如果目标 pod 不在该节点上 ,还会再有一次转发。这种情况可以通过设置 Service
的 externalTrafficPolicy
来避免。
如果 service.sepc.externalTrafficPolicy
设置为 Local
,只有 Service
的 Endpoints
所在的节点,才会参与广播和路由;否则,所有的节点都会参与广播和路由。
总结
由于篇幅的原因,本文只介绍了两种不同负载均衡器的工作原理,没有深入探索其实现细节。不同云提供商的负载均衡器的实现可能各不相同,不同实现间也各有优缺点。