Kubernetes MetalLB 使用指南

什麼是 MetalLB?

MetalLB 是一個用於裸機 Kubernetes 叢集的負載平衡器實作。它提供了在本地環境中使用 LoadBalancer 類型 Service 的能力,填補了雲端和本地 Kubernetes 環境之間的功能差距。

MetalLB 的主要功能:

  • IP 位址分配:為 LoadBalancer Service 自動分配外部 IP 位址
  • 流量路由:將外部流量路由到正確的 Service 與後端 Pod
  • 高可用性:支援多節點部署與故障轉移
  • 協定支援:支援 Layer 2 (ARP/NDP) 和 BGP 模式

為什麼需要 MetalLB?

在雲端 Kubernetes 環境中,當我們建立 type: LoadBalancer 的 Service,雲端供應商通常會自動配置一組外部 IP 或負載平衡器,讓叢集外部可以連進服務。

但在 Minikube、裸機伺服器或本機實驗環境中,Kubernetes 本身不會幫你分配外部 LoadBalancer IP。這時候如果直接建立 LoadBalancer Service,很常看到 EXTERNAL-IP 一直停在 <pending>

MetalLB 解決的就是這個問題。它會從指定的 IP 位址池分配外部 IP 給 LoadBalancer Service,讓本機或內網 Kubernetes 也能用接近雲端的方式對外暴露服務。

本篇會使用 Minikube 作為示範環境,先啟用 MetalLB,接著配置 192.168.49.100-192.168.49.150 作為 LoadBalancer IP 位址池,最後沿用前面文章建立好的 dockertest Spring Boot 應用程式,透過 MetalLB 取得外部 IP 並測試連線。


本篇範例架構

本次範例會聚焦在下列資源:

  • MetalLB addon:在 Minikube 中提供 LoadBalancer IP 分配能力
  • MetalLB ConfigMap:設定可分配的 LoadBalancer IP 範圍
  • dockertest-loadbalancer Service:透過 MetalLB 取得外部 IP
  • 既有的 dockertest-deployment Pod:接收 Service 轉送進來的流量

整體流量會像這樣:

1
2
3
4
Client
-> 192.168.49.100:7777
-> Service/dockertest-loadbalancer
-> Pod:8080

這裡的 7777 是對外服務埠,8080 是 Spring Boot 容器實際監聽的埠號。

啟用 Minikube MetalLB

先確認 Minikube 的 IP。這個 IP 會決定我們可以選擇哪一段 LoadBalancer IP 範圍。

1
minikube ip

範例環境取得的 Minikube IP 是:

1
192.168.49.2

接著啟用 MetalLB addon:

1
minikube addons enable metallb

啟用後,Minikube 會安裝 MetalLB 的 controller 與 speaker。截圖中可以看到 Minikube 使用 quay.io/metallb/speakerquay.io/metallb/controller 映像,並顯示 The 'metallb' addon is enabled

設定 MetalLB IP 位址池

啟用 addon 後,還需要指定 MetalLB 可以分配哪些 IP 給 LoadBalancer Service。這裡使用 192.168.49.100-192.168.49.150

1
minikube addons configure metallb

互動式設定時輸入:

1
2
-- Enter Load Balancer Start IP: 192.168.49.100
-- Enter Load Balancer End IP: 192.168.49.150

這段 IP 必須與 Minikube 所在網段相容,而且不要與既有主機或服務衝突。以這次環境來說,Minikube IP 是 192.168.49.2,所以示範使用同網段中尚未使用的 192.168.49.100-192.168.49.150

先用 nginx 快速驗證 MetalLB

在部署自己的服務之前,可以先用 nginx 做一個最小驗證,確認 MetalLB 是否真的會分配外部 IP。

1
2
3
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --type=LoadBalancer --port=80
kubectl get svc

如果 MetalLB 正常運作,nginx Service 的 EXTERNAL-IP 就不會停在 <pending>,而是會取得位址池中的其中一個 IP。

截圖中 nginx 取得了 192.168.49.101,代表 MetalLB 已經可以替 LoadBalancer Service 分配 IP。

接著直接使用 curl 測試:

1
curl http://192.168.49.101

如果可以看到 nginx 的 HTML 回應,代表外部 IP 與 Service 導流都已經正常。測試完成後可以移除 nginx 範例資源:

1
2
kubectl delete service nginx
kubectl delete deployment nginx

查看 MetalLB ConfigMap

Minikube addon 會把 MetalLB 設定寫入 metallb-system namespace 底下的 config ConfigMap。可以用下列指令確認目前的 IP 位址池:

1
kubectl get configmap config -n metallb-system -o yaml

截圖中可以看到 ConfigMap 內包含:

1
2
3
4
5
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.49.100-192.168.49.150

如果想要針對 dockertest 服務指定一個清楚的 address pool 名稱,可以建立或調整成下列 ConfigMap。重點是 Service annotation 的 pool 名稱必須與這裡的 name 一致。

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: ConfigMap
metadata:
name: config
namespace: metallb-system
data:
config: |
address-pools:
- name: dockertest-address-pool
protocol: layer2
addresses:
- 192.168.49.100-192.168.49.150

版本提醒

