Salta ai contenuti

MetalLB Cheat Sheet

Overview

MetalLB is a load-balancer implementation for bare-metal Kubernetes clusters. In cloud environments, Kubernetes Services of type LoadBalancer automatically provision external load balancers (AWS ELB, GCP LB, etc.), but on bare metal these services remain perpetually in the “pending” state. MetalLB fills this gap by providing two functions: address allocation (assigning external IPs to Services from a configured pool) and external announcement (making the network aware that the IP exists using either Layer 2 mode or BGP mode).

In Layer 2 mode, one node in the cluster takes ownership of the service IP and responds to ARP requests, making it the simplest setup but limiting failover to single-node traffic handling. In BGP mode, all nodes that host a service’s pods establish BGP peering sessions with network routers, enabling true load balancing across multiple nodes. MetalLB supports both IPv4 and IPv6, multiple address pools, and per-service pool selection via annotations. It is widely deployed in on-premises Kubernetes installations, home labs, and edge computing environments.

Installation

Helm Installation

# Add MetalLB Helm repo
helm repo add metallb https://metallb.github.io/metallb
helm repo update

# Install MetalLB
helm install metallb metallb/metallb --namespace metallb-system --create-namespace

# Verify pods
kubectl get pods -n metallb-system

Manifest Installation

# Apply MetalLB manifests
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml

# Wait for pods to be ready
kubectl wait --namespace metallb-system \
  --for=condition=ready pod \
  --selector=app=metallb \
  --timeout=90s

Configuration

Layer 2 Mode

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.1.200-192.168.1.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - default-pool

BGP Mode

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: bgp-pool
  namespace: metallb-system
spec:
  addresses:
    - 203.0.113.0/24
---
apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: router-peer
  namespace: metallb-system
spec:
  myASN: 64500
  peerASN: 64501
  peerAddress: 10.0.0.1
  password: bgpsecret
  holdTime: 90s
  keepaliveTime: 30s
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: bgp-advert
  namespace: metallb-system
spec:
  ipAddressPools:
    - bgp-pool
  communities:
    - 64500:100
  aggregationLength: 32

Multiple Address Pools

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: internal-pool
  namespace: metallb-system
spec:
  addresses:
    - 10.0.100.0/24
  autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: external-pool
  namespace: metallb-system
spec:
  addresses:
    - 203.0.113.0/28
  autoAssign: false
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: internal-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - internal-pool
  interfaces:
    - eth0
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: external-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - external-pool
  interfaces:
    - eth1

Using MetalLB with Services

Basic LoadBalancer Service

apiVersion: v1
kind: Service
metadata:
  name: web-app
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

Request Specific IP

apiVersion: v1
kind: Service
metadata:
  name: web-app
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.210
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

Select Specific Pool

apiVersion: v1
kind: Service
metadata:
  name: external-api
  annotations:
    metallb.universe.tf/address-pool: external-pool
spec:
  type: LoadBalancer
  selector:
    app: api
  ports:
    - port: 443
      targetPort: 8443

Share IP Between Services

apiVersion: v1
kind: Service
metadata:
  name: http-service
  annotations:
    metallb.universe.tf/allow-shared-ip: "shared-web"
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.210
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: https-service
  annotations:
    metallb.universe.tf/allow-shared-ip: "shared-web"
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.210
  selector:
    app: web
  ports:
    - port: 443
      targetPort: 8443

Core Commands

CommandDescription
kubectl get ipaddresspool -n metallb-systemList IP address pools
kubectl get l2advertisement -n metallb-systemList L2 advertisements
kubectl get bgppeer -n metallb-systemList BGP peers
kubectl get svc -AShow services with External IPs
kubectl describe svc <name>Check service events for MetalLB assignment
kubectl logs -n metallb-system -l app=metallb -c speakerSpeaker logs
kubectl logs -n metallb-system -l app=metallb -c controllerController logs

Advanced Usage

Node Selectors for L2 Advertisement

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: targeted-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - default-pool
  nodeSelectors:
    - matchLabels:
        kubernetes.io/hostname: node1
    - matchLabels:
        kubernetes.io/hostname: node2

BGP with Multiple Routers

apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: router1
  namespace: metallb-system
spec:
  myASN: 64500
  peerASN: 64501
  peerAddress: 10.0.0.1
  nodeSelectors:
    - matchLabels:
        rack: rack1
---
apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: router2
  namespace: metallb-system
spec:
  myASN: 64500
  peerASN: 64502
  peerAddress: 10.0.0.2
  nodeSelectors:
    - matchLabels:
        rack: rack2

IPv6 Support

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: ipv6-pool
  namespace: metallb-system
spec:
  addresses:
    - 2001:db8:1::100-2001:db8:1::200

Troubleshooting

IssueSolution
Service stuck in PendingCheck IPAddressPool has available addresses; verify MetalLB pods running
L2 mode: no connectivityVerify VIP is in same subnet as nodes; check ARP with arping
BGP session not establishedCheck firewall allows TCP 179; verify ASN and peer addresses
Failover too slow (L2)L2 relies on ARP cache expiry; set gratuitousARP and reduce client timeouts
IP not assigned from correct poolAdd metallb.universe.tf/address-pool annotation to Service
Speaker CrashLoopBackOffCheck if kube-proxy is in IPVS mode; set strictARP: true in kube-proxy config
Multiple speakers claiming IPVerify memberlist secret exists in metallb-system namespace