Skip to content

MetalLB

MetalLB provides LoadBalancer services for bare-metal Kubernetes clusters, enabling external access to services in local development environments.

Overview

MetalLB is deployed in local Kind clusters to provide LoadBalancer functionality that would normally be provided by cloud load balancers.

Configuration

Deployment Location

  • Configuration: apps/infra/metallb/
  • Environment: Local development only
  • Chart: Official MetalLB Helm chart

Values Structure

apps/infra/metallb/
├── common-values.yaml
└── local/
    ├── values.yaml
    └── extra/
        └── cluster1.yaml

Address Pool Configuration

Local Development Pool

Configuration: apps/infra/metallb/local/extra/cluster1.yaml

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.200-172.18.255.250
  autoAssign: true

---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default

Address Range Planning

Kind Network Range: 172.18.0.0/16 - Cluster nodes: 172.18.0.2-172.18.0.10 - Pod CIDR: 10.244.0.0/16 - Service CIDR: 10.96.0.0/12 - MetalLB pool: 172.18.255.200-172.18.255.250

Layer 2 Configuration

L2 Advertisement

Why Layer 2 Mode? - Simpler configuration for local development - No BGP router requirements - Direct ARP/NDP responses - Suitable for single-node or small clusters

Configuration:

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default
  nodeSelectors:
  - matchLabels:
      node-role.kubernetes.io/control-plane: ""

Service Integration

LoadBalancer Services

Example Service Configuration:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: default
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx
  loadBalancerIP: 172.18.255.200  # Optional: specific IP

Ingress Controller Integration

NGINX Ingress with MetalLB:

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  type: LoadBalancer
  ports:
  - name: http
    port: 80
    targetPort: http
  - name: https
    port: 443
    targetPort: https
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/component: controller

Advanced Configuration

Multiple Address Pools

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: web-services
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.200-172.18.255.220
  autoAssign: false

---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: api-services
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.221-172.18.255.240
  autoAssign: false

Service Selector

Target Specific Pools:

apiVersion: v1
kind: Service
metadata:
  name: web-service
  annotations:
    metallb.universe.tf/address-pool: web-services
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: web-app

BGP Configuration (Optional)

BGP Mode Setup

For more advanced networking:

apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: router-peer
  namespace: metallb-system
spec:
  myASN: 64512
  peerASN: 64512
  peerAddress: 172.18.0.1
  sourceAddress: 172.18.0.2

---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default

Monitoring and Troubleshooting

Status Checking

# Check MetalLB pods
kubectl get pods -n metallb-system

# Check IP address pools
kubectl get ipaddresspool -n metallb-system

# Check L2 advertisements
kubectl get l2advertisement -n metallb-system

# Check service external IPs
kubectl get svc --all-namespaces -o wide

Service Status

# Check LoadBalancer services
kubectl get svc -o wide | grep LoadBalancer

# Describe service for events
kubectl describe svc <service-name> -n <namespace>

# Check service endpoints
kubectl get endpoints <service-name> -n <namespace>

Common Issues

External IP Pending: 1. Check MetalLB controller status 2. Verify IP address pool configuration 3. Check L2Advertisement setup 4. Review network connectivity

IP Assignment Conflicts: 1. Check address pool ranges 2. Verify no external DHCP conflicts 3. Review existing IP assignments 4. Check network bridge configuration

Logs and Events

# MetalLB controller logs
kubectl logs -n metallb-system deployment/controller

# MetalLB speaker logs
kubectl logs -n metallb-system daemonset/speaker

# System events
kubectl get events --sort-by=.metadata.creationTimestamp

Network Testing

Connectivity Tests

# Test external IP access
curl http://172.18.255.200

# Check ARP table
arp -a | grep 172.18.255

# Ping test
ping 172.18.255.200

# Port connectivity
nc -zv 172.18.255.200 80

Docker Network Inspection

# Check Kind network
docker network ls | grep kind

# Inspect network details
docker network inspect kind

# Check container IPs
docker ps | grep kind
docker inspect <container-id> | grep IPAddress

Integration with Kind

Kind Cluster Configuration

Cluster setup with port mapping:

# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP

Network Bridge Setup

Docker bridge configuration:

# Check bridge networks
docker network ls

# Create custom bridge (if needed)
docker network create --driver bridge \
  --subnet=172.19.0.0/16 \
  --ip-range=172.19.240.0/20 \
  kind-custom

Security Considerations

Network Isolation

Namespace restrictions:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: secure-pool
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.240-172.18.255.250
  namespaces:
  - secure-namespace

Access Control

Service-specific pools:

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: secure-advertisement
  namespace: metallb-system
spec:
  ipAddressPools:
  - secure-pool
  nodeSelectors:
  - matchLabels:
      security-level: high

Performance Tuning

Speaker Configuration

apiVersion: v1
kind: ConfigMap
metadata:
  name: config
  namespace: metallb-system
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 172.18.255.200-172.18.255.250
    bgp-communities:
      no-advertise: 65535:65282

Resource Limits

controller:
  resources:
    limits:
      cpu: 100m
      memory: 100Mi
    requests:
      cpu: 50m
      memory: 50Mi

speaker:
  resources:
    limits:
      cpu: 100m
      memory: 100Mi
    requests:
      cpu: 50m
      memory: 50Mi

Best Practices

Address Planning

  1. Reserve address ranges for different service types
  2. Avoid conflicts with existing network infrastructure
  3. Document IP assignments for troubleshooting
  4. Use consistent naming for pools and advertisements

Development Workflow

  1. Start with Layer 2 for simplicity
  2. Test connectivity before deploying applications
  3. Monitor resource usage of MetalLB components
  4. Regular cleanup of unused IP assignments

Maintenance

  1. Regular updates of MetalLB charts
  2. Monitor logs for errors and warnings
  3. Test failover scenarios in multi-node setups
  4. Document network topology for team reference

Limitations

Layer 2 Mode Limitations

  1. Single node handling traffic per service
  2. ARP table limitations in large networks
  3. Network broadcast dependency
  4. Limited scalability compared to BGP mode

Kind-Specific Limitations

  1. Docker network dependency
  2. Host network access requirements
  3. Port mapping conflicts with host services
  4. Limited multi-cluster networking

Migration from NodePort

Service Type Migration

Before (NodePort):

apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080
  selector:
    app: my-app

After (LoadBalancer with MetalLB):

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

DNS Configuration

Update DNS records to point to LoadBalancer IPs instead of NodePort combinations.