MetalLB:裸集群也可以有负载均衡

在 Kubernetes 集群中,可以使用 NodePort、LoadBalancer 和 Ingress 三种方式来将服务暴露给外部访问,但是,在 Kubernetes 却只实现了 NodePort 和 Ingress,要使用 LoadBalancer 就只能使用商用的云平台,比如阿里云、GCP、AWS、AKS等。 如果你是使用 kubeadm 搭建的“裸集群”是没有 LoadBalancer 的,所以裸集群在生产环境下根本不能用,这使得裸集成为了 Kubernetes 生态中的“二等公民”。本文介绍如何利用 MetalLB 让“裸集群”也可以有负载均衡。

原理

MetalLB 在 Kubernetes 内运行,监控服务对象的变化,一旦发现有新的 LoadBalancer 服务运行,并且没有可以申请的负载均衡器之后,便会完成两部分的工作:

  • 分配地址:用户需要在配置中提供一个地址池, MetalLB 会在其中选择地址分配给 LoadBalancer 服务
  • 地址广播:根据不同的配置, MetalLB 会依据 layer2(ARP/NDP) 协议或者 BGP 协议的方式进行地址的广播。

安装

MetaLB 支持两种方式安装,一种是通过 kubectl apply

kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml

另一种是通过 helm

helm install --name metallb stable/metallb

安装好之后会创建一个 metallb-system 的 Namespace,在这个 Namespace 下会生成叫作 controller 的 Deployment 和 叫作 speaker 的 DeamonSet:

$ kubectl get pods -n metallb-system
NAME READY STATUS RESTARTS AGE
controller-cd8657667-dzm74 1/1 Running 0 56m
speaker-h4592 1/1 Running 0 56m
speaker-vxkbp 1/1 Running 0 56m

配置 MetalLB

创建配置文件 layer2-config.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: my-ip-space
protocol: layer2
addresses:
- 192.168.1.240-192.168.1.250

该配置文件指定在metallb-system下生成一个名为config的标准Kubernetes ConfigMap,配置项中包含两条信息,一个是使用了layer2协议,一个是地址的取值范围为 192.168.1.240-192.168.1.250

注意: 地址的取值范围要根据自己的集群IP来配置

将这个 ConfigMap 部署到集群上:

kubectl apply -f layer2-config.yaml

测试

然后我们就可以创建一个 LoadBalancer 类型的服务了,使用官方提供的nginx LoadBalancer 测试:

kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/tutorial-2.yaml

然后我们可以查看其状态:

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-558d677d68-j9x9x 1/1 Running 0 47s
$ kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.102.30.250 192.168.1.240 80:31517/TCP 1d

查看 speaker 的日志观察路由变化:

$ kubectl logs -l component=speaker -n metallb-system
...
{"caller":"main.go:210","event":"endUpdate","msg":"end of service update","service":"default/nginx","ts":"2019-06-27T12:23:01.34994031Z"}
{"caller":"main.go:254","event":"serviceWithdrawn","ip":"","msg":"withdrawing service announcement","reason":"serviceDeleted","service":"default/nginx","ts":"2019-06-27T12:28:44.091902322Z"}
{"caller":"announcer.go:106","event":"deleteARPResponder","interface":"calibacfa2664a3","msg":"deleted ARP responder for interface","ts":"2019-06-27T12:28:49.749392943Z"}
{"caller":"announcer.go:113","event":"deleteNDPResponder","interface":"calibacfa2664a3","msg":"deleted NDP responder for interface","ts":"2019-06-27T12:28:49.749669965Z"}
{"caller":"main.go:159","event":"startUpdate","msg":"start of service update","service":"default/nginx","ts":"2019-06-27T12:22:37.380708331Z"}
{"caller":"main.go:172","event":"endUpdate","msg":"end of service update","service":"default/nginx","ts":"2019-06-27T12:22:37.381369211Z"}
...

访问服务就会看到 nginx 的欢迎页面:

$ curl http://192.168.1.240
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

兼容性

按理来说,MetalLB 是不关心集群究竟使用的是哪种网络插件,但是不同的 CNI 实现可能存在一些隐藏的问题,下表是官方给出的兼容性表:

网络插件 兼容性
Calico 部分支持
Flanner 支持
Kube-router 不支持
Romana 支持
Weave Net 支持