How to Expose a Kubernetes MetalLB Load Balancer in a Virtual Network
Problem
How do you make a MetalLB load balancer service in a PCD-K cluster reachable from outside the cluster's virtual (tenant) network?
By default, MetalLB assigns external IPs from an address pool within the cluster's virtual network. Those IPs are not directly routable from the physical network. To expose a service externally, you must bridge the virtual and physical networks using OpenStack floating IPs.
Environment
Platform9 Private Cloud Director (PCD) with PCD-K clusters deployed on virtual (Neutron) networks
MetalLB add-on enabled on the cluster (L2 mode)
An OpenStack router connecting the virtual network to the physical/external network
How it works
The virtual and physical networks do not connect at Layer 2. OpenStack bridges them at Layer 3 using NAT via a floating IP:
Floating IP (e.g. 10.10.17.140)
Physical / provider
Neutron port on external network; the public address
Fixed port (e.g. 192.168.1.233)
Virtual / tenant
Reserves the MetalLB VIP in IPAM; anchors the floating IP
Floating IP association
Router
DNAT 10.10.17.140 → 192.168.1.233, SNAT on return
When a packet reaches the physical floating IP, the Neutron router DNATs it to the virtual network IP. MetalLB (in L2 mode) makes a worker node answer ARP for that IP on the tenant network. kube-proxy on that node then forwards the packet to the target pod.
The Expose LB configuration in PCD handles the OpenStack port security requirements that allow MetalLB's L2 ARP announcements to work on Neutron.
Procedure
Prerequisites
MetalLB add-on is enabled on the cluster and Expose LB is configured with the MetalLB VIP in the Allowed Addresses field. See Set up Load Balancers using MetalLB. Wait for all worker nodes to return to Ready before proceeding.
The MetalLB VIP must be in the same subnet as the worker nodes. MetalLB L2 mode relies on ARP, which is scoped to a single broadcast domain — if the VIP is in a different subnet, the Neutron router will never ARP for it on the worker node segment and the service will be unreachable.
A virtual network IP from the worker nodes' subnet (e.g.
192.168.1.233) is reserved for the MetalLB VIP — it must not conflict with existing VM IPs,podCIDR, orserviceCIDR.An OpenStack router is already configured to connect the virtual network to the physical/external network.
You have OpenStack CLI access (
openstackCLI or equivalent API access).
Step 1: Configure the MetalLB IPAddressPool and L2Advertisement
Apply an IPAddressPool containing the virtual network IP and a corresponding L2Advertisement in the cluster:
Step 2: Create an OpenStack port for the MetalLB VIP
This port reserves the VIP in Neutron's IPAM and provides an attachment point for the floating IP.
Note the port UUID from the output — you will need it in the next step.
Step 3: Assign a floating IP to the MetalLB VIP port
Map an existing floating IP (or allocate a new one) to the port created in the previous step:
Step 4: Deploy a service and verify
Create a LoadBalancer service in the cluster. MetalLB should assign the VIP from the pool:
Confirm the external IP is assigned:
Test access via the floating IP from outside the virtual network:
Additional Information
The floating IP to port association (Step 3) is currently only possible via the OpenStack CLI.
Last updated
