In raw, unmanaged Cluster API, creating a single workload cluster requires writing and maintaining at least six to eight interdependent Custom Resource manifests: Cluster, InfrastructureCluster (e.g., AWSCluster), KubeadmControlPlane, control-plane AWSMachineTemplate, multiple worker MachineDeployments, worker AWSMachineTemplates, and KubeadmConfigTemplates.
Managing this sprawling CRD graph for one cluster is educational. Managing it across fifty or five hundred clusters in production creates a combinatorial explosion of boilerplate YAML.
Traditionally, platform engineering teams attempted to tame this complexity by layering external, client-side templating tools (such as Helm, Kustomize, or Jinja scripts) on top of Cluster API. However, client-side templating introduces critical architectural flaws: it lacks server-side schema validation, provides no continuous drift reconciliation, and forces application teams to understand low-level cloud provider infrastructure schemas.
Cluster API resolves this through ClusterClass and Managed Topologies.
ClusterClass elevates cluster definition into an object-oriented class-and-instance pattern. A platform team defines a parameterized, reusable cluster blueprint once as a ClusterClass. Cluster consumers then instantiate clusters through a lightweight, high-level spec.topology block on the Cluster resource.
1. The Problem: The Unmanaged CRD Graph at Scale
To understand why ClusterClass is necessary, contrast how clusters are defined with and without managed topologies:
| Dimension | Raw Cluster API (Unmanaged CRD Graph) | Managed Topologies (ClusterClass) |
|---|---|---|
| Control Point | Fragmented across 6+ disparate CRs (Cluster, KCP, MD, templates) | Single control point: Cluster.spec.topology |
| Boilerplate Overhead | Hundreds of lines of repetitive YAML copied per cluster | Minimal manifest: ~30 lines specifying version, counts, and variables |
| Drift Reconciliation | None: changing a template requires manual rotation across every cluster | Continuous: updating the ClusterClass cascades rollouts fleet-wide |
| Validation Layer | Client-side YAML linter or generic Kubernetes API validation | Server-side OpenAPI v3 schema validation on cluster variables |
| Consumer Experience | High cognitive load: developers must master provider-specific VM/VPC CRDs | Low cognitive load: developers select worker classes and pass parameters |
| Upgrade Experience | Multi-phase manual patching of KCP and individual MachineDeployments | Single-knob operation: updating Cluster.spec.topology.version |
2. The Architectural Mental Model: Class vs. Instance
The relationship between ClusterClass and a managed Cluster mirrors class-and-object architecture in software engineering:
flowchart TD
subgraph PlatformEngineering["Platform Engineering Team (Blueprint Definition)"]
CC["ClusterClass<br/>(Reusable Architecture)"]
T_INFRA["InfrastructureCluster Template"]
T_CP["ControlPlane Template"]
T_WORKERS["Worker Classes (General, High-Mem, GPU)"]
VARS["OpenAPI v3 Variable Schema"]
PATCHES["JSON 6902 / Go Template Patches"]
CC --> T_INFRA
CC --> T_CP
CC --> T_WORKERS
CC --> VARS
CC --> PATCHES
end
subgraph ClusterConsumers["Cluster Consumers (Concrete Deployments)"]
C1["Cluster: Production EU<br/>(topology.version: v1.31.0, workers: 20)"]
C2["Cluster: Staging US<br/>(topology.version: v1.31.0, workers: 3)"]
C3["Cluster: Machine Learning<br/>(topology.version: v1.30.0, gpu-workers: 8)"]
end
CC -.->|"Instantiated by spec.topology.classRef"| C1
CC -.->|"Instantiated by spec.topology.classRef"| C2
CC -.->|"Instantiated by spec.topology.classRef"| C3
subgraph TopologyEngine["CAPI Topology Controller (Server-Side Compiler)"]
COMPILER["Evaluates Variables & Patches<br/>Reconciles Child CRD Graph"]
end
C1 --> COMPILER
C2 --> COMPILER
C3 --> COMPILER
subgraph GeneratedResources["Generated Runtime Resources"]
G_KCP["KubeadmControlPlane"]
G_MD["MachineDeployments"]
G_INFRA["AWSCluster / AzureCluster"]
end
COMPILER --> G_KCP
COMPILER --> G_MD
COMPILER --> G_INFRA
The Two Roles
ClusterClass(The Blueprint): Authored by platform architects. It defines how a cluster of a particular tier or environment is structured: the base VPC topology, OS image templates, control plane settings, available worker classes, and variable validation rules.Cluster.spec.topology(The Instance): Authored by cluster consumers or automated GitOps pipelines. It defines what specific parameters this instance requires: target Kubernetes version, control plane replica count, worker pool sizes, and variable assignments.
The Topology Controller acts as a server-side compiler. It takes the blueprint and consumer parameters, evaluates variable substitutions and JSON patches, and generates and continuously reconciles the underlying concrete CAPI resources.
3. Anatomy of a ClusterClass
A production ClusterClass is organized into five structural sections:
apiVersion: cluster.x-k8s.io/v1beta1
kind: ClusterClass
metadata:
name: standard-aws-cluster
namespace: default
spec:
# 1. Shared Infrastructure Template
infrastructure:
ref:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterTemplate
name: standard-aws-cluster-infra
# 2. Control Plane Templates
controlPlane:
ref:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlaneTemplate
name: standard-aws-kcp
machineInfrastructure:
ref:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSMachineTemplate
name: standard-aws-cp-machine
# 3. Reusable Worker Classes
workers:
machineDeployments:
- class: "general-purpose"
template:
bootstrap:
ref:
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
name: general-worker-bootstrap
infrastructure:
ref:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSMachineTemplate
name: general-worker-infra
- class: "gpu-worker"
template:
bootstrap:
ref:
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
name: gpu-worker-bootstrap
infrastructure:
ref:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSMachineTemplate
name: gpu-worker-infra
# 4. Strongly-Typed OpenAPI v3 Variable Definitions
variables:
- name: region
required: true
schema:
openAPIV3Schema:
type: string
enum: ["us-east-1", "us-west-2", "eu-central-1"]
- name: workerInstanceType
required: false
schema:
openAPIV3Schema:
type: string
default: "m6i.xlarge"
# 5. Server-Side Patches
patches:
- name: injectRegion
enabledIf: "{{ .region }}"
definitions:
- selector:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSClusterTemplate
matchResources:
infrastructureCluster: true
jsonPatches:
- op: add
path: "/spec/region"
valueFrom:
variable: region
The Power of OpenAPI v3 Variables and Patches
Variables are not mere dumb string replacements. Cluster API validates variable inputs against the embedded openAPIV3Schema before any resources are stamped out. If a consumer provides an invalid region or unsupported instance type, the admission webhook rejects the kubectl apply synchronously.
Once validated, the patch engine injects values into the generated infrastructure templates using JSON 6902 patches or Go templates.
4. The Consumer Interface: Cluster.spec.topology
From the application or cluster consumer’s perspective, the entire infrastructure footprint collapses into a clean, declarative specification:
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: prod-analytics-cluster
namespace: default
spec:
topology:
class: standard-aws-cluster
version: v1.31.0
controlPlane:
replicas: 3
workers:
machineDeployments:
- class: "general-purpose"
name: "app-pool"
replicas: 10
- class: "gpu-worker"
name: "training-pool"
replicas: 4
variables:
- name: region
value: "us-west-2"
- name: workerInstanceType
value: "c6i.4xlarge"
Consumers do not configure VPC route tables, security group ingress rules, or kubeadm bootstrap tokens. They interact purely with the platform API exposed by the ClusterClass.
5. Lifecycle Orchestration Under Managed Topologies
When operating a managed cluster, lifecycle operations transition from imperative manual patching to single-point topology mutations:
sequenceDiagram
autonumber
actor Consumer
participant API as Kubernetes API
participant TC as Topology Controller
participant KCP as KubeadmControlPlane
participant MD as MachineDeployments
Note over Consumer,MD: Scenario: Fleet Upgrade from v1.30 to v1.31
Consumer->>API: Patch Cluster.spec.topology.version = v1.31.0
API-->>TC: Triggers Topology Reconciliation
TC->>TC: Validates version & renders updated templates
TC->>KCP: Patches KCP.spec.version = v1.31.0
Note over TC,MD: Holds worker MachineDeployments at v1.30 (Version Skew Gate)
KCP->>KCP: Executes rolling upgrade of control-plane machines
KCP-->>TC: Reports status.ready = true & version = v1.31.0
TC->>MD: Cascades version = v1.31.0 to worker MachineDeployments
MD->>MD: Generates new MachineSets & rolls worker fleet
TC-->>API: Sets Cluster condition TopologyReconciled = True
1. Scaling Capacity
To scale a worker fleet, update spec.topology.workers.machineDeployments[name].replicas. The topology controller updates the underlying MachineDeployment, which cascades replica drift down to the MachineSet.
2. Upgrading Kubernetes Versions
To upgrade an entire cluster, change spec.topology.version. The topology controller manages the rolling replacement: it patches the control plane first, enforces the Kubernetes version skew policy by holding worker versions, verifies control plane stability, and automatically cascades the upgrade to all worker pools.
3. Modifying Infrastructure Flavors
To change instance types, storage classes, or IAM roles, consumers adjust variable values in spec.topology.variables. The patch engine compiles new immutable templates and triggers rolling updates.
6. The Golden Rule of Managed Topologies
The most common operational mistake when adopting ClusterClass is treating generated child resources as independent objects:
ANTI-PATTERN: Directly editing child objects in a managed cluster:
kubectl edit kubeadmcontrolplane ...
kubectl scale machinedeployment ...
flowchart LR
OPERATOR["Operator runs kubectl edit<br/>on generated MachineDeployment"] --> MUTATE["Mutates child object directly in etcd"]
MUTATE --> TRIGGER["Topology Controller detects drift<br/>relative to Cluster.spec.topology"]
TRIGGER --> REVERT["Topology Controller overwrites child object<br/>back to declared topology state"]
The Golden Rule: If a cluster defines
spec.topology, theClusterresource is the single source of truth. Any manual edits made directly to child resources (KCP,MachineDeployment, or generated templates) will be automatically detected as drift and reverted by the Topology Controller.
Always direct mutations to Cluster.spec.topology.
7. Diagnostic Workflows & Decision Trees
When topology reconciliation encounters invalid schemas, failing patches, or blocked child rollouts, use a structured diagnostic sequence:
flowchart TD
START["Topology Reconciliation Stalled"] --> CHECK{"Inspect Cluster Conditions"}
CHECK -->|"TopologyReconciled = False"| T_FAIL{"Where is Topology Failing?"}
T_FAIL -->|"Variable Validation Error"| V1["Check Cluster.spec.topology.variables against<br/>ClusterClass openAPIV3Schema rules"]
T_FAIL -->|"Patch Engine Failure"| V2["Inspect ClusterClass patches: invalid JSON path<br/>or missing template selector match"]
T_FAIL -->|"Template Reference Missing"| V3["Verify all referenced templates in ClusterClass<br/>exist in the target namespace"]
CHECK -->|"TopologyReconciled = True"| CH_FAIL{"Child Controller Blocked?"}
CH_FAIL -->|"KCP Rolling Upgrade Stuck"| C1["Check control-plane etcd quorum & cloud quotas<br/>(See Part 10 on Upgrades)"]
CH_FAIL -->|"MachineDeployment Drain Stuck"| C2["Check PDB violations & volume unmount locks<br/>(See Part 7 on Worker Internals)"]
Essential Inspection Commands
1. Inspect Topology Condition on the Cluster
kubectl describe cluster <cluster-name>
Look for the TopologyReconciled condition:
Status: "True": The desired topology was successfully parsed, patched, and applied to all child resources.Status: "False": Indicates a patch execution error or schema validation failure. TheMessagefield specifies the exact JSON path or variable that failed.
2. Inspect the Blueprint Definition
kubectl describe clusterclass <class-name>
Inspect the defined worker classes, variable schemas, and patch selectors.
3. View the Complete Generated Resource Hierarchy
clusterctl describe cluster <cluster-name> --show-conditions
Confirms whether generated control plane and worker deployments are progressing toward the declared topology state.
8. Key Takeaways
- The Class-Instance Abstraction:
ClusterClassdefines the reusable, parameterized architecture of a cluster;Cluster.spec.topologyinstantiates and customizes a specific cluster. - Server-Side Validation & Compilation: Variables are strongly validated against OpenAPI v3 schemas before being injected into templates via server-side JSON patches.
- The Cluster is the Single Control Point: Scaling, upgrades, and configuration adjustments are executed by mutating
Cluster.spec.topology. Never edit generated child objects directly. - Clean Organizational Boundaries: Platform engineering teams author and govern
ClusterClassdefinitions; application and infrastructure operators consume self-service clusters via concise topology manifests. - Underlying CAPI Mechanics Remain Intact:
ClusterClassdoes not replace core providers or controllers. It acts as an orchestrator sitting on top of standardMachineDeployment,KubeadmControlPlane, andMachineresources.
Next in the series
Now that we understand how ClusterClass and managed topologies standardize multi-cluster lifecycle management, explore Part 12: Cluster Autoscaler and Cluster API to examine how the Kubernetes Cluster Autoscaler integrates with declarative MachineDeployments to scale worker fleets dynamically based on workload scheduling pressure.