MetalLB 使用随记

MetalLB 是使用标准路由协议(ARP/BGP)为 bare metal Kubernetes 集群实现的负载平衡器。

MetalLB 的两种模式

L2 模式 (ARP)

L2 模式下,MetalLB 会通过 memberlist 选举出一个 Leader 节点,此节点负责向本地网络宣告 LoadBalancerIP。 从网络的角度来看,这台机器似乎有多个 IP 地址,它会响应来自 LoadBancerIP 的 ARP 请求。 L2 模式最大的优势是它不需要依赖譬如路由器等硬件的依赖便可工作。

  • 优势:通用型,不需要额外的硬件支持
  • 缺点:单节点的带宽限制、稍缓慢的故障转移(10s 左右)

L3 模式 (BGP)

在 BGP 模式下,集群中的每个节点都会与路由器建立 BGP Peer,并使用该会话向集群外部通告集群服务的 LoadBalanceIP。 BGP Router 基于每个不同的连接选择一个下一跳(即集群某个节点,这不同于 L2 模式下所有流量先到达某个 Leader 节点)。

部署 MetalLB

参考文档:https://metallb.io/installation/

1
2
3
helm repo add metallb https://metallb.github.io/metallb
helm repo update
helm -n metallb-system upgrade --install metallb metallb/metallb --create-namespace
1
2
3
4
5
6
root@test-0:~# kubectl get pod -n metallb-system
NAME READY STATUS RESTARTS AGE
metallb-controller-9bf78f497-twzcz 1/1 Running 0 4m56s
metallb-speaker-db2vv 4/4 Running 0 4m56s
metallb-speaker-kt45g 4/4 Running 0 4m56s
metallb-speaker-pjxm9 4/4 Running 0 4m56s

IP Address Pool

顾名思义,池子中的 IP 就是分配给 Service 使用的 VIP。

默认情况下,MetalLB 会自动分配池子中的 IP,这可能会造成浪费,可以在通过 autoAssign: false 避免这个问题,同时为了避免 .0 和 .255 这样的 IP 被分配,可以将 avoidBuggyIPs 设置为 true

1
2
3
4
5
6
7
8
9
10
11
12
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 172.16.16.0/24
autoAssign: false
avoidBuggyIPs: true
EOF

IP 池也支持指定特定的 Namespace 和 Service 使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 172.16.16.0/24
autoAssign: false
avoidBuggyIPs: true
serviceAllocation:
# 权重为 50,value 越小代表权重越高,1 代表最高,0 /没设置 priority 代表最低,优先级相同则随机选择
priority: 50
# IP 可以分配到下面的 Namespace 中,或者包含下面 label 的 Namespace 中
namespaces:
- default
namespaceSelectors:
- matchLabels:
foo: bar
# 在上述匹配的 Namespace 中,匹配标签为 app=nginx 的 Service 才能够分配 IP
serviceSelectors:
- matchExpressions:
- {key: app, operator: In, values: [nginx]}
EOF

L2 Configuration

默认情况下,只有一个节点会被选出来宣告 VIP,这个节点被称为 Leader 节点。

正常情况下所有节点的 Speaker 都可以宣告 VIP,但在某些特殊情况下,VIP 只有在部分节点上能够使用,这时候可以通过使用 L2Advertisement CR 中的 nodeSelector 来实现。

在下面的例子中,first-pool 中的 VIP 只会被 test-1 节点宣告:

1
2
3
4
5
6
7
8
9
10
11
12
13
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
spec:
ipAddressPools:
- first-pool
nodeSelectors:
- matchLabels:
kubernetes.io/hostname: test-1
EOF

也可以指定网卡:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
spec:
ipAddressPools:
- first-pool
nodeSelectors:
- matchLabels:
kubernetes.io/hostname: test-1
interfaces:
- eth0
EOF

Service 使用 VIP

在 Service 指定 IP Address Pool,会从 Pool 中随机获取 IP 使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
kubectl create deployment nginx --image harbor.warnerchen.com/library/nginx:mainline

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
annotations:
metallb.io/ip-allocated-from-pool: first-pool
metallb.universe.tf/address-pool: first-pool
labels:
app: nginx
name: nginx
namespace: default
spec:
ports:
- name: port-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
EOF

即可使用 VIP 访问服务:

同时也可以看到在对应节点进行的宣告:

如果要指定 IP,可以通过下面的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
annotations:
metallb.io/ip-allocated-from-pool: first-pool
metallb.universe.tf/address-pool: first-pool
metallb.universe.tf/loadBalancerIPs: 172.16.16.201
labels:
app: nginx
name: nginx
namespace: default
spec:
ports:
- name: port-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
EOF

IP 也可以被多个 Service 共享使用,通过端口进行区分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
annotations:
metallb.io/ip-allocated-from-pool: first-pool
metallb.universe.tf/address-pool: first-pool
metallb.universe.tf/allow-shared-ip: "nginx"
labels:
app: nginx
name: nginx-1
namespace: default
spec:
ports:
- name: port-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer

---
apiVersion: v1
kind: Service
metadata:
annotations:
metallb.io/ip-allocated-from-pool: first-pool
metallb.universe.tf/address-pool: first-pool
metallb.universe.tf/allow-shared-ip: "nginx"
labels:
app: nginx
name: nginx-2
namespace: default
spec:
ports:
- name: port-81
port: 81
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
EOF

Author

Warner Chen

Posted on

2025-04-22

Updated on

2025-04-22

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.