metalLB 指定 IP 并且共享 IP

3 阅读3分钟

注:spec.loadBalancerIP 字段在 Kubernetes v1.24+ 已被标记为 deprecated,MetalLB 推荐使用注解方式指定 IP。

❯   annotations:
    metallb.universe.tf/loadBalancerIPs: "10.210.10.186"
    metallb.universe.tf/allow-shared-ip: "10.210.10.186" # 只用 metallb.universe.tf/allow-shared-ip 是否可以实现指定ip和共享 ip?

结论:不行。allow-shared-ip 单独无法指定 IP。

两个注解的职责完全不同:

  ┌─────────────────────────────────────┬────────────────────────────────────────────────────┐
  │                注解                 │                        作用                        │
  ├─────────────────────────────────────┼────────────────────────────────────────────────────┤
  │ metallb.universe.tf/loadBalancerIPs │ 指定要分配哪个 IP                                  │
  ├─────────────────────────────────────┼────────────────────────────────────────────────────┤
  │ metallb.universe.tf/allow-shared-ip │ 允许多个 Service 共享同一个 IP(值是共享组的 key) │
  └─────────────────────────────────────┴────────────────────────────────────────────────────┘

原因分析

allow-shared-ip 的值(如 "10.210.10.186")只是一个任意字符串作为分组标识,MetalLB 用它来判断哪些 Service 可以共享 IP,但它不会被解析为 IP 地址。你写 "my-group" 和 "10.210.10.186" 效果一样——都只是分组 key。

如果只写 allow-shared-ip 而不指定 loadBalancerIPs:

  • MetalLB 会从地址池中随机分配一个 IP
  • 同组的后续 Service 会共享第一个被分配的 IP
  • 但你无法控制具体是哪个 IP

正确用法

要同时实现指定 IP + 共享 IP,两个注解都需要:

  metadata:
    annotations:
      metallb.universe.tf/loadBalancerIPs: "10.210.10.186"    # 指定 IP
      metallb.universe.tf/allow-shared-ip: "shared-group-1"   # 允许共享(key 建议用语义化名称)

共享的前提条件(所有条件必须同时满足):

  1. allow-shared-ip 的值相同
  2. Service 的端口不冲突(不能有相同 protocol + port)
  3. 如果指定了 loadBalancerIPs,必须是同一个 IP

allow-shared-ip 只是一个任意字符串作为分组标识, 这个是不是缓存了 allow-shared-ip 对应的 external ip,因为缓存会导致实现 指定 external ip 的效果?

● 你的直觉有道理,但本质不是"缓存",而是 MetalLB 的 IP 共享分配逻辑。

实际机制

MetalLB 分配 IP 时的逻辑大致是:

  新 Service 进来,带有 allow-shared-ip: "group-x"
    → 查找已有分配中是否存在 sharing key = "group-x" 的 Service
      → 如果存在,且端口不冲突 → 复用同一个 IP
      → 如果不存在 → 从地址池中分配一个新 IP

所以你描述的场景确实可以工作:

  时间线:
  1. Service A: loadBalancerIPs=10.210.10.186 + allow-shared-ip="group-1"
     → 分配到 10.210.10.186 ✓

  2. Service B: 只有 allow-shared-ip="group-1"(无 loadBalancerIPs)
     → MetalLB 发现 group-1 已关联 10.210.10.186 → 复用 → 得到 10.210.10.186 ✓

但这不等于"指定 IP"

这个机制脆弱且不可靠,原因:

  ┌────────────────────────────────────────┬─────────────────────────────────────┐
  │                  场景                  │                结果                 │
  ├────────────────────────────────────────┼─────────────────────────────────────┤
  │ Service A 先被删除,Service B 重新调谐 │ B 可能被分配随机 IP                 │
  ├────────────────────────────────────────┼─────────────────────────────────────┤
  │ Service B 先于 A 创建                  │ B 得到随机 IP,A 反而可能因冲突失败 │
  ├────────────────────────────────────────┼─────────────────────────────────────┤
  │ 集群重启,Service 恢复顺序不确定       │ IP 分配结果不可预测                 │
  ├────────────────────────────────────────┼─────────────────────────────────────┤
  │ Service A 的 IP 因池变更不再可用       │ 整个组的分配全部失效                │
  └────────────────────────────────────────┴─────────────────────────────────────┘

结论

  • 表面上:allow-shared-ip 的组内复用机制确实产生了"间接指定 IP"的效果
  • 本质上:这是依赖运行时状态顺序的副作用,不是声明式的 IP 指定
  • 正确做法:每个需要固定 IP 的 Service 都显式声明 loadBalancerIPs,这是幂等且可靠的
  # 推荐:每个 Service 都显式声明,不依赖隐式行为
  metadata:
    annotations:
      metallb.universe.tf/loadBalancerIPs: "10.210.10.186"
      metallb.universe.tf/allow-shared-ip: "group-1"

一句话:显式优于隐式,不要依赖分配顺序的副作用来代替声明式配置。