Phase 1-3 runbooks and design notes, version pins, config templates, project-history conventions, cluster-inspector read-only credentials setup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.6 KiB
Phase 3 — Hosted control planes with Kamaji ("poor man's KaaS")
Goal: create a tenant Kubernetes cluster from one manifest. Its control plane runs as pods in the management cluster (Kamaji), and its workers are autoscaled Proxmox (later also KVM) VMs. Status: design notes.
Why Kamaji
With kubeadm control planes every tenant costs ≥ 1 VM (3 for HA) just for apiserver/etcd. Kamaji runs each tenant control plane as a Deployment in the management cluster, backed by a shared datastore. That cuts the control plane to a few hundred MB of RAM, gives it seconds to start, and makes upgrades a rolling Deployment update.
Architecture
flowchart LR
subgraph MGMT[management cluster - 3 nodes]
CAPI[CAPI core + CAPMOX]
KCP[Kamaji CP provider]
KAM[Kamaji controller]
DS[(kamaji-etcd datastore)]
LB[MetalLB pool on VLAN 4]
subgraph T1[ns tenant-a]
TCP1[TenantControlPlane pods<br/>apiserver, ctrl-mgr, scheduler]
CA1[cluster-autoscaler tenant-a]
end
end
PVE[Proxmox VE]
W1[tenant-a worker VMs]
KCP --> KAM --> TCP1
TCP1 --> DS
LB -. VIP :6443 .- TCP1
CAPI -- MachineDeployment --> PVE --> W1
W1 -- kubelet join via VIP --> TCP1
CA1 -- scales MD --> CAPI
CAPI object wiring per tenant:
Cluster.spec.controlPlaneRef→KamajiControlPlane, which creates the KamajiTenantControlPlane.Cluster.spec.infrastructureRef→ProxmoxClusterwithexternalManagedControlPlane: true, so the Kamaji provider patches the control plane endpoint. The field exists in CAPMOX v1alpha2.- Workers: a regular
MachineDeployment+KubeadmConfigTemplate+ProxmoxMachineTemplate, with autoscaler annotations as in phase 1. - Konnectivity handles apiserver → kubelet traffic, because the control plane isn't on the worker network.
References: Kamaji + Proxmox infra provider docs,
CLASTIX Helm chart capi-kamaji-proxmox (0.0.4) as a wiring example.
Management cluster changes vs. phase 1
| Area | Phase 1 | Phase 3 |
|---|---|---|
| Nodes | 1× k3s | 3 nodes: tenant control planes and their datastore live here |
| LoadBalancer | none needed (kube-vip in workload CP) | MetalLB (or kube-vip cloud provider) with an IP pool on VLAN 4, one VIP per tenant |
| Datastore | — | kamaji-etcd chart 0.18.1 (etcd 3.6.12); alternatives: PostgreSQL/MySQL via kine |
| Storage | — | CSI for etcd PVCs |
k3s' built-in servicelb (klipper) binds host ports, so it can't hand out many :6443 endpoints. Use MetalLB.
Autoscaling per tenant
A cluster-autoscaler instance in clusterapi mode serves one workload cluster (one kubeconfig).
So each tenant gets its own CA release in its namespace in the management cluster
(kubeconfig-incluster, secret <tenant>-kubeconfig). Automating that is our first real Go code:
- tenant-operator (small controller in this repo): watches
Clusterobjects labelledkaas.lab/autoscaling=enabled, renders a per-tenant cluster-autoscaler Deployment + RBAC, and cleans up on delete. The alternative is CAAPH (cluster-api-addon-provider-helm), but that installs into the workload cluster, and we want CA in the management cluster.
Self-service UX
ClusterClasslab-tenantwith variables: Kubernetes version, worker size, min/max workers, VLAN.- A tenant cluster is one
Clusterwithspec.topologyreferencing the class. - A namespace per tenant with RBAC limited to its own
Cluster; the kubeconfig is handed out from the<tenant>-kubeconfigsecret.
Open questions and risks
- CAPI version skew: Kamaji CP provider v0.21.0 is built against CAPI v1.13.4, while CAPMOX
v0.9.x is tested on v1.12. All providers share one CAPI core, so either find a Kamaji CP
provider release built on 1.12 or wait for CAPMOX to support 1.13+. Check each provider's
metadata.yamlcontract before installing. - Kamaji OSS release model: OSS publishes edge releases only (latest tag
edge-26.2.5, Helm chart0.0.0+latest). Stable versioned builds are commercial (CLASTIX), so pin by edge tag and image digest. - Tenant Kubernetes version range is bounded by the Kamaji release and by our node images. We need one image-builder template per supported minor.
- Backup and restore of the shared datastore (etcd snapshots) becomes critical: it holds every tenant.
Exit criteria
- Tenant cluster created from a single
Cluster+ClusterClass, control plane as pods in mgmt. - Workers join via Konnectivity, and CNI works.
- Per-tenant cluster-autoscaler deployed automatically, scale 0 → N → 0 works.
- Deleting the
Clusterremoves the control plane, VMs, VIP and autoscaler.