Kubernetes LoadBalancer系列|MetalLB介绍与配置
MetalLB 概念与介绍
1. 什么是 MetalLB?
MetalLB 是一个专为裸机 Kubernetes 集群设计的开源负载均衡器实现。它的核心目标是解决 Kubernetes 原生 LoadBalancer类型 Service 在物理机/虚拟机集群(非云环境)中无法自动分配外部 IP 的问题。通过在裸机环境中模拟云厂商提供的负载均衡服务,MetalLB 让 Kubernetes Service 能够获得外部可访问的 IP 地址,从而将流量从集群外部路由到内部 Pod。
2. 为什么需要 MetalLB?
在公有云环境(如 AWS、GCP、Azure)中,Kubernetes 集群的 LoadBalancer类型 Service 可以直接调用云厂商的 API 自动创建负载均衡器(如 AWS ELB、GCP Load Balancer),并获得外部 IP。但在裸机集群(如企业自建数据中心、私有云、边缘计算节点)中,没有云厂商提供的 LB 服务,原生 LoadBalancerService 会处于 pending状态,无法分配外部 IP。
MetalLB 的出现填补了这一空白,它通过与底层物理网络交互(如 ARP、BGP 等协议),为 Service 分配外部 IP 并引导流量进入集群,使裸机集群也能具备类似云环境的负载均衡能力。
3. MetalLB 的核心概念
(1) 虚拟 IP(VIP, Virtual IP)
MetalLB 为每个 LoadBalancer类型的 Service 分配一个虚拟 IP(VIP)。这个 VIP 是集群外部可访问的地址,所有发送到该 IP 的流量会被转发到对应的 Service 后端 Pod。
- VIP 通常来自用户指定的 IP 地址池(通过
ConfigMap配置),可以是集群节点所在网络的子网段。 - 一个 VIP 可以绑定到一个或多个 Service(取决于模式)。
(2) 模式(Modes)
MetalLB 支持两种主要工作模式,分别适用于不同的网络环境和规模需求:
| 模式 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Layer 2 | 通过 ARP(IPv4)或 NDP(IPv6)协议,让集群中的某个节点直接“宣告”拥有 VIP。 所有发送到 VIP 的流量会被底层网络路由到该节点,再由节点通过 kube-proxy 转发到 Pod。 | 简单易用,无需额外网络设备支持; 适合小规模集群。 | 单节点瓶颈(所有流量经单个节点); 广播域限制(VIP 仅能在二层网络内可达)。 | 小型集群、二层网络环境。 |
| BGP | 与网络中的 BGP 路由器(如核心交换机、路由器)建立 BGP 会话,动态宣告 VIP 的路由。 路由器将 VIP 的流量路由到所有参与 BGP 的集群节点,节点再通过 kube-proxy 转发到 Pod。 | 无单节点瓶颈(流量可负载分担到多节点); 支持大规模集群; 跨三层网络可达。 | 需要网络设备支持 BGP 协议; 配置复杂度较高。 | 中大型集群、三层网络环境。 |
(3) 组件架构
MetalLB 由两个核心组件组成:
- Controller:负责管理
LoadBalancerService 的生命周期。它会监控集群中的 Service 变化,为需要外部 IP 的 Service 分配 VIP,并协调 Speaker 组件完成 VIP 的宣告。 - Speaker:运行在每个节点上的代理,负责实际执行 VIP 的宣告操作(如发送 ARP 报文或与 BGP 路由器通信)。多个 Speaker 节点协作,确保 VIP 的高可用和流量分发。
4. 工作流程示例
假设我们有一个 LoadBalancer类型的 Service:
- 用户创建 Service 并指定
type: LoadBalancer。 - MetalLB Controller 检测到该 Service,从其管理的 IP 池中分配一个未使用的 VIP。
- 根据配置的模式(如 Layer 2 或 BGP),Controller 通知所有 Speaker 节点关于该 VIP 的信息。
- Speaker 节点执行宣告:
- Layer 2 模式:某个节点通过 ARP 宣告“我拥有该 VIP”,底层网络将 VIP 的流量路由到该节点。
- BGP 模式:所有 Speaker 节点向 BGP 路由器宣告 VIP 的路由,路由器将流量负载分担到所有节点。
- 外部客户端访问 VIP,流量被路由到集群节点,最终通过 kube-proxy 转发到后端 Pod。
5. 关键特性
- 高可用性:通过多节点协作避免单点故障(尤其在 BGP 模式下)。
- 与 Kubernetes 深度集成:依赖 Kubernetes API 监听 Service 变化,自动管理 VIP。
- 灵活的网络适配:支持 Layer 2(二层)和 BGP(三层)两种主流网络模式。
- 开源与轻量:代码开源(Apache 2.0 协议),资源占用低,适合生产环境。
6. 适用场景
- 私有云/本地数据中心:为企业自建的 Kubernetes 集群提供外部访问入口。
- 边缘计算:在边缘节点部署的集群中,通过 MetalLB 暴露边缘服务。
- 混合云环境:补充公有云与本地集群之间的负载均衡需求。
- 替代云厂商 LB 服务:降低对云厂商的依赖,实现跨云/本地的一致网络体验。
MetalLB配置部署
如果你使用的是 IPVS 模式的 kube-proxy,从 Kubernetes v1.14.2 版本开始,你必须启用严格的 ARP 模式。
你可以通过编辑当前集群中的 kube-proxy 配置来实现这一点:
[root@master minio]# kubectl edit configmap -n kube-system kube-proxy
1.找到mode:ipvs
2.# 设置staticARP为true
[root@master minio]# kubectl delete pod kube-proxy-khv7p kube-proxy-p6ghb kube-proxy-xsrbg kube-proxy-zr7pc -n kube-system
pod "kube-proxy-khv7p" deleted
pod "kube-proxy-p6ghb" deleted
pod "kube-proxy-xsrbg" deleted
pod "kube-proxy-zr7pc" deleted
下载 Manifest:官方安装与配置地址
https://metallb.universe.tf/installation/
直接通过清单来安装Controller与speaker
[root@master ~]# mkdir metalLB
[root@master ~]# cd metalLB/
[root@master metalLB]# wget https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml
[root@master metalLB]# kubectl apply -f metallb-native.yaml
namespace/metallb-system created
customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgpadvertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgppeers.metallb.io created
customresourcedefinition.apiextensions.k8s.io/communities.metallb.io created
customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created
customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/servicebgpstatuses.metallb.io created
customresourcedefinition.apiextensions.k8s.io/servicel2statuses.metallb.io created
serviceaccount/controller created
serviceaccount/speaker created
role.rbac.authorization.k8s.io/controller created
role.rbac.authorization.k8s.io/pod-lister created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/controller created
rolebinding.rbac.authorization.k8s.io/pod-lister created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
configmap/metallb-excludel2 created
secret/metallb-webhook-cert created
service/metallb-webhook-service created
deployment.apps/controller created
daemonset.apps/speaker created
validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created[root@master metalLB]# kubectl get pod -n metallb-system
NAME READY STATUS RESTARTS AGE
controller-58fdf44d87-hhp7q 1/1 Running 1 10m
speaker-4x768 1/1 Running 0 10m
speaker-hftdk 1/1 Running 0 10m
speaker-pktgr 1/1 Running 0 10m
speaker-vvvlz 1/1 Running 0 10m
集群部署 MetalLB,部署在 metallb-system 命名空间下。清单中的组件包括:
metallb-system/controller部署。这是负责处理 IP 地址分配的全局控制器。metallb-system/speaker守护进程。这是与您选择的协议通信以使服务可达的组件。- 控制器和协议通信的服务账户,以及组件运行所需的 RBAC 权限。
安装清单不包含配置文件。MetalLB 的组件仍会启动,但会保持空闲状态,直到你开始部署资源。
MetalLB 在配置之前处于空闲状态。这通过在 MetalLB 部署到的同一命名空间(metallb-system)中创建和部署各种资源来完成。
定义分配给负载均衡服务的 IP 地址
为了给服务分配 IP 地址,MetalLB 必须通过 IPAddressPool CR 来指示这样做。所有通过 IPAddressPool 分配的 IP 地址都贡献给了 MetalLB 用于分配 IP 地址的 IP 地址池。
案列
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:name: first-poolnamespace: metallb-system
spec:addresses:- 192.168.10.0/24- 192.168.9.1-192.168.9.5- fc00:f853:0ccd:e799::/124
多个 IPAddressPool 实例可以共存,地址可以通过 CIDR、范围定义,并且可以分配 IPV4 和 IPV6 地址。
发布服务 IP 地址
一旦 IP 地址分配给某个服务,就必须进行发布。
具体的配置取决于您想要使用什么协议来宣告服务 IP。跳转至:
- Layer 2 configuration Layer 2 配置
- BGP configuration BGP 配置
- Advanced BGP configuration
高级 BGP 配置 - Advanced L2 configuration
高级 L2 配置 - Advanced IPAddressPool configuration
高级 IPAddressPool 配置
Layer 2 configuration 二层配置
二层模式是最简单的配置方式:在许多情况下,不需要任何特定协议的配置,只需要 IP 地址。
二层模式不需要将 IP 地址绑定到你的工作节点的网络接口上。它通过直接响应本地网络上的 ARP 请求来工作,向客户端提供机器的 MAC 地址。
例如,以下配置将 IP 范围从 192.168.209.10 到 192.168.209.20 分配给 MetalLB,并配置为 Layer 2 模式:
创建地址池
[root@master metalLB]# cat metallb-ippool.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:name: first-poolnamespace: metallb-system
spec:addresses:- 192.168.209.150-192.168.209.155[root@master metalLB]# kubectl apply -f metallb-ippool.yml
ipaddresspool.metallb.io/first-pool created[root@master metalLB]# kubectl get IPAddressPool -n metallb-system
NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES
first-pool true false ["192.168.209.150-192.168.209.155"]
为了宣传来自 IPAddressPool 的 IP 地址,一个 L2Advertisement 实例必须与 IPAddressPool 相关联。
宣告地址池
[root@master metalLB]# cat metallb-sement.yml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:name: examplenamespace: metallb-system
spec:ipAddressPools: #与指定的地址池关联- first-pool
[root@master metalLB]# kubectl apply -f metallb-sement.yml
l2advertisement.metallb.io/example created[root@master metalLB]# kubectl get L2Advertisement -n metallb-system
NAME IPADDRESSPOOLS IPADDRESSPOOL SELECTORS INTERFACES
example ["first-pool"]
在 L2Advertisement 中不设置 IPAddressPool 选择器,表示该实例与所有可用的 IPAddressPool 相关联。如果存在专门的 IPAddressPool ,并且只有其中一些需要通过 L2 进行通告,那么我们需要通告的 IPAddressPool 列表必须被声明(或者,可以使用标签选择器)。
创建svc测试
[root@master metalLB]# cat nginx-dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentlabels:app: nginx
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:name: nginx-servicelabels:app: nginx
spec:type: LoadBalancerloadBalancerIP: 192.168.209.151selector:app: nginxports:- protocol: TCPport: 80targetPort: 80[root@master metalLB]# kubectl apply -f nginx-dep.yml
deployment.apps/nginx-deployment created
service/nginx-service created[root@master metalLB]# kubectl get pod,svc
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-6bb58fc8c6-7fhhb 1/1 Running 0 21s
pod/nginx-deployment-6bb58fc8c6-gf8jr 1/1 Running 0 21s
pod/nginx-deployment-6bb58fc8c6-k6727 0/1 ContainerCreating 0 21sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d3h
service/nginx-service LoadBalancer 10.98.114.41 192.168.209.151 80:32102/TCP 21s
测试访问

Advanced L2 configuration 高级 L2 配置
限制服务可以宣布的节点集
在 L2 模式下,仅选举一个节点来宣布 IP。通常情况下,所有正在运行 Speaker 的节点都适合任何给定的 IP。可能存在某些场景,只有部分节点暴露给特定网络,因此限制这些节点作为服务 IP 的潜在入口点可能很有用。这是通过在 L2Advertisement CR 中使用节点选择器来实现的。

在这个例子中,NodeA 和 NodeB 暴露在子网 A 中,而节点 C 暴露在子网 B 中。为了限制特定广告的节点集,必须设置节点选择器:
定义要分配的地址池
[root@master metalLB]# mkdir L2_gj
[root@master metalLB]# cd L2_gj/
[root@master L2_gj]# cat metallb-ipool.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:name: L2-poolnamespace: metallb-system
spec:addresses:- 192.168.209.145-192.168.209.149[root@master L2_gj]# kubectl apply -f metallb-ipool.yml
ipaddresspool.metallb.io/ltwo-pool created
[root@master L2_gj]# kubectl get IPAddressPool -n metallb-system
NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES
ltwo-pool true false ["192.168.209.145-192.168.209.149"]
根据节点选择器为不同节点宣告地址
[root@master L2_gj]# cat metallb-sement.yml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:name: l2-examplenamespace: metallb-system
spec:ipAddressPools:- ltwo-pool #指定要关联的地址池nodeSelectors: #指定节点选择器- matchLabels: #通过标签匹配节点kubernetes.io/hostname: node-1- matchLabels:kubernetes.io/hostname: node-2[root@master L2_gj]# cat metallb-sement-3.yml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:name: l3-examplenamespace: metallb-system
spec:ipAddressPools:- first-pool #指定第一次创建的地址池nodeSelectors:- matchLabels:kubernetes.io/hostname: node-3[root@master L2_gj]# kubectl apply -f metallb-sement.yml
l2advertisement.metallb.io/l2-example created
[root@master L2_gj]# kubectl apply -f metallb-sement-3.yml
l2advertisement.metallb.io/l3-example created
[root@master L2_gj]# kubectl get l2advertisement -n metallb-system
NAME IPADDRESSPOOLS IPADDRESSPOOL SELECTORS INTERFACES
l2-example ["ltwo-pool"]
l3-example ["first-pool"][root@master L2]# kubectl get ipaddresspool -n metallb-system
NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES
first-pool true false ["192.168.209.150-192.168.209.155"]
ltwo-pool true false ["192.168.209.145-192.168.209.149"]
通过这种方式,所有来自 ltwo-pool 的 IP 地址只能通过 Node-1 和 Node-2访问,并且只会选择其中一个节点来暴露该 IP 地址。另一方面,来自 first-pool 的 IP 地址将始终通过 Node-3 暴露。
MetalLB 是裸机 Kubernetes 集群暴露服务的核心工具,通过模拟云环境负载均衡能力,解决了原生 LoadBalancerService 无法在物理机/虚拟机集群中工作的问题。无论是小规模测试还是大规模生产环境,MetalLB 都提供了灵活的解决方案(Layer 2 或 BGP 模式),是构建混合云、本地云基础设施的重要组件。
