Install Kube OVN
The Kube-OVN project is a Kubernetes Network Plugin that uses OVN as the network provider. It is a CNI plugin that provides a network solution for Kubernetes. It is a lightweight, scalable, and easy-to-use network solution for Kubernetes.
Prerequisites
The override values file for Kube-OVN can be found in /etc/genestack/helm-configs/kube-ovn/kube-ovn-helm-overrides.yaml
and should be setup-up before running the deployment. In a common production ready setup, the only values that will
likely need to be defined is the network interface that will Kube-OVN will bind to.
Example Kube-OVN Helm Overrides
In the example below, the IFACE and VLAN_INTERFACE_NAME are the only values that need to be defined and
are set to bond0. If you intend to enable hardware offloading, you will need to set the IFACE to the
a physical interface that supports hardware offloading.
For a full review of all the available options, see the Kube-OVN base helm overrides file.
Example Kube-OVN Helm Overrides
# Default values for kubeovn.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
---
global:
registry:
address: ghcr.io/rackerlabs/genestack-images
images:
kubeovn:
repository: kube-ovn
vpcRepository: vpc-nat-gateway
tag: v1.15.4
support_arm: true
thirdparty: true
image:
pullPolicy: IfNotPresent
replicaCount: 3
networking:
# NET_STACK defaults to ipv4
ENABLE_SSL: true
IFACE: "bond0"
vlan:
VLAN_INTERFACE_NAME: "bond0"
# VLAN_NAME: "ovn-vlan"
# VLAN_ID: "100"
ENABLE_EIP_SNAT: false
ENABLE_ECMP: true
# comma-separated string of nodelocal DNS ip addresses
PROBE_INTERVAL: 60000
OVN_NORTHD_PROBE_INTERVAL: 15000
OVN_LEADER_PROBE_INTERVAL: 15
OVN_REMOTE_PROBE_INTERVAL: 30000
OVN_NORTHD_N_THREADS: 4 # Number of threads for ovn-northd, default is 4 production environments could set it to a higher value.
ENABLE_COMPACT: true
func:
ENABLE_EXTERNAL_VPC: true
OVSDB_CON_TIMEOUT: 5
OVSDB_INACTIVITY_TIMEOUT: 30
ipv4:
POD_CIDR: "10.236.0.0/14"
POD_GATEWAY: "10.236.0.1"
SVC_CIDR: "10.233.0.0/18"
PINGER_EXTERNAL_ADDRESS: "208.67.222.222"
PINGER_EXTERNAL_DOMAIN: "opendns.com."
performance:
GC_INTERVAL: 360
INSPECT_INTERVAL: 300
OVS_VSCTL_CONCURRENCY: 150
ovs-ovn:
requests:
cpu: '1'
memory: 1Gi
limits:
cpu: '8'
memory: 4Gi
Label Kube-OVN nodes
key |
type | value |
notes |
|---|---|---|---|
| kube-ovn/role | str | master |
Defines where the Kube-OVN Masters will reside |
| ovn.kubernetes.io/ovs_dp_type | str | kernel |
(Optional) Defines OVS DPDK mode |
Label all controllers as Kube-OVN control plane nodes
Deployment
To run the Kube-OVN deployment, run the following command commands or script.
Run the Kube-OVN deployment Script /opt/genestack/bin/install-kube-ovn.sh
#!/bin/bash
# Description: Fetches the version for SERVICE_NAME_DEFAULT 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_DEFAULT="kube-ovn"
SERVICE_NAMESPACE="kube-system" # Note: kube-ovn uses the kube-system namespace
# Helm
HELM_REPO_NAME_DEFAULT="kubeovn"
HELM_REPO_URL_DEFAULT="https://kubeovn.github.io/kube-ovn"
# 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_DEFAULT}"
SERVICE_CUSTOM_OVERRIDES="${GENESTACK_OVERRIDES_DIR}/helm-configs/${SERVICE_NAME_DEFAULT}"
# Define the Global Overrides directory used in the original script
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_DEFAULT variable
SERVICE_VERSION=$(grep "^[[:space:]]*${SERVICE_NAME_DEFAULT}:" "$VERSION_FILE" | sed "s/.*${SERVICE_NAME_DEFAULT}: *//")
if [ -z "$SERVICE_VERSION" ]; then
echo "Error: Could not extract version for '$SERVICE_NAME_DEFAULT' from $VERSION_FILE" >&2
exit 1
fi
echo "Found version for $SERVICE_NAME_DEFAULT: $SERVICE_VERSION"
# --- Kube-OVN specific logic to determine masters and replica count ---
MASTER_NODES=$(kubectl get nodes -l kube-ovn/role=master -o json | jq -r '[.items[].status.addresses[] | select(.type == "InternalIP") | .address] | join(",")' | sed 's/,/\\,/g')
MASTER_NODE_COUNT=$(kubectl get nodes -l kube-ovn/role=master -o json | jq -r '.items[].status.addresses[] | select(.type=="InternalIP") | .address' | wc -l)
if [ "${MASTER_NODE_COUNT}" -eq 0 ]; then
echo "Error: No master nodes found labeled with 'kube-ovn/role=master'" >&2
echo "Be sure to label your master nodes with 'kube-ovn/role=master' before running this script." >&2
exit 1
fi
echo "Found $MASTER_NODE_COUNT master node(s) with IPs: ${MASTER_NODES//\\,/ }."
# --------------------------------------------------------------------
# Load chart metadata from custom override YAML if defined
for yaml_file in "${SERVICE_CUSTOM_OVERRIDES}"/*.yaml; do
if [ -f "$yaml_file" ]; then
HELM_REPO_URL=$(yq eval '.chart.repo_url // ""' "$yaml_file")
HELM_REPO_NAME=$(yq eval '.chart.repo_name // ""' "$yaml_file")
SERVICE_NAME=$(yq eval '.chart.service_name // ""' "$yaml_file")
break # use the first match and stop
fi
done
# Fallback to defaults if variables not set
: "${HELM_REPO_URL:=$HELM_REPO_URL_DEFAULT}"
: "${HELM_REPO_NAME:=$HELM_REPO_NAME_DEFAULT}"
: "${SERVICE_NAME:=$SERVICE_NAME_DEFAULT}"
# Determine Helm chart path
if [[ "$HELM_REPO_URL" == oci://* ]]; then
# OCI registry path
HELM_CHART_PATH="$HELM_REPO_URL/$HELM_REPO_NAME/$SERVICE_NAME"
else
# --- Helm Repository and Execution ---
helm repo add "$HELM_REPO_NAME" "$HELM_REPO_URL"
helm repo update
HELM_CHART_PATH="$HELM_REPO_NAME/$SERVICE_NAME"
fi
# Debug output
echo "[DEBUG] HELM_REPO_URL=$HELM_REPO_URL"
echo "[DEBUG] HELM_REPO_NAME=$HELM_REPO_NAME"
echo "[DEBUG] SERVICE_NAME=$SERVICE_NAME"
echo "[DEBUG] HELM_CHART_PATH=$HELM_CHART_PATH"
# Prepare an array to collect -f arguments
overrides_args=()
# Include all YAML files from the BASE configuration directory
# NOTE: Files in this directory are included first.
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 GLOBAL configuration directory
# NOTE: Files here override base settings and are applied before service-specific ones.
if [[ -d "$GLOBAL_OVERRIDES_DIR" ]]; then
echo "Including global overrides from directory: $GLOBAL_OVERRIDES_DIR"
for file in "$GLOBAL_OVERRIDES_DIR"/*.yaml; do
if [[ -e "$file" ]]; then
echo " - $file"
overrides_args+=("-f" "$file")
fi
done
else
echo "Warning: Global override directory not found: $GLOBAL_OVERRIDES_DIR"
fi
# Include all YAML files from the custom SERVICE configuration directory
# NOTE: Files here have the highest precedence.
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
# Collect all --set arguments, executing commands and quoting safely
set_args=(
--set "MASTER_NODES=${MASTER_NODES}"
--set "replicaCount=${MASTER_NODE_COUNT}"
)
helm_command=(
helm upgrade --install "$SERVICE_NAME_DEFAULT" "$HELM_CHART_PATH"
--version "${SERVICE_VERSION}"
--namespace="$SERVICE_NAMESPACE"
--timeout 120m
--create-namespace
"${overrides_args[@]}"
"${set_args[@]}"
# Post-renderer configuration
--post-renderer "$GENESTACK_OVERRIDES_DIR/kustomize/kustomize.sh"
--post-renderer-args "$SERVICE_NAME_DEFAULT/overlay"
"$@"
)
echo "Executing Helm command (arguments are quoted safely):"
printf '%q ' "${helm_command[@]}"
echo
# Execute the command directly from the array
"${helm_command[@]}"
Deployment Verification
Once the script has completed, you can verify that the Kube-OVN pods are running by running the following command
Output
NAME PROVIDER VPC PROTOCOL CIDR PRIVATE NAT DEFAULT GATEWAYTYPE V4USED V4AVAILABLE V6USED V6AVAILABLE EXCLUDEIPS U2OINTERCONNECTIONIP
join ovn ovn-cluster IPv4 100.64.0.0/16 false false false distributed 3 65530 0 0 ["100.64.0.1"]
ovn-default ovn ovn-cluster IPv4 10.236.0.0/14 false true true distributed 111 262030 0 0 ["10.236.0.1"]
Tip
After the deployment, and before going into production, it is highly recommended to review the Kube-OVN Backup documentation, from the operators guide for setting up you backups.
Upon successful deployment the Kubernetes Nodes should transition into a Ready state. Validate the nodes are ready by
running the following command.