Skip to main content

Provision a Managed Cluster

A Managed Cluster is a Kubernetes cluster with its own API, control plane, worker nodes, and networking. Kube-DC keeps its infrastructure resources in the owning Project's backing namespace ({organization}-{project}), while you administer cluster workloads through a separate Managed Cluster kubeconfig.

How It Works

When you create a Managed Cluster, Kube-DC orchestrates several components:

  • Control Plane — Managed by Kamaji. The API server, scheduler, and controller manager run as pods in your Project's backing namespace — no VMs needed for the control plane.
  • Worker Nodes — Provisioned through Cluster API. KubeVirt-backed workers run as VMs on the management cluster; provider catalogs can also offer CloudSigma-backed workers.
  • etcd DataStore — A dedicated or shared etcd cluster stores your Kubernetes state, managed automatically with TLS certificates and persistent storage.
  • Cloud Controller Manager (CCM) — Bridges your Managed Cluster with the infrastructure, enabling LoadBalancer services and node lifecycle management.
  • Cluster add-ons — Cilium CNI, CoreDNS, and a provider-compatible CSI driver are deployed automatically via Sveltos.

The diagram below shows a KubeVirt-backed example for Project production, whose backing namespace is acme-production.

KubeVirt-backed Managed Cluster topologyIn the acme-production Project infrastructure namespace, KdcCluster dev owns a LoadBalancer Service for the API, a Kamaji TenantControlPlane with API server, scheduler, and controller-manager Pods, a KdcClusterDatastore backed by an etcd StatefulSet, a worker MachineDeployment backed by KubeVirt virtual machines, and a CCM Deployment. Together these resources create Managed Cluster dev with three workers and a separate Kubernetes API for tenant Deployments, Services, and persistent volume claims.MANAGEMENT CLUSTER · ACME-PRODUCTIONKdcClusterdevControl-plane Servicedev-cpLoadBalancer APITenantControlPlaneKamajiDatastoredev-etcdCCM Deploymentkccm-devControl-plane PodsAPI server · schedulercontroller-manager Podsetcd clusteretcd StatefulSetpersistent storageMANAGED CLUSTER DEV · SEPARATE APIWorker MachineDeploymentworker-1 · worker-2worker-3 · KubeVirt VMsTenant workloadsDeployments · ServicesPVCs
KdcCluster owns a Kamaji control plane, datastore, worker MachineDeployment, and CCM inside the Project infrastructure namespace; users reach a separate tenant Kubernetes API and run workloads on its workers.

Prerequisites

Managed Clusters View

Navigate to Kubernetes in your Project to view its Managed Clusters:

The view shows each Managed Cluster's version, phase, API endpoint, datastore backend, and age. Use Create Managed Cluster to open the wizard. From a Managed Cluster row or detail view, you can inspect status, use the Kubeconfig action, or open Delete Managed Cluster.

Create a Managed Cluster in the Console

The console provides a guided 5-step wizard.

