Hosted Control Plane Tasks¶
How tenant control planes are reached, and where their state lives.
For the model, see Hosted Control Planes.
Make a tenant control plane reachable¶
Each tenant control plane is published as a LoadBalancer Service taking an address from tenant-cp-pool, which MetalLB serves.
Your part is that the pool exists, has room, and is routed — see Expose tenant control planes for the pool and its constraints, and Pin or auto-assign a control-plane address for how a cluster ends up on a particular address.
# What each tenant control plane is published on
kubectl get svc -A -l kamaji.clastix.io/component=service \
-o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,IP:.status.loadBalancer.ingress[0].ip'
Pin the address rather than letting it be assigned: it ends up in every kubeconfig and in the api-server certificate's SANs, so it is not something to move later.
For reaching control planes from outside the data centre, see Control Plane External Access.
Hand a tenant a working kubeconfig¶
The kubeconfig in the cluster's Secret points at the control plane's internal address, which a tenant cannot reach. The address to use is the one the LoadBalancer Service publishes.
Three ways to produce a usable one, in order of convenience:
# 1. The utility: namespace first, cluster second
kmetal-kubeconfig dynamo-prod solar > solar.kubeconfig
- The web console serves the same kubeconfig with the address already substituted.
- By hand — read the address off the Service and rewrite the
serverfield.
See Create First Cluster.
Decide where control-plane state lives¶
Every hosted control plane keeps its etcd data on a Kamaji datastore, and that data sits on the class named by storage.etcdClassName:
spec:
storage:
underclusterClassName: platform-storage # golden images and machine disks
etcdClassName: local-path # this control-plane state; optional
etcdClassName is optional and defaults to underclusterClassName, so on a cluster with one platform class there is nothing to set.
Name it separately when the class the golden images need — a snapshotting one — is the wrong class for etcd, which on most backends it is.
This is the platform's most consequential storage decision. A node-local class ties the durability of every tenant control plane to the node holding the data; a network-attached class survives losing it. Choosing it here rather than inheriting it means the trade is deliberate instead of a side effect of what the image pipeline needed.
Changing it after tenants exist means migrating etcd data, so settle it before the first tenant — see Configuration Best Practices.
Control which Kubernetes versions tenants may ask for¶
A tenant sets spec.topology.version on their Cluster.
What makes a version usable is that the worker image matching it exists — the kubeadm tier's containerDisk.tag must match the version tenant clusters request:
spec:
clusterClass:
values:
kubeadm:
containerDisk:
image: quay.io/capk/ubuntu-2404-container-disk
tag: v1.34.1
A tenant asking for a version with no matching worker image gets a control plane that comes up and workers that never join.
See MultiCluster Management Tasks for the version and machine-shape surface.