Install KubeLB CCM and set up the tenant cluster

Prerequisites

  • Access to the Kubernetes API of the KubeLB management cluster.
  • A tenant registered in the KubeLB management cluster.

See Requirements for the full port and resource sizing reference.

  • Create a kubelb namespace for KubeLB CCM.

  • KubeLB CCM expects a Secret named kubelb-cluster by default. Its kubelb data key must contain the kubeconfig for the management cluster.

    • First register the tenant by following the tenant registration guide.
    • Fetch the generated kubeconfig from the management cluster, switch to the tenant cluster, and create the Secret:
    # Replace with the tenant cluster kubeconfig path
    TENANT_KUBECONFIG=~/.kube/<tenant-cluster>
    # Replace with the tenant name
    TENANT_NAME=tenant-shroud
    KUBELB_KUBECONFIG_B64=$(kubectl get secret kubelb-ccm-kubeconfig --namespace "$TENANT_NAME" --template='{{ .data.kubelb }}')
    # Switch to the tenant cluster.
    export KUBECONFIG="$TENANT_KUBECONFIG"
    kubectl --namespace kubelb create secret generic kubelb-cluster \
      --from-literal=kubelb="$(printf '%s' "$KUBELB_KUBECONFIG_B64" | base64 -d)"
    
  • Override the Secret name with .Values.kubelb.clusterSecretName if required. Otherwise, the Secret is named kubelb-cluster and looks like this:

    kubectl get secret kubelb-cluster --namespace kubelb -o yaml
    
    apiVersion: v1
    data:
      kubelb: xxx-base64-encoded-xxx
    kind: Secret
    metadata:
      name: kubelb-cluster
      namespace: kubelb
    type: Opaque
    
  • Set tenantName in values.yaml to the tenant’s unique identifier. The management cluster stores tenant namespaces with a tenant- prefix; for example, my-tenant becomes tenant-my-tenant. KubeLB accepts the name with or without this prefix.

At this point a minimal values.yaml should look like this:

kubelb:
  clusterSecretName: kubelb-cluster
  tenantName: <unique-identifier-for-tenant>

Private clusters: If the tenant nodes do not have external IP addresses, set kubelb.nodeAddressType to InternalIP.

kubectl get nodes -o wide
NAME     STATUS   ROLES           AGE    VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE          KERNEL-VERSION       CONTAINER-RUNTIME
node-x   Ready    control-plane   208d   v1.29.9   10.66.99.222   <none>        Ubuntu            5.15.0-121-generic   containerd://1.6.33

Adjust values.yaml:

kubelb:
  # -- Address type to use for routing traffic to node ports. Values are ExternalIP, InternalIP, or Hostname.
  nodeAddressType: InternalIP

Install KubeLB CCM

To enable Gateway API support, set both fields below in values.yaml. KubeLB CCM cannot start with Gateway API enabled unless the CRDs are installed.

kubelb:
  enableGatewayAPI: true
  installGatewayAPICRDs: true

Prerequisites

  • Create a namespace kubelb for the CCM to be deployed in.
  • Create imagePullSecrets for the chart to pull the image from the registry in kubelb namespace.

At this point a minimal values.yaml should look like this:

imagePullSecrets:
  - name: <imagePullSecretName>
kubelb:
    clusterSecretName: kubelb-cluster
    tenantName: <unique-identifier-for-tenant>

Install the Helm chart

helm pull oci://quay.io/kubermatic/helm-charts/kubelb-ccm-ee --version=v1.5.0 --untardir "." --untar
## Apply CRDs
kubectl apply -f kubelb-ccm-ee/crds/
## Create and update values.yaml with the required values.
helm upgrade --install kubelb-ccm kubelb-ccm-ee --namespace kubelb -f kubelb-ccm-ee/values.yaml --create-namespace

Helm applies a chart’s crds/ directory only on helm install and silently skips it on helm upgrade. Re-apply the CRDs on every upgrade — see Upgrading KubeLB.

KubeLB CCM EE Values