Step 1: Basic Configuration

  • Managed Cluster Name — Unique within your Project (2–12 characters; lowercase letters, numbers, and hyphens).
  • Kubernetes Version — Select the desired version (e.g., v1.35.0).
  • Control Plane Replicas:
    • 1 — Development
    • 2 — High Availability
    • 3 — Production
  • Expose API endpoint externally (via TLSRoute) — Check this to make the API reachable from outside the cluster network (e.g., https://dev-cp-acme-production.kube-dc.cloud:443).
  • Enable etcd encryption at rest — Encrypt supported API resources with a Project KMS key.

Step 2: Datastore Configuration

Create Cluster - Datastore

Choose how the Managed Cluster's etcd state is stored:

ModeDescriptionBest For
Shared Datastore (cost-effective)Multiple Managed Clusters use an existing shared etcd datastoreDevelopment, staging
Dedicated Datastore (isolated)The Managed Cluster gets its own etcd datastoreProduction workloads

For dedicated datastores:

  • etcd Replicas1 for development, 3 for production high availability, or 5 for maximum redundancy.

The console allocates the dedicated datastore service port automatically from the free range 3238032499; it is not a user setting. The Managed Cluster reaches etcd members over DNS on port 2379.

Step 3: Network & Add-ons

Create Cluster - Network

  • Service CIDR — IP range for Kubernetes Services (default: 10.96.0.0/16). Each cluster has isolated networking.
  • Pod CIDR — IP range for Pods (default: 10.244.0.0/16).

Cluster add-ons deployed automatically:

Add-onDescriptionRecommended
Cilium CNIPod networking and network policyYes (required)
CoreDNSCluster DNS (cluster.local)Yes (required)
Provider CSIPersistent storage; KubeVirt uses hotplug DataVolumesEnable for stateful workloads

Step 4: Worker Pools

Define one or more worker pools — each a group of identically configured VMs:

  • Pool Name — Identifier (e.g., workers).
  • Replicas — Number of worker VMs.
  • CPU Cores / Memory (GB) — Resources per worker.
  • Worker TypeDataVolume (Persistent) provides root disks that survive VM restarts.
  • Container Image — OS image for worker VMs (e.g., Ubuntu 24.04 + K8s v1.35.2).

Click Add Pool to add pools with different configurations (e.g., a general pool and a high-memory pool).

Step 5: Review and Create

Create Cluster - Review

The Review step shows the exact YAML manifests that will be applied. Inspect both the KdcCluster and Datastore tabs to verify the configuration.

Click Create Cluster to start provisioning the Managed Cluster.

Create a Managed Cluster with kubectl

Apply a KdcCluster resource to your Project's backing namespace:

apiVersion: k8s.kube-dc.com/v1alpha1
kind: KdcCluster
metadata:
name: dev
namespace: acme-production
annotations:
k8s.kube-dc.com/expose-route: "true"
spec:
version: v1.36.1
controlPlane:
replicas: 1
dataStore:
dedicated: true
eipName: default-gw
port: 32381
network:
serviceCIDR: 10.96.0.0/16
podCIDR: 10.244.0.0/16
eip:
create: true
externalNetworkType: cloud
enableClusterAPI: true
workers:
- name: workers
replicas: 3
cpuCores: 2
memory: 8Gi
diskSize: 30Gi
image: docker.io/shalb/ubuntu-2404-container-disk:v1.36.1
architecture: amd64
infrastructureProvider: kubevirt
storageType: datavolume
kubectl apply -f cluster.yaml

Spec Reference

FieldDescriptionDefault
spec.versionKubernetes versionRequired
spec.controlPlane.replicasAPI server replicas (1–5)2
spec.dataStore.dedicatedCreate dedicated etcdfalse
spec.dataStore.eipNameEIP for datastore LoadBalancerdefault-gw
spec.dataStore.portOptional dedicated datastore service port; it must be unique on the selected EIP. The console allocates 3238032499; the API defaults to 2379 when omitted.2379
spec.network.serviceCIDRKubernetes service CIDR10.96.0.0/12
spec.network.podCIDRPod network CIDR10.244.0.0/16
spec.eip.createAuto-create EIP for API servertrue
spec.eip.externalNetworkTypecloud or publicpublic
spec.enableClusterAPIEnable Cluster API worker managementfalse
spec.workers[].nameWorker pool name (unique)Required
spec.workers[].replicasNumber of workers in pool2
spec.workers[].cpuCoresCPU cores per worker1
spec.workers[].memoryMemory per worker2Gi
spec.workers[].imageKubeVirt worker image; choose one compatible with spec.versionPlatform default
spec.workers[].storageTypedatavolume (persistent) or containerdisk (ephemeral)datavolume
spec.workers[].infrastructureProviderkubevirt or cloudsigmakubevirt
spec.loadBalancer.blocksNumber of /28 blocks (16 VIPs each) for project-internal LoadBalancer VIPs (1–8; KubeVirt clusters, where the platform enables VIP pools)1
spec.encryption.etcd.enabledEncrypt selected API data at rest in the cluster's etcd (Secrets by default) (KMS v2 envelope via OpenBao Transit) — see Encryption at Restfalse
spec.encryption.etcd.kekRotation.enabledAuto-rotate the Key Encryption Key on a schedulefalse
spec.encryption.etcd.kekRotation.intervalDuration between rotations (units: d, h, m, sw is not accepted). Must be ≥ 7d and ≥ spec.backup.retentionDays.

Annotations

AnnotationDescription
k8s.kube-dc.com/expose-route: "true"Expose API endpoint externally via TLSRoute (recommended for cloud network type)
kube-dc.com/restore-fromSelects a snapshot for a disruptive restore of an existing Managed Cluster. Adding it starts the restore workflow; it is not a first-boot option. Plaintext .db keys and envelope directories are detected automatically. Use the cluster detail view's danger zone and read Backups and Recovery before restoring.

Encryption at Rest

Your cluster's etcd stores everything kubectl returns — Secrets, ConfigMaps, CRDs, ServiceAccount tokens. By default these are written to disk in plaintext. Flipping a single field encrypts them with KMS v2 envelope encryption: each row gets a fresh AES-256-GCM data key (DEK), and the DEK is wrapped by a per-cluster Key Encryption Key (KEK) that lives in Kube-DC's OpenBao Transit engine and never leaves in plaintext.

spec:
encryption:
etcd:
enabled: true

That setting enables the default encryption flow: a per-cluster KMSKey is auto-created, the apiserver starts with a KMS plugin sidecar, and every new Secret you create from that point on is stored encrypted. Existing Secrets are re-encrypted on their next update. Do not use a delete-and-recreate sweep: it can rotate generated metadata, interrupt controllers, and remove resources between operations. Plan a controlled maintenance workflow with your platform operator when existing data must be rewritten immediately.

To check it took effect:

kubectl get kdccluster <name> -o jsonpath='{.status.encryption}'

You should see a resolvedKeyRef (the name of the auto-created KMSKey) and kekRotation.currentVersion: 1.

Rotating the KEK

Encryption-at-rest by itself doesn't rotate the wrapping key — schedule that explicitly:

spec:
encryption:
etcd:
enabled: true
kekRotation:
enabled: true
interval: 90d

Rules:

  • Units are d, h, m, s (no w).
  • Minimum 7 days, maximum 730 days.
  • Interval must be ≥ spec.backup.retentionDays * 24h. The rationale: rotating faster than backups age out leaves historic backups bound to key versions you'd have to manage by hand.

Rotation creates a new Transit key version. Old key versions stay alive — historic Secrets and backups remain decryptable indefinitely. Bulk re-wrap of existing rows is not done automatically (each row re-wraps on next Update); a CLI-driven sweep is planned for a future release.

Status:

kubectl get kdccluster <name> -o jsonpath='{.status.encryption.kekRotation}'
# {"currentVersion":2,"enabled":true,"lastRotatedTime":"...","minDecryptionVersion":1,"nextRotationTime":"..."}

Backups stay encrypted too

When encryption.etcd.enabled: true, spec.backup.enabled: true, and the platform's managed backup bucket is available, the per-cluster snapshot CronJob writes envelope-encrypted snapshots to S3 as three sibling objects. Enabling the field alone does not provision object storage; confirm the Managed Cluster's backup status on each installation.

See Backups & Snapshots. Restoring an encrypted snapshot also requires OpenBao access, so possession of the S3 objects is not sufficient to decrypt them.

Limits

  • What's encrypted: Secret values by default. Opt into ConfigMap too via spec.encryption.etcd.resources: ["secrets", "configmaps"]. Other resources (leases, events, endpoints, pods) stay plaintext for the high-write-rate / low-sensitivity reasons — see the design doc on the public site.
  • What's NOT encrypted: application data on PVCs. Use a workload- side mechanism (LUKS-backed StorageClass, application encryption, Velero with restic) for that.
  • Disabling: flipping enabled: false on a cluster that was previously encrypted is intentionally blocked — the apiserver would fail to read existing rows. Contact your cluster admin for the documented two-step migration.

Monitor Managed Cluster Creation

# Watch cluster status
kubectl get kdccluster -n acme-production -w

# Example output:
# NAME VERSION PHASE ENDPOINT DATASTORE AGE
# dev v1.35.0 Ready https://dev-cp-acme-production.kube-dc.cloud:443 dev-etcd 5m

# Check datastore status
kubectl get kdcclusterdatastores -n acme-production

# Example output:
# NAME READY DEDICATED DATASTORE AGE
# dev-etcd Ready true dev-etcd 5m

Provisioning time depends on worker capacity, image availability, and networking. The cluster moves through these phases: PendingWaitingForServiceProvisioningReady.

Access the Managed Cluster

In the Console

Once the Managed Cluster is Ready, use Kubeconfig on its detail page to download the kubeconfig file.

Cluster View

The cluster detail page shows:

  • Phase — Current state (Ready)
  • Control Plane — Replicas and health status
  • API Endpoint — The external URL (e.g., https://dev-cp-acme-production.kube-dc.cloud:443)
  • DataStore — etcd name and type (Shared/Dedicated)
  • Worker Pools — Number of pools and total workers
  • Tabs for Summary, Workers, Network, and YAML views

With kubectl

Use the Kubeconfig action in the cluster detail view. For a cluster with an external API endpoint, the equivalent Project-level command reads the generated external kubeconfig:

kubectl get secret dev-cp-admin-kubeconfig-external -n acme-production \
-o jsonpath='{.data.admin\.conf}' | base64 -d > /tmp/dev-kubeconfig

kubectl --kubeconfig=/tmp/dev-kubeconfig get nodes

If the external Secret does not exist, the cluster API is private. Its internal kubeconfig is intended for clients with connectivity to the platform network; enable the external API endpoint or use an approved private access path before downloading it to a workstation.

Managed Cluster Infrastructure

When you create a Managed Cluster, its KdcCluster and related infrastructure resources are provisioned automatically in the Project's backing namespace:

ResourceName PatternPurpose
Service (LoadBalancer){cluster}-cpAPI server endpoint
Service (ClusterIP){cluster}-cp-extExternal DNS endpoint
TenantControlPlane{cluster}-cpKamaji control plane pods
KdcClusterDatastore{cluster}-etcdetcd (if dedicated)
StatefulSet{cluster}-etcd-etcdetcd pods
Service (LoadBalancer){cluster}-etcd-etcd-lbetcd external access
Cluster (CAPI){cluster}Cluster API cluster object
MachineDeployment{cluster}-{pool}Worker pool VMs
Deploymentkccm-{cluster} or csccm-{cluster}Provider Cloud Controller Manager

Example — Services created for cluster dev in the acme-production backing namespace:

$ kubectl get svc -n acme-production | grep dev
dev-cp LoadBalancer 10.101.79.219 100.65.0.148 6443/TCP 18d
dev-cp-ext ClusterIP None <none> 6443/TCP 18d
dev-etcd-etcd ClusterIP None <none> 2379/TCP 18d
dev-etcd-etcd-lb LoadBalancer 10.101.20.207 100.65.0.115 32382/TCP 18d
dev-etcd-etcd-lb-ext ClusterIP None <none> 32382/TCP 18d

Next Steps