Skip to content

使用 MetalLB 为 Kubernetes 集群服务分配独立 IP

概述

在本地或私有数据中心(On-Premise)部署的 Kubernetes 集群中,暴露服务是一个常见挑战。当将 Service 类型设置为 LoadBalancer 时,其 EXTERNAL-IP 字段会持续显示 <pending> 状态,这是因为自建集群不具备云服务商(AWS、GCP、Azure)提供的自动 IP 分配能力。

MetalLB 是专为裸金属 Kubernetes 集群设计的负载均衡器实现方案。它能够监听 LoadBalancer 类型的 Service,并从预先配置的 IP 地址池中为其分配独立的、可路由的内网 IP 地址。

本文档将详细介绍如何在现有 K8s 集群中部署和配置 MetalLB,并以 Grafana 服务为例,展示如何实现"一个服务一个独立 IP"的目标。

前置条件

在开始之前,需要确保满足以下条件:

  • 正在运行的 Kubernetes 集群
  • 已配置 kubectl 命令行工具并成功连接到集群
  • 已规划好局域网中未被占用的 IP 地址范围(例如:172.18.130.200-172.18.130.240

安装 MetalLB

获取安装清单

访问 MetalLB 官方 GitHub 仓库 获取最新的稳定版本号,然后下载部署所需的 YAML 清单文件。

注意:务必将以下命令中的 v0.15.2 替换为官方推荐的最新稳定版本。

bash
wget https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml

镜像源替换(可选,国内环境推荐)

MetalLB 默认使用的镜像为 quay.io/metallb/controllerquay.io/metallb/speaker。在国内网络环境下可能无法直接拉取,可通过以下步骤替换为国内镜像源。

bash
# 检查清单文件中的镜像地址
grep "image:" metallb-native.yaml

# 批量替换镜像仓库地址
# 示例:将 quay.io 替换为国内镜像代理地址
sed -i 's#quay.io#docker.at9.net/quay.io#g' metallb-native.yaml

部署 MetalLB

执行以下命令在集群中创建 metallb-system 命名空间并部署相关组件:

bash
kubectl apply -f metallb-native.yaml

验证安装

等待所有 Pod 启动完成,检查 metallb-system 命名空间下的 Pod 状态:

bash
kubectl get pods -n metallb-system

正常情况下应该看到一个 controller Pod 和多个 speaker Pod(每个节点一个)处于 Running 状态。

配置 IP 地址池

MetalLB 安装完成后,需要配置可管理和分配的 IP 地址范围。

创建配置文件

创建名为 metallb-config.yaml 的配置文件,定义以下两个关键资源:

  • IPAddressPool:声明可用的 IP 地址范围
  • L2Advertisement:允许 MetalLB 通过 ARP 协议在二层网络上宣告这些 IP 地址,确保局域网内可达
yaml
# metallb-config.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: production-ip-pool
  namespace: metallb-system
spec:
  addresses:
  # 替换为实际规划的 IP 地址段
  - 172.18.130.200-172.18.130.240
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default-l2-advertisement
  namespace: metallb-system
spec:
  # 确保此处名称与 IPAddressPool 名称一致
  ipAddressPools:
  - production-ip-pool

应用配置

bash
kubectl apply -f metallb-config.yaml

配置应用后,MetalLB 将自动扫描集群中 type: LoadBalancerEXTERNAL-IP<pending> 的 Service,并从配置的地址池中分配 IP。

验证配置效果

如果集群中存在处于 <pending> 状态的 Service(例如 ingress-nginx-controller),此时应该已经成功获取到 IP 地址。

bash
# 使用 -w 参数实时观察变化
kubectl get svc -n ingress-nginx -w

预期输出示例:

NAME                       TYPE           CLUSTER-IP  EXTERNAL-IP      PORT(S)                      AGE
ingress-nginx-controller   LoadBalancer   10.96.2.3   172.18.130.200   80:30691/TCP,443:31601/TCP   46h

为 Grafana 服务分配独立 IP

以下步骤演示如何为 Grafana 服务分配独立的内网 IP。

查看当前 Service 状态

首先查看 monitoring 命名空间下的 Service,找到 Grafana 对应的 Service(通常名为 prometheus-grafana):

bash
kubectl get svc -n monitoring

修改 Service 类型

使用 kubectl patch 命令将 Service 类型修改为 LoadBalancer

bash
kubectl patch svc prometheus-grafana -n monitoring -p '{"spec": {"type": "LoadBalancer"}}'

此命令执行后将触发 MetalLB 的 IP 分配流程。

验证 IP 分配

稍等片刻,MetalLB Controller 会检测到新的 LoadBalancer Service 请求,并从 IP 池中分配一个未使用的 IP 地址(例如 172.18.130.201)。

再次查看 Service 状态:

bash
kubectl get svc -n monitoring

成功后的输出示例:

NAME                 TYPE           CLUSTER-IP   EXTERNAL-IP      PORT(S)        AGE
prometheus-grafana   LoadBalancer   10.96.3.13   172.18.130.201   80:31432/TCP   47h

此时可通过浏览器访问 http://172.18.130.201 打开 Grafana 登录页面。

附录:获取 Grafana 初始密码

如果通过 kube-prometheus-stack 或类似方式安装 Grafana,admin 用户的初始密码通常存储在 Secret 中。

使用以下命令获取并解码密码:

bash
kubectl get secret -n monitoring prometheus-grafana -o jsonpath="{.data.admin-password}" | base64 --decode

默认用户名为 admin,配合获取到的密码即可登录。

总结

通过部署 MetalLB 并配置 IP 地址池,成功解决了自建 Kubernetes 集群中 LoadBalancer Service 无法分配外部 IP 的问题。这种方案特别适合内网环境,使得每个服务都能拥有独立的、可直接访问的 IP 地址,简化了服务暴露和网络管理的复杂度。

用心记录,持续学习 | CNB