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-loadbalancerService:透過 MetalLB 取得外部 IP- 既有的
dockertest-deploymentPod:接收 Service 轉送進來的流量
整體流量會像這樣:
1 | Client |
這裡的 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/speaker 與 quay.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 | -- Enter Load Balancer Start IP: 192.168.49.100 |
這段 IP 必須與 Minikube 所在網段相容,而且不要與既有主機或服務衝突。以這次環境來說,Minikube IP 是 192.168.49.2,所以示範使用同網段中尚未使用的 192.168.49.100-192.168.49.150。
先用 nginx 快速驗證 MetalLB
在部署自己的服務之前,可以先用 nginx 做一個最小驗證,確認 MetalLB 是否真的會分配外部 IP。
1 | kubectl create deployment nginx --image=nginx |
如果 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 | kubectl delete service nginx |
查看 MetalLB ConfigMap
Minikube addon 會把 MetalLB 設定寫入 metallb-system namespace 底下的 config ConfigMap。可以用下列指令確認目前的 IP 位址池:
1 | kubectl get configmap config -n metallb-system -o yaml |
截圖中可以看到 ConfigMap 內包含:
1 | address-pools: |
如果想要針對 dockertest 服務指定一個清楚的 address pool 名稱,可以建立或調整成下列 ConfigMap。重點是 Service annotation 的 pool 名稱必須與這裡的 name 一致。
1 | apiVersion: v1 |
版本提醒
本篇示範的 Minikube addon 使用 ConfigMap 格式設定 MetalLB。若你使用的是較新的 MetalLB 原生安裝版本,可能會改用
IPAddressPool與L2Advertisement這類 CRD 資源。兩種做法的核心概念相同:先建立可分配的 IP 位址池,再讓 LoadBalancer Service 從該位址池取得外部 IP。
確認 dockertest 工作負載
Namespace、私有 Registry Secret 與 Deployment 的建立方式前面文章已經介紹過,這裡不再重複。MetalLB 這篇只需要確認 dockertest namespace 中已經有可接收流量的 Pod,且 Pod label 能被 Service selector 找到。
1 | kubectl rollout status deployment/dockertest-deployment -n dockertest |
確認 Pod 已經 Ready 後,就可以建立 LoadBalancer Service,讓 MetalLB 替它分配外部 IP。
建立 LoadBalancer Service
最後建立 LoadBalancer Service,讓外部可以透過 MetalLB 分配的 IP 連進 dockertest 應用程式。
1 | apiVersion: v1 |
這裡有幾個和 MetalLB 相關的重點:
type: LoadBalancer:要求 Kubernetes 建立 LoadBalancer 型態的 Servicemetallb.universe.tf/address-pool:指定使用 MetalLB 中的dockertest-address-poolport: 7777:外部連線使用的 Service porttargetPort: 8080:Pod 內 Spring Boot 容器實際監聽的 portselector.app: dockertest:將流量導向具有app: dockertestlabel 的 Pod
如果你維持 Minikube addon 預設產生的 default address pool,可以把 annotation 改成:
1 | annotations: |
或是在只有一個 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 | kubectl get ipaddresspools.metallb.io -n metallb-system |
Service 指定的 address pool 找不到
如果 Service 有設定:
1 | annotations: |
MetalLB 設定中就必須存在同名的 address pool:
1 | address-pools: |
名稱不一致時,LoadBalancer IP 可能無法成功分配。
EXTERNAL-IP 已取得,但連不到服務
這時候 MetalLB 已經完成 IP 分配,問題通常會回到 Service 與後端 Pod 的對應關係。可以確認 Service selector、Pod label 與 endpoints 是否一致:
1 | kubectl get pods -n dockertest --show-labels |
如果 endpoints 沒有出現 Pod IP,通常是 selector 對不到 label,或 readinessProbe 還沒有通過。
小結
MetalLB 讓 Minikube 或裸機 Kubernetes 也能使用 type: LoadBalancer 的 Service。這次範例的核心流程是:
- 啟用 Minikube MetalLB addon
- 設定可分配的 IP 位址池
192.168.49.100-192.168.49.150 - 沿用既有
dockertestDeployment,建立 LoadBalancer Service - 透過
192.168.49.100:7777/hello從叢集外部存取 Spring Boot Pod
只要 MetalLB 的 address pool、Service annotation、Service selector 與 Pod label 都有對齊,就可以在本地 Kubernetes 環境中取得穩定的外部入口 IP,讓開發與測試流程更接近實際部署環境。