KeyTypeDefaultDescription
affinityobject{}
autoscaling.enabledboolfalse
autoscaling.maxReplicasint10
autoscaling.minReplicasint1
autoscaling.targetCPUUtilizationPercentageint80
autoscaling.targetMemoryUtilizationPercentageint80
extraVolumeMountslist[]
extraVolumeslist[]
fullnameOverridestring""
global.imagePullSecretslist[]Global image pull secrets propagated to all pod specs. Used for private mirror registries.
global.imageRegistrystring""Override the registry for all images (prefix replacement). Example: “registry.customer.internal” rewrites quay.io/foo/bar to registry.customer.internal/foo/bar
grafana.dashboards.annotationsobject{}Additional annotations for dashboard ConfigMaps
grafana.dashboards.enabledboolfalseRequires grafana to be deployed with sidecar.dashboards.enabled=true. For more info: https://github.com/grafana/helm-charts/tree/grafana-10.5.13/charts/grafana#:~:text=%5B%5D-,sidecar.dashboards.enabled,-Enables%20the%20cluster
image.pullPolicystring"IfNotPresent"
image.repositorystring"quay.io/kubermatic/kubelb-ccm-ee"
image.tagstring"v1.5.0"
imagePullSecrets[0].namestring"kubermatic-quay.io"
kubeRbacProxy.image.pullPolicystring"IfNotPresent"
kubeRbacProxy.image.repositorystring"quay.io/brancz/kube-rbac-proxy"
kubeRbacProxy.image.tagstring"v0.20.1"
kubelb.clusterSecretNamestring"kubelb-cluster"Name of the secret that contains kubeconfig for the loadbalancer cluster
kubelb.disableBackendTrafficPolicyControllerboolfalsedisableBackendTrafficPolicyController specifies whether to disable the BackendTrafficPolicy Controller.
kubelb.disableClientTrafficPolicyControllerboolfalsedisableClientTrafficPolicyController specifies whether to disable the ClientTrafficPolicy Controller.
kubelb.disableGRPCRouteControllerboolfalsedisableGRPCRouteController specifies whether to disable the GRPCRoute Controller.
kubelb.disableGatewayControllerboolfalsedisableGatewayController specifies whether to disable the Gateway Controller.
kubelb.disableHTTPRouteControllerboolfalsedisableHTTPRouteController specifies whether to disable the HTTPRoute Controller.
kubelb.disableIngressControllerboolfalsedisableIngressController specifies whether to disable the Ingress Controller.
kubelb.disableTCPRouteControllerboolfalsedisableTCPRouteController specifies whether to disable the TCPRoute Controller.
kubelb.disableTLSRouteControllerboolfalsedisableTLSRouteController specifies whether to disable the TLSRoute Controller.
kubelb.disableUDPRouteControllerboolfalsedisableUDPRouteController specifies whether to disable the UDPRoute Controller.
kubelb.enableGatewayAPIboolfalseenableGatewayAPI specifies whether to enable the Gateway API and Gateway Controllers. By default Gateway API is disabled since without Gateway APIs installed the controller cannot start.
kubelb.enableLeaderElectionbooltrueEnable the leader election.
kubelb.enableSecretSynchronizerboolfalseEnable to automatically convert Secrets labelled with kubelb.k8c.io/managed-by: kubelb to Sync Secrets. This is used to sync secrets from tenants to the LB cluster in a controlled and secure way.
kubelb.gatewayAPICRDsChannelstring"experimental"gatewayAPICRDsChannel specifies the channel for the Gateway API CRDs. Options are standard and experimental.
kubelb.gatewayClasseslist["kubelb"]gatewayClasses specifies tenant GatewayClass names watched when useGatewayClass is true.
kubelb.ingressConversion.copyTLSSecretsbooltruecopyTLSSecrets copies TLS secrets from Ingress namespace to Gateway namespace for cross-namespace certificate references
kubelb.ingressConversion.disableEnvoyGatewayFeaturesboolfalsedisableEnvoyGatewayFeatures disables creation of Envoy Gateway policies (SecurityPolicy, BackendTrafficPolicy)
kubelb.ingressConversion.domainReplacestring""domainReplace is the domain suffix to replace in hostnames
kubelb.ingressConversion.domainSuffixstring""domainSuffix is the replacement domain suffix for hostnames
kubelb.ingressConversion.enabledboolfalseenabled enables automatic Ingress to HTTPRoute conversion
kubelb.ingressConversion.gatewayAnnotationsstring""gatewayAnnotations are annotations to add to created Gateway (comma-separated key=value pairs) Example: “cert-manager.io/cluster-issuer=letsencrypt,external-dns.alpha.kubernetes.io/target=lb.example.com”
kubelb.ingressConversion.gatewayClassstring"kubelb"gatewayClass is the GatewayClass name for created Gateway
kubelb.ingressConversion.gatewayNamestring"kubelb"gatewayName is the name of the Gateway for converted HTTPRoutes
kubelb.ingressConversion.gatewayNamespacestring"kubelb"gatewayNamespace is the namespace for the shared Gateway (required)
kubelb.ingressConversion.ingressClassstring""ingressClass filters Ingresses to convert (empty = convert all)
kubelb.ingressConversion.propagateExternalDnsAnnotationsbooltruepropagateExternalDnsAnnotations propagates external-dns annotations to Gateway/HTTPRoute
kubelb.ingressConversion.standaloneModeboolfalsestandaloneMode runs as standalone converter, disabling all other controllers
kubelb.installGatewayAPICRDsboolfalseinstallGatewayAPICRDs Installs and manages the Gateway API CRDs using gateway crd controller.
kubelb.logLevelstring"info"To configure the verbosity of logging. Can be one of ‘debug’, ‘info’, ’error’, ‘panic’ or any integer value > 0 which corresponds to custom debug levels of increasing verbosity.
kubelb.maxNodeAddressCountint0Maximum number of node addresses to forward to the LB cluster. When set, addresses are selected with topology-aware round-robin spread across zones (topology.kubernetes.io/zone). 0 means no limit.
kubelb.nodeAddressLabelSelectorstring""Only use nodes matching this label selector as endpoint addresses (e.g. kubelb.k8c.io/endpoint=true).
kubelb.nodeAddressTypestring"ExternalIP"Address type to use for routing traffic to node ports. Values are ExternalIP, InternalIP.
kubelb.tenantNamestringnilName of the tenant, must be unique against a load balancer cluster.
kubelb.tenantProxy.envoy.image.repositorystring"docker.io/envoyproxy/envoy"Envoy image repository for the mTLS tenant proxy DaemonSet.
kubelb.tenantProxy.envoy.image.tagstring"distroless-v1.36.4"Envoy image tag for the mTLS tenant proxy DaemonSet.
kubelb.tenantProxy.serviceAccount.annotationsobject{}Annotations to add to the mTLS tenant proxy ServiceAccount.
kubelb.tenantProxy.serviceAccount.createbooltrueCreate a dedicated ServiceAccount for the mTLS tenant proxy xDS writer sidecar.
kubelb.tenantProxy.serviceAccount.namestring""Name of the mTLS tenant proxy ServiceAccount. If not set and create is true, a name is generated.
kubelb.tenantProxy.serviceTypestring""Override the mTLS tenant proxy Service type (NodePort or LoadBalancer). Empty follows the management cluster configuration.
kubelb.tenantProxy.shutdownManager.image.repositorystring"docker.io/envoyproxy/gateway"Shutdown-manager image repository for the mTLS tenant proxy DaemonSet.
kubelb.tenantProxy.shutdownManager.image.tagstring"v1.8.3"Shutdown-manager image tag for the mTLS tenant proxy DaemonSet. Must match images.DefaultShutdownManagerImage — this value is what actually reaches the DaemonSet, and only tags present in docs/images/images.txt exist in an air-gap mirror.
kubelb.tenantProxy.staticAddresseslist[]Static IPs or hostnames published as the mTLS tenant proxy dial target instead of node or load balancer addresses. For proxies fronted by an appliance, NAT, or a user-managed DNS record.
kubelb.tenantProxy.staticPortint15443Port dialed together with staticAddresses.
kubelb.useGatewayClassbooltrueuseGatewayClass specifies whether to target resources with kubelb gateway class or all resources.
kubelb.useIngressClassbooltrueuseIngressClass specifies whether to target resources with kubelb ingress class or all resources.
kubelb.useLoadBalancerClassboolfalseuseLoadBalancerClass specifies whether to target services of type LoadBalancer with kubelb load balancer class or all services of type LoadBalancer.
metrics.portint9445Port where the CCM exposes metrics
nameOverridestring""
nodeSelectorobject{}
podAnnotationsobject{}
podLabelsobject{}
podSecurityContext.runAsNonRootbooltrue
podSecurityContext.seccompProfile.typestring"RuntimeDefault"
rbac.allowLeaderElectionRolebooltrue
rbac.allowMetricsReaderRolebooltrue
rbac.allowProxyRolebooltrue
rbac.enabledbooltrue
replicaCountint1
resources.limits.cpustring"500m"
resources.limits.memorystring"512Mi"
resources.requests.cpustring"100m"
resources.requests.memorystring"128Mi"
securityContext.allowPrivilegeEscalationboolfalse
securityContext.capabilities.drop[0]string"ALL"
securityContext.runAsUserint65532
service.portint8443
service.protocolstring"TCP"
service.typestring"ClusterIP"
serviceAccount.annotationsobject{}
serviceAccount.createbooltrue
serviceAccount.namestring""
serviceMonitor.enabledboolfalse
testImage.repositorystring"busybox"
testImage.tagstring"1.35.0"
tolerationslist[]

Install the Helm chart

helm pull oci://quay.io/kubermatic/helm-charts/kubelb-ccm --version=v1.5.0 --untardir "." --untar
## Apply CRDs
kubectl apply -f kubelb-ccm/crds/
## Create and update values.yaml with the required values.
helm upgrade --install kubelb-ccm kubelb-ccm --namespace kubelb -f kubelb-ccm/values.yaml --create-namespace

Helm applies a chart’s crds/ directory only on helm install and silently skips it on helm upgrade. Re-apply the CRDs on every upgrade — see Upgrading KubeLB.

KubeLB CCM CE Values

KeyTypeDefaultDescription
affinityobject{}
autoscaling.enabledboolfalse
autoscaling.maxReplicasint10
autoscaling.minReplicasint1
autoscaling.targetCPUUtilizationPercentageint80
autoscaling.targetMemoryUtilizationPercentageint80
extraVolumeMountslist[]
extraVolumeslist[]
fullnameOverridestring""
grafana.dashboards.annotationsobject{}Additional annotations for dashboard ConfigMaps
grafana.dashboards.enabledboolfalseRequires grafana to be deployed with sidecar.dashboards.enabled=true. For more info: https://github.com/grafana/helm-charts/tree/grafana-10.5.13/charts/grafana#:~:text=%5B%5D-,sidecar.dashboards.enabled,-Enables%20the%20cluster
image.pullPolicystring"IfNotPresent"
image.repositorystring"quay.io/kubermatic/kubelb-ccm"
image.tagstring"v1.5.0"
imagePullSecretslist[]
kubeRbacProxy.image.pullPolicystring"IfNotPresent"
kubeRbacProxy.image.repositorystring"quay.io/brancz/kube-rbac-proxy"
kubeRbacProxy.image.tagstring"v0.20.1"
kubelb.clusterSecretNamestring"kubelb-cluster"Name of the secret that contains kubeconfig for the loadbalancer cluster
kubelb.disableGRPCRouteControllerboolfalsedisableGRPCRouteController specifies whether to disable the GRPCRoute Controller.
kubelb.disableGatewayControllerboolfalsedisableGatewayController specifies whether to disable the Gateway Controller.
kubelb.disableHTTPRouteControllerboolfalsedisableHTTPRouteController specifies whether to disable the HTTPRoute Controller.
kubelb.disableIngressControllerboolfalsedisableIngressController specifies whether to disable the Ingress Controller.
kubelb.enableGatewayAPIboolfalseenableGatewayAPI specifies whether to enable the Gateway API and Gateway Controllers. By default Gateway API is disabled since without Gateway APIs installed the controller cannot start.
kubelb.enableLeaderElectionbooltrueEnable the leader election.
kubelb.enableSecretSynchronizerboolfalseEnable to automatically convert Secrets labelled with kubelb.k8c.io/managed-by: kubelb to Sync Secrets. This is used to sync secrets from tenants to the LB cluster in a controlled and secure way.
kubelb.gatewayAPICRDsChannelstring"standard"gatewayAPICRDsChannel specifies the channel for the Gateway API CRDs. Options are standard and experimental.
kubelb.ingressConversion.copyTLSSecretsbooltruecopyTLSSecrets copies TLS secrets from Ingress namespace to Gateway namespace for cross-namespace certificate references
kubelb.ingressConversion.disableEnvoyGatewayFeaturesboolfalsedisableEnvoyGatewayFeatures disables creation of Envoy Gateway policies (SecurityPolicy, BackendTrafficPolicy)
kubelb.ingressConversion.domainReplacestring""domainReplace is the domain suffix to replace in hostnames
kubelb.ingressConversion.domainSuffixstring""domainSuffix is the replacement domain suffix for hostnames
kubelb.ingressConversion.enabledboolfalseenabled enables automatic Ingress to HTTPRoute conversion
kubelb.ingressConversion.gatewayAnnotationsstring""gatewayAnnotations are annotations to add to created Gateway (comma-separated key=value pairs) Example: “cert-manager.io/cluster-issuer=letsencrypt,external-dns.alpha.kubernetes.io/target=lb.example.com”
kubelb.ingressConversion.gatewayClassstring"kubelb"gatewayClass is the GatewayClass name for created Gateway
kubelb.ingressConversion.gatewayNamestring"kubelb"gatewayName is the name of the Gateway for converted HTTPRoutes
kubelb.ingressConversion.gatewayNamespacestring"kubelb"gatewayNamespace is the namespace for the shared Gateway (required)
kubelb.ingressConversion.ingressClassstring""ingressClass filters Ingresses to convert (empty = convert all)
kubelb.ingressConversion.propagateExternalDnsAnnotationsbooltruepropagateExternalDnsAnnotations propagates external-dns annotations to Gateway/HTTPRoute
kubelb.ingressConversion.standaloneModeboolfalsestandaloneMode runs as standalone converter, disabling all other controllers
kubelb.installGatewayAPICRDsboolfalseinstallGatewayAPICRDs Installs and manages the Gateway API CRDs using gateway crd controller.
kubelb.logLevelstring"info"To configure the verbosity of logging. Can be one of ‘debug’, ‘info’, ’error’, ‘panic’ or any integer value > 0 which corresponds to custom debug levels of increasing verbosity.
kubelb.nodeAddressTypestring"ExternalIP"Address type to use for routing traffic to node ports. Values are ExternalIP, InternalIP.
kubelb.tenantNamestringnilName of the tenant, must be unique against a load balancer cluster.
kubelb.useGatewayClassbooltrueuseGatewayClass specifies whether to target resources with kubelb gateway class or all resources.
kubelb.useIngressClassbooltrueuseIngressClass specifies whether to target resources with kubelb ingress class or all resources.
kubelb.useLoadBalancerClassboolfalseuseLoadBalancerClass specifies whether to target services of type LoadBalancer with kubelb load balancer class or all services of type LoadBalancer.
metrics.portint9445Port where the CCM exposes metrics
nameOverridestring""
nodeSelectorobject{}
podAnnotationsobject{}
podLabelsobject{}
podSecurityContext.runAsNonRootbooltrue
podSecurityContext.seccompProfile.typestring"RuntimeDefault"
priorityClassNamestring""PriorityClassName for the manager pod (e.g., “system-cluster-critical”)
rbac.allowLeaderElectionRolebooltrue
rbac.allowMetricsReaderRolebooltrue
rbac.allowProxyRolebooltrue
rbac.enabledbooltrue
replicaCountint1
resources.limits.cpustring"500m"
resources.limits.memorystring"512Mi"
resources.requests.cpustring"100m"
resources.requests.memorystring"128Mi"
securityContext.allowPrivilegeEscalationboolfalse
securityContext.capabilities.drop[0]string"ALL"
securityContext.runAsUserint65532
service.portint8443
service.protocolstring"TCP"
service.typestring"ClusterIP"
serviceAccount.annotationsobject{}
serviceAccount.createbooltrue
serviceAccount.namestring""
serviceMonitor.enabledboolfalse
tolerationslist[]

Verify the installation

Check that the CCM is running:

kubectl get pods -n kubelb
NAME                          READY   STATUS    RESTARTS   AGE
kubelb-ccm-7c9f7d6b8d-4qk2n   2/2     Running   0          1m

The kubelb-ccm pod must be in Running state before load balancer services are reconciled.

Set up the tenant cluster

Install Gateway API CRDs

Starting from KubeLB v1.2.0, the Gateway API CRDs can be installed using the installGatewayAPICRDs flag.

imagePullSecrets:
  - name: <imagePullSecretName>
kubelb:
    clusterSecretName: kubelb-cluster
    tenantName: <unique-identifier-for-tenant>
    # This will install the experimental channel of the Gateway API CRDs
    installGatewayAPICRDs: true
    enableGatewayAPI: true

For more details: Experimental Install

kubelb:
    clusterSecretName: kubelb-cluster
    tenantName: <unique-identifier-for-tenant>
    # This will install the standard channel of the Gateway API CRDs
    installGatewayAPICRDs: true
    enableGatewayAPI: true

For more details: Standard Install