本篇示範的 Minikube addon 使用 ConfigMap 格式設定 MetalLB。若你使用的是較新的 MetalLB 原生安裝版本,可能會改用 IPAddressPoolL2Advertisement 這類 CRD 資源。兩種做法的核心概念相同:先建立可分配的 IP 位址池,再讓 LoadBalancer Service 從該位址池取得外部 IP。

確認 dockertest 工作負載

Namespace、私有 Registry Secret 與 Deployment 的建立方式前面文章已經介紹過,這裡不再重複。MetalLB 這篇只需要確認 dockertest namespace 中已經有可接收流量的 Pod,且 Pod label 能被 Service selector 找到。

1
2
kubectl rollout status deployment/dockertest-deployment -n dockertest
kubectl get pods -n dockertest -l app=dockertest --show-labels

確認 Pod 已經 Ready 後,就可以建立 LoadBalancer Service,讓 MetalLB 替它分配外部 IP。

建立 LoadBalancer Service

最後建立 LoadBalancer Service,讓外部可以透過 MetalLB 分配的 IP 連進 dockertest 應用程式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Service
metadata:
name: dockertest-loadbalancer
namespace: dockertest
annotations:
metallb.universe.tf/address-pool: dockertest-address-pool
spec:
type: LoadBalancer
externalTrafficPolicy: Cluster
sessionAffinity: None
selector:
app: dockertest
ports:
- name: tcp-game
port: 7777
targetPort: 8080
protocol: TCP

這裡有幾個和 MetalLB 相關的重點:

  • type: LoadBalancer:要求 Kubernetes 建立 LoadBalancer 型態的 Service
  • metallb.universe.tf/address-pool:指定使用 MetalLB 中的 dockertest-address-pool
  • port: 7777:外部連線使用的 Service port
  • targetPort: 8080:Pod 內 Spring Boot 容器實際監聽的 port
  • selector.app: dockertest:將流量導向具有 app: dockertest label 的 Pod

如果你維持 Minikube addon 預設產生的 default address pool,可以把 annotation 改成:

1
2
annotations:
metallb.universe.tf/address-pool: default

或是在只有一個 address pool 的情況下先移除 annotation,讓 MetalLB 自動從可用的 pool 分配 IP。

套用 Service 並測試

確認 MetalLB 位址池與 dockertest Deployment 都準備好後,套用 LoadBalancer Service:

1
kubectl apply -f dockertest_loadbalancer.yaml

確認 dockertest-loadbalancer 是否取得外部 IP:

1
kubectl get service dockertest-loadbalancer -n dockertest

如果設定成功,Service 的 EXTERNAL-IP 會取得 MetalLB 分配的 IP,例如 192.168.49.100

接著用 Service 對外 port 測試:

1
curl http://192.168.49.100:7777/hello

預期回應類似:

1
Hello, World! UUID: <uuid>

截圖左側可以看到 dockertest-loadbalancer 取得 192.168.49.100,右側則連續呼叫 http://192.168.49.100:7777/hello,每次都取得 Hello, World! 與不同的 UUID,代表請求已經成功經由 LoadBalancer Service 導入後端 Pod。

常見問題排查

EXTERNAL-IP 一直是 pending

先確認 MetalLB Pod 是否正常:

1
kubectl get pods -n metallb-system

再確認 ConfigMap 或 IPAddressPool 是否有設定可用 IP 範圍:

1
kubectl get configmap config -n metallb-system -o yaml

如果使用新版 CRD,則改查:

1
2
kubectl get ipaddresspools.metallb.io -n metallb-system
kubectl get l2advertisements.metallb.io -n metallb-system

Service 指定的 address pool 找不到

如果 Service 有設定:

1
2
annotations:
metallb.universe.tf/address-pool: dockertest-address-pool

MetalLB 設定中就必須存在同名的 address pool:

1
2
address-pools:
- name: dockertest-address-pool

名稱不一致時,LoadBalancer IP 可能無法成功分配。

EXTERNAL-IP 已取得,但連不到服務

這時候 MetalLB 已經完成 IP 分配,問題通常會回到 Service 與後端 Pod 的對應關係。可以確認 Service selector、Pod label 與 endpoints 是否一致:

1
2
3
kubectl get pods -n dockertest --show-labels
kubectl describe service dockertest-loadbalancer -n dockertest
kubectl get endpoints -n dockertest

如果 endpoints 沒有出現 Pod IP,通常是 selector 對不到 label,或 readinessProbe 還沒有通過。

小結

MetalLB 讓 Minikube 或裸機 Kubernetes 也能使用 type: LoadBalancer 的 Service。這次範例的核心流程是:

  1. 啟用 Minikube MetalLB addon
  2. 設定可分配的 IP 位址池 192.168.49.100-192.168.49.150
  3. 沿用既有 dockertest Deployment,建立 LoadBalancer Service
  4. 透過 192.168.49.100:7777/hello 從叢集外部存取 Spring Boot Pod

只要 MetalLB 的 address pool、Service annotation、Service selector 與 Pod label 都有對齊,就可以在本地 Kubernetes 環境中取得穩定的外部入口 IP,讓開發與測試流程更接近實際部署環境。