Overview
MetalLB is a load balancer implementation for bare metal Kubernetes clusters. It provides LoadBalancer-type service support, which is typically only available in cloud environments.
HelmRelease Configuration
MetalLB is deployed using Flux with the following HelmRelease:
apiVersion : helm.toolkit.fluxcd.io/v2
kind : HelmRelease
metadata :
name : metallb
spec :
chart :
spec :
chart : metallb
sourceRef :
kind : HelmRepository
name : metallb
version : "=0.15.3"
interval : 24h
releaseName : metallb
install :
crds : Create
upgrade :
crds : CreateReplace
Key Configuration
MetalLB Helm chart version. Currently using 0.15.3
How often Flux checks for chart updates
CRD management strategy during installation
IP Address Pool Configuration
MetalLB requires an IPAddressPool resource to define the range of IP addresses it can assign:
apiVersion : metallb.io/v1beta1
kind : IPAddressPool
metadata :
name : first-pool
namespace : metallb-system
spec :
addresses :
- 192.168.10.0/24
L2 Advertisement
For Layer 2 mode, configure L2Advertisement to announce IP addresses:
apiVersion : metallb.io/v1beta1
kind : L2Advertisement
metadata :
name : l2-advertisement
namespace : metallb-system
spec :
ipAddressPools :
- first-pool
L2 mode is simpler to configure but requires that MetalLB nodes are on the same Layer 2 network as your clients.
Operating Modes
Layer 2 mode uses ARP (for IPv4) or NDP (for IPv6) to announce service IPs: Advantages:
Simple configuration
No special network equipment required
Works with standard switches
Limitations:
All traffic goes through a single node
Failover can take a few seconds
Configuration: apiVersion : metallb.io/v1beta1
kind : L2Advertisement
metadata :
name : l2-advertisement
spec :
ipAddressPools :
- first-pool
BGP mode peers with your network routers using the BGP protocol: Advantages:
True load balancing across multiple nodes
Faster failover
Better scalability
Requirements:
BGP-capable router
Network configuration for BGP peering
Configuration: apiVersion : metallb.io/v1beta1
kind : BGPAdvertisement
metadata :
name : bgp-advertisement
spec :
ipAddressPools :
- first-pool
peers :
- 192.168.1.1
Using LoadBalancer Services
Create a LoadBalancer service
Define a service with type LoadBalancer: apiVersion : v1
kind : Service
metadata :
name : my-app
namespace : default
spec :
type : LoadBalancer
selector :
app : my-app
ports :
- port : 80
targetPort : 8080
protocol : TCP
Apply the service
kubectl apply -f service.yaml
Verify IP assignment
Check that MetalLB assigned an external IP: Expected output: NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
my-app LoadBalancer 10.96.100.50 192.168.10.10 80:30123/TCP
Test connectivity
Access the service using the external IP: curl http://192.168.10.10
Advanced Configuration
Requesting Specific IP Addresses
You can request a specific IP from the pool:
apiVersion : v1
kind : Service
metadata :
name : my-app
spec :
type : LoadBalancer
loadBalancerIP : 192.168.10.50 # Request specific IP
selector :
app : my-app
ports :
- port : 80
targetPort : 8080
IP Sharing
Multiple services can share the same IP using the metallb.universe.tf/allow-shared-ip annotation:
http-service.yaml
https-service.yaml
apiVersion : v1
kind : Service
metadata :
name : http-service
annotations :
metallb.universe.tf/allow-shared-ip : "shared-ip-key"
spec :
type : LoadBalancer
loadBalancerIP : 192.168.10.100
ports :
- port : 80
protocol : TCP
When sharing IPs, ensure services use different ports to avoid conflicts.
kubectl get pods -n metallb-system
Expected output:
NAME READY STATUS RESTARTS AGE
controller-xxxxxxxxxx-xxxxx 1/1 Running 0 5d
speaker-xxxxx 1/1 Running 0 5d
speaker-yyyyy 1/1 Running 0 5d
View IP address pools
kubectl get ipaddresspools -n metallb-system
Check allocated IPs
kubectl get svc --all-namespaces -o wide | grep LoadBalancer
Troubleshooting
Service stuck in pending
If a LoadBalancer service remains in pending:
Check if IPAddressPool is configured:
kubectl get ipaddresspools -n metallb-system
Verify pool has available IPs:
kubectl describe ipaddresspool first-pool -n metallb-system
Check MetalLB controller logs:
kubectl logs -n metallb-system -l app.kubernetes.io/component=controller
IP not reachable
Verify L2Advertisement is configured:
kubectl get l2advertisements -n metallb-system
Check speaker logs:
kubectl logs -n metallb-system -l app.kubernetes.io/component=speaker
Verify network connectivity from speaker nodes
Best Practices
Define IP pools carefully to avoid conflicts with your network DHCP range
Use L2 mode for simplicity in small to medium deployments
Consider BGP mode for production environments requiring high availability
Monitor IP address pool utilization to prevent exhaustion
Document IP assignments for services in your infrastructure documentation
Resources