metalLB 地址池:三种配置方式

3 阅读3分钟

metallb 支持的三种格式



  支持的三种格式:

  addresses:
    - 192.168.1.200-192.168.1.250   # IP 范围(start-end)
    - 10.0.0.0/24                    # CIDR 前缀
    - 172.16.1.100/32                # 单个 IP


# 同一个 pool 可以混合使用,你的配置写法完全正确。




❯ apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.200-192.168.1.250 


❯ metalLB 维护的 ip 的掩码都是/32 么? 另外谁维护的 metalLB 的 LB 的公网 ip 的配置?不是纯控制器模式么?如何维护的?


问题一:MetalLB 维护的 IP 掩码都是 /32 吗?

结论:默认是 /32(IPv6 是 /128),但 BGP 模式可配置。

代码证据 — internal/config/config.go:806-807:

AggregationLength: 32, // IPv4 默认 /32 AggregationLengthV6: 128, // IPv6 默认 /128

在 BGP 模式中,通告路由的前缀长度由 AggregationLength 控制 — speaker/bgp_controller.go:329:

  m := net.CIDRMask(adCfg.AggregationLength, 32) // 默认 /32
  ad := &bgp.Advertisement{
      Prefix: &net.IPNet{
          IP:   lbIP.Mask(m),
          Mask: m,
      },
  }

可通过 BGPAdvertisement CRD 改为更短前缀(如 /24),实现路由聚合。

L2 模式没有掩码概念,它只是对单个 IP 响应 ARP/NDP,本质上就是逐 IP(/32)工作的。


问题二:谁维护公网 IP?MetalLB 不是纯控制面吗?

结论:MetalLB 确实是纯控制面。它从不把 EIP 配置到任何网卡上。

整个代码库中 没有 ip addr add、netlink.AddrAdd 或任何将 IP 绑定到接口的操作。

MetalLB "维护" EIP 的方式完全是欺骗网络层:

┌──────┬──────────────────────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────┐
│ 模式 │                                       MetalLB 做了什么                                       │         MetalLB 没做什么          │
├──────┼──────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────┤
│ L2   │ 在选举节点上用用户态程序直接回复 ARP/NDP 请求,告诉询问者"这个 EIP 的 MAC 是我" (arp.go:112) │ 不把 EIP 加到任何接口,不改路由表 │
├──────┼──────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────┤
│ BGP  │ 通过 FRR/native BGP 向路由器通告 EIP/32 的路由                                               │ 不把 EIP 加到任何接口,不改路由表 │
└──────┴──────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────┘



那流量到了节点后谁处理?

                       MetalLB 的工作到此为止
                            ↓
外部 → [ARP/BGP让EIP可达] → 包到达节点 eth0 → kube-proxy iptables/IPVS → DNAT → Pod
                                                ↑
                                          kube-proxy 的工作

kube-proxy 监听到 Service 的 status.loadBalancer.ingress 被 MetalLB Controller 写入后,自动创建对应的 DNAT 规则(KUBE-SERVICES → KUBE-SVC-xxx → KUBE-SEP-xxx)。

所以完整的协作链是:

  1. MetalLB Controller:分配 EIP,写入 svc.status.loadBalancer.ingress
  2. kube-proxy:看到 ingress IP,创建 iptables DNAT 规则(EIP:port → PodIP:targetPort)
  3. MetalLB Speaker:让 EIP 在网络上可达(ARP 欺骗 或 BGP 路由通告)
  4. 内核 netfilter:执行 kube-proxy 写好的 DNAT 规则,转发流量到 Pod

MetalLB 自始至终不碰数据面,EIP 也从不作为真实地址存在于任何接口上。

metalLB 既有控制器也有 daemonset speaker

纠正:"纯控制面"这个说法不准确

准确的表述是:MetalLB 不操作数据面转发规则(iptables/IPVS),但 Speaker 确实参与了网络面(ARP/BGP)。

MetalLB 的两个组件

  ┌────────────┬───────────────────────┬────────────────────────────────────────┬──────────────────────┐
  │    组件    │       部署形态        │                做了什么                │      属于哪一面      │
  ├────────────┼───────────────────────┼────────────────────────────────────────┼──────────────────────┤
  │ Controller │ Deployment(单副本)  │ 分配 EIP,写入 svc.status              │ 控制面               │
  ├────────────┼───────────────────────┼────────────────────────────────────────┼──────────────────────┤
  │ Speaker    │ DaemonSet(每个节点) │ L2: 用户态 ARP/NDP 应答;BGP: 路由通告 │ 网络面(让 IP 可达) │
  └────────────┴───────────────────────┴────────────────────────────────────────┴──────────────────────┘

Speaker 确实以 DaemonSet 运行在每个节点上,它做的事情是网络可达性层面的:

  • L2 模式:在用户态直接构造 ARP Reply 包(arp.go:112),告诉交换机 "EIP 的 MAC 是我"
  • BGP 模式:通过 FRR 或 native BGP 向路由器通告 EIP/32 路由

我前面说的"纯控制面"想表达的是

MetalLB 不写任何 iptables/IPVS/nftables 规则,不参与实际的包转发(DNAT/SNAT/负载均衡)。这部分完全由 kube-proxy 负责。

Speaker 的边界: 让包能"到达"节点(ARP/BGP) ← MetalLB 做到这里 kube-proxy 的边界: 让包从节点"转发"到 Pod(DNAT) ← MetalLB 不碰这里

所以更准确的说法是:MetalLB 不参与数据面转发,但 Speaker DaemonSet 参与了网络面的 IP 可达性宣告。

前面的表述有误,感谢纠正。