MetalLB
Nov 25, 2019 23:10 · 976 words · 2 minute read
Kubernetes 没有为非 IaaS 上的集群提供网络负载均衡器的实现(LB 的实现需要调用对应 IaaS 平台的接口)。而 MetalLB 通过提供与标准网络设备集成的负载均衡器来解决这个问题,非 IaaS 集群上对外暴露的服务也可以使用负载均衡。
部署 MetalLB
$ kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.8.1/manifests/metallb.yaml
配置 MetalLB
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 10.211.55.210-10.211.55.250
EOF
addresses
字段填写可用的 IP 段。
测试
1. 部署三副本 flask demo 应用程序
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
labels:
app: flask
spec:
replicas: 3
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask
image: jcdemo/flaskapp:latest
ports:
- containerPort: 5000
2. 创建 LoadBalancer 类型的 Service
$ oc expose deployment/flask-app --name=metallb-app --type=LoadBalancer --port=5000
$ oc get svc
kc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
metallb-app LoadBalancer 10.106.146.179 10.211.55.210 5000:31098/TCP 143m
3. 添加安全组规则
OpenStack 环境对应安全组需要开放 5000 端口
4. 测试负载均衡
执行脚本访问 http://10.211.55.210:5000,同时查看相关 pod 日志。
while :
do
curl http://10.211.55.210:5000
sleep 1
done
原理
CLUSTER-IP
Service 是由 kube-proxy 组件与 iptables 共同实现的。
当创建名为 metallb-app 的 Service 时,kube-proxy 通过 Service 的 Informer 感知到,作为对这个事件的响应,会在宿主机上创建一条 iptables 规则:
$ iptables-save | grep 10.106.146.179
-A KUBE-SERVICES -d 10.106.146.179/32 -p tcp -m comment --comment "default/metallb-app: cluster IP" -m tcp --dport 5000 -j KUBE-SVC-OV3ETQZUIOQJMUSK
目的地址是 10.106.146.179 端口是 5000 的 IP 包,都要跳转到 KUBE-SVC-OV3ETQZUIOQJMUSK 链去进行下一步处理:
$ iptables-save | grep KUBE-SVC-OV3ETQZUIOQJMUSK
-A KUBE-SVC-OV3ETQZUIOQJMUSK -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-LRYTBXUIEJW3QYRZ
-A KUBE-SVC-OV3ETQZUIOQJMUSK -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-TXMJOGKLPMPWQ4TQ
-A KUBE-SVC-OV3ETQZUIOQJMUSK -j KUBE-SEP-MKZ3SRKT3VXPEO23
这是一组随机模式的 iptables 链。KUBE-SEP-LRYTBXUIEJW3QYRZ、KUBE-SEP-TXMJOGKLPMPWQ4TQ、KUBE-SEP-MKZ3SRKT3VXPEO23 指向的最终目的地,就是 Service 代理的三个 flask Pod,这组规则实现了负载均衡(第一条规则被选中的概率就是 1/3;而如果第一条规则没有被选中,那么这时候就只剩下两条规则了,所以第二条规则的 probability 就必须设置为 1/2;类似地,最后一条就必须设置为 1)。
$ iptables-save | grep KUBE-SEP-LRYTBXUIEJW3QYRZ
-A KUBE-SEP-LRYTBXUIEJW3QYRZ -s 10.244.0.32/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-LRYTBXUIEJW3QYRZ -p tcp -m tcp -j DNAT --to-destination 10.244.0.32:5000
$ iptables-save | grep KUBE-SEP-TXMJOGKLPMPWQ4TQ
-A KUBE-SEP-TXMJOGKLPMPWQ4TQ -s 10.244.0.35/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-TXMJOGKLPMPWQ4TQ -p tcp -m tcp -j DNAT --to-destination 10.244.0.35:5000
$ iptables-save | grep KUBE-SEP-MKZ3SRKT3VXPEO23
-A KUBE-SEP-MKZ3SRKT3VXPEO23 -s 10.244.0.36/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-MKZ3SRKT3VXPEO23 -p tcp -m tcp -j DNAT --to-destination 10.244.0.36:5000
这是三条 DNAT 规则,在 PREROUTING 检查点,也就是路由之前,将流入 IP 包的目的地址和端口,改成 –to-destination 所指定目的 IP 和端口,也就是被代理 Pod 的地址和端口。
$ kubectl get pods -L name=flask -o wide
flask-app-56bdccfb59-79d9w 1/1 Running 3 3d8h 10.244.0.35 kube-master-1 <none> <none>
flask-app-56bdccfb59-jf4zl 1/1 Running 3 3d8h 10.244.0.36 kube-master-1 <none> <none>
flask-app-56bdccfb59-z58s6 1/1 Running 3 3d8h 10.244.0.32 kube-master-1 <none> <none>
EXTERNAL-IP
$ iptables-save | grep 10.211.55.210
-A KUBE-SERVICES -d 10.211.55.210/32 -p tcp -m comment --comment "default/metallb-app: loadbalancer IP" -m tcp --dport 5000 -j KUBE-FW-OV3ETQZUIOQJMUSK
与 CLUSTER-IP 类似,目的地址是 10.211.55.210 端口是 5000 的 IP 包,都要跳转到 KUBE-SVC-OV3ETQZUIOQJMUSK 链去进行下一步处理。
MetalLB controller 和 speaker 应用会读入名为 config 的 ConfigMap,addresses
字段为我们预先分配好的可用 IP 地址段。在 Service 对象被创建时,speaker 应用将挑选一个未被分配过的可用 IP 给 Service。