Setup the MetalLB Loadbalancer
The MetalLb loadbalancer can be setup by editing the following file metallb-openstack-service-lb.yml, You will need to add
your "external" VIP(s) to the loadbalancer so that they can be used within services. These IP addresses are unique and will
need to be customized to meet the needs of your environment.
Tip
When L2Advertisement is used, you should use a CIDR that is not overlapping with any local interface CIDR. This also enables later migration to BGP advertisement.
Create the MetalLB namespace
Install MetalLB
Run the MetalLB deployment Script /opt/genestack/bin/install-metallb.sh You can include paramaters to deploy aio or base-monitoring. No paramaters deploys base
#!/bin/bash
# Description: Fetches the version for SERVICE_NAME from the specified
# YAML file and executes a helm upgrade/install command with dynamic values files.
# Disable SC2124 (unused array), SC2145 (array expansion issue), SC2294 (eval)
# shellcheck disable=SC2124,SC2145,SC2294
# Service
SERVICE_NAME="metallb"
SERVICE_NAMESPACE="metallb-system"
# Helm
HELM_REPO_NAME="metallb"
HELM_REPO_URL="https://metallb.github.io/metallb"
# Base directories provided by the environment
GENESTACK_BASE_DIR="${GENESTACK_BASE_DIR:-/opt/genestack}"
GENESTACK_OVERRIDES_DIR="${GENESTACK_OVERRIDES_DIR:-/etc/genestack}"
# Define service-specific override directories based on the framework
SERVICE_BASE_OVERRIDES="${GENESTACK_BASE_DIR}/base-helm-configs/${SERVICE_NAME}"
SERVICE_CUSTOM_OVERRIDES="${GENESTACK_OVERRIDES_DIR}/helm-configs/${SERVICE_NAME}"
GLOBAL_OVERRIDES_DIR="${GENESTACK_OVERRIDES_DIR}/helm-configs/global_overrides"
# Read the desired chart version from VERSION_FILE
VERSION_FILE="${GENESTACK_OVERRIDES_DIR}/helm-chart-versions.yaml"
if [ ! -f "$VERSION_FILE" ]; then
echo "Error: helm-chart-versions.yaml not found at $VERSION_FILE" >&2
exit 1
fi
# Extract version dynamically using the SERVICE_NAME variable
SERVICE_VERSION=$(grep "^[[:space:]]*${SERVICE_NAME}:" "$VERSION_FILE" | sed "s/.*${SERVICE_NAME}: *//")
if [ -z "$SERVICE_VERSION" ]; then
echo "Error: Could not extract version for '$SERVICE_NAME' from $VERSION_FILE" >&2
exit 1
fi
echo "Found version for $SERVICE_NAME: $SERVICE_VERSION"
# Prepare an array to collect -f arguments
overrides_args=()
# Include all YAML files from the BASE configuration directory
if [[ -d "$SERVICE_BASE_OVERRIDES" ]]; then
echo "Including base overrides from directory: $SERVICE_BASE_OVERRIDES"
for file in "$SERVICE_BASE_OVERRIDES"/*.yaml; do
# Check that there is at least one match
if [[ -e "$file" ]]; then
echo " - $file"
overrides_args+=("-f" "$file")
fi
done
else
echo "Warning: Base override directory not found: $SERVICE_BASE_OVERRIDES"
fi
# Include all YAML files from the custom SERVICE configuration directory
if [[ -d "$SERVICE_CUSTOM_OVERRIDES" ]]; then
echo "Including overrides from service config directory:"
for file in "$SERVICE_CUSTOM_OVERRIDES"/*.yaml; do
if [[ -e "$file" ]]; then
echo " - $file"
overrides_args+=("-f" "$file")
fi
done
else
echo "Warning: Service config directory not found: $SERVICE_CUSTOM_OVERRIDES"
fi
echo
# --- Helm Repository and Execution ---
helm repo add "$HELM_REPO_NAME" "$HELM_REPO_URL"
helm repo update
# Collect all --set arguments, executing commands and quoting safely
set_args=()
helm_command=(
helm upgrade --install "$SERVICE_NAME" "$HELM_REPO_NAME/$SERVICE_NAME"
--version "${SERVICE_VERSION}"
--namespace="$SERVICE_NAMESPACE"
--timeout 120m
--create-namespace
"${overrides_args[@]}"
"${set_args[@]}"
# Post-renderer configuration
# NOTE: Metallb doesn't typically require a post-renderer, but we keep it
# for template compliance.
--post-renderer "$GENESTACK_OVERRIDES_DIR/kustomize/kustomize.sh"
--post-renderer-args "$SERVICE_NAME/overlay"
"$@"
)
echo "Executing Helm command (arguments are quoted safely):"
printf '%q ' "${helm_command[@]}"
echo
# Execute the command directly from the array
"${helm_command[@]}"
Example LB manifest
Example for metallb-openstack-service-lb.yml file.
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: gateway-api-external
namespace: metallb-system
spec:
addresses:
- 10.74.8.99/32 # This is assumed to be the public LB vip address
autoAssign: false
avoidBuggyIPs: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: openstack-external-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- gateway-api-external
nodeSelectors: # Optional block to limit nodes for a given advertisement
- matchLabels:
node-role.kubernetes.io/worker: worker
# interfaces: # Optional block to limit ifaces used to advertise VIPs
# - br-host
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: primary
namespace: metallb-system
spec:
addresses:
- 10.234.0.0/24
autoAssign: false
avoidBuggyIPs: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: cluster-internal-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- primary
nodeSelectors: # Optional block to limit nodes for a given advertisement
- matchLabels:
node-role.kubernetes.io/worker: worker
# interfaces: # Optional block to limit ifaces used to advertise VIPs
# - br-host
Tip
Edit the /etc/genestack/manifests/metallb/metallb-openstack-service-lb.yml file following the comment instructions with the details of your cluster.
The file metallb-openstack-service-lb.yml is intially provided during bootstrap for genestack.
Verify the deployment of MetalLB by checking the pods in the metallb-system namespace.
Once MetalLB is operatianal, apply the metallb service manifest.
Re-IP the advertisement pools
In situations where the advertisement pools must be changed, the following disruptive procedure can be used:
Update existing metallb configuration:
kubectl -n metallb-system delete IPAddressPool/primary
kubectl -n metallb-system delete IPAddressPool/gateway-api-external
kubectl apply -f /etc/genestack/manifests/metallb/metallb-openstack-service-lb.yml
Restart the metallb controller:
Once the metallb controller restarts it'll begin to reip the external service IP associations which typically requires DNS entry updates. This change including the DNS refresh (TTL) time will be disruptive.
Node Exclusion from LoadBalancer
To exclude specific nodes from receiving loadbalancer traffic, you can add the following label to the nodes you want to exclude.
Replace <node-name> with the name of the node you want to exclude. This will prevent MetalLB
from assigning load balancer IPs to services on that node.
Conversely, to include a node back into loadbalancer assignments, you can remove the label.
For more information on this well known label, refer to the Kubernetes documentation.