MetalLB Layer2 vs BGP: Address Pool Setup - 夜莺博客

MetalLB Layer2 vs BGP: Address Pool Setup

In a cloud cluster, type: LoadBalancer just works because the provider hands out an IP. On bare metal nothing does that for you — the service sits forever at EXTERNAL-IP: <pending>. MetalLB fills that gap: it owns a pool of addresses you control and advertises them to the network, either by answering ARP/ND for the whole pool (Layer2 mode) or by peering with your routers and advertising the addresses over BGP. Choosing the wrong mode is the difference between "works fine" and "all traffic hits one node".

Layer2 vs BGP in One Table

Layer2 mode BGP mode
How the IP is advertised One elected node answers ARP/ND for the load-balancer IP Every node peers with a router and advertises the IP as a host route
Traffic distribution All inbound traffic enters via the elected node, then is forwarded inside the cluster — no true load spreading Router ECMP spreads flows across nodes (with FRR mode and consistent hashing)
Failover Leader election + gratuitous ARP; failover time depends on ARP refresh and neighbour caches BGP withdraw/re-converge; can be sub-second with BFD
Requirements Nodes must share the layer-2 segment; no router configuration Router config (ASN, peering, password), address space that can be advertised
Best for Home labs, small clusters, quickly exposing a service on the local VLAN Production, multi-rack, high throughput — especially with externalTrafficPolicy: Local

Install and Define the Pool

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
kubectl -n metallb-system get pods            # controller + speaker must be Running

Address pools are the modern API. Keep "cheap" and "expensive" ranges as separate pools, and disable automatic allocation on the expensive one so only services that ask for it get an address from it:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: cheap
  namespace: metallb-system
spec:
  addresses:
    - 192.168.10.0/24
  autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: expensive
  namespace: metallb-system
spec:
  addresses:
    - 42.176.25.64/30
  autoAssign: false          # only services that request this pool by name

A service selects a specific pool with an annotation:

apiVersion: v1
kind: Service
metadata:
  name: ingress-public
  annotations:
    metallb.universe.tf/address-pool: expensive
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  ports:
    - port: 443
      targetPort: 8443

Then tell MetalLB how to advertise it. Layer2 needs an L2Advertisement; BGP needs a BGPPeer plus a BGPAdvertisement:

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2-default
  namespace: metallb-system
spec:
  ipAddressPools:
    - cheap
---
apiVersion: metallb.io/v1beta1
kind: BGPPeer
metadata:
  name: rack-tor
  namespace: metallb-system
spec:
  myASN: 64512
  peerASN: 64500
  peerAddress: 192.168.10.1
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: bgp-default
  namespace: metallb-system
spec:
  ipAddressPools:
    - expensive

Verify That It Really Works

kubectl get svc                                # EXTERNAL-IP must leave <pending>
kubectl get ipaddresspool -n metallb-system
kubectl -n metallb-system logs deploy/metallb-controller --tail=50
kubectl -n metallb-system logs ds/metallb-speaker --tail=50   # speaker elects / announces

# from a client outside the cluster
curl -k https://42.176.25.64/
arping -I eth0 192.168.10.10                   # Layer2: which MAC answers?
ip route get 42.176.25.64                      # BGP: which next-hop is used?

Pitfalls That Waste Afternoons

  • Pool not on a shared L2 segment: in Layer2 mode the elected node's ARP replies must reach clients. A pool advertised from a VLAN nobody routes to is invisible.
  • Traffic all lands on one node: that is Layer2 mode's design, not a bug. Fix it with BGP + FRR and externalTrafficPolicy: Local, or accept it for small workloads.
  • Announcing an address the router will not accept: in BGP mode your upstream must permit the prefix. If it filters the host route, nothing arrives.
  • Overlapping pools: two pools covering the same range produce nondeterministic allocation across restarts.
  • Switch/router ARP-cache lag: Layer2 failover can take seconds even though the cluster moved in milliseconds — lower ARP timers on the adjacent L3 device or move to BGP.

相关阅读:Kubernetes NetworkPolicy 默认拒绝模式Calico、Cilium 与 Flannel CNI 对比 以及 Proxmox VE VLAN-Aware 网桥与 LACP 聚合

原文链接:MetalLB - Advanced AddressPool configuration