Learning Journey: Part 03 of 16

Module Goal: Master the complete Cluster API object graph: how Cluster, Machine, MachineSet, MachineDeployment, MachinePool, KubeadmControlPlane, BootstrapConfig, and Infrastructure resources relate, delegate, and transfer state.
Target Spec: Cluster API v1.14 (v1beta2 APIs).
Prerequisites: Part 02: Cluster API Architecture: How the Control Plane Pieces Cooperate (Asynchronous Controller Coordination & Provider Contracts).


1. The Core Challenge: Decoupling Abstraction Overload

The most common obstacle when adopting Cluster API (CAPI) is not installation; it is navigating the object graph.

A standard CAPI management cluster registers dozens of Custom Resource Definitions (CRDs):

Cluster
Machine
MachineSet
MachineDeployment
MachinePool
KubeadmControlPlane
KubeadmConfig
InfrastructureCluster (e.g., AWSCluster / VSphereCluster)
InfrastructureMachine (e.g., AWSMachine / VSphereVM)
InfrastructureMachineTemplate (e.g., AWSMachineTemplate)
KubeadmConfigTemplate

At first glance, this layered object graph can appear over-engineered for a system designed to provision virtual machines and join them to a Kubernetes cluster.

However, each custom resource encapsulates a single, tightly scoped responsibility. To prevent monolithic controller code, Cluster API categorizes these resources into three architectural layers:

flowchart TD
    subgraph Layer1["Layer 1: Cluster Intent & High-Level Policy"]
        C["Cluster"]
        KCP["KubeadmControlPlane"]
        MD["MachineDeployment"]
        MP["MachinePool"]
        MHC["MachineHealthCheck"]
    end

    subgraph Layer2["Layer 2: Machine Lifecycle & Replica Management"]
        MS["MachineSet"]
        M["Machine"]
    end

    subgraph Layer3["Layer 3: Provider Implementation & Blueprints"]
        IC["InfrastructureCluster"]
        IM["InfrastructureMachine"]
        BC["KubeadmConfig"]
        IT["InfraMachineTemplate"]
        BT["KubeadmConfigTemplate"]
    end

    C -->|infraRef| IC
    C -->|controlPlaneRef| KCP
    C --> MD
    MD --> MS
    MS --> M
    KCP --> M
    M -->|bootstrapRef| BC
    M -->|infraRef| IM
    MD -. blueprint .-> IT
    MD -. blueprint .-> BT
    MHC --> M

Once every object is mapped to its layer, the object graph transforms from a confusing collection of CRDs into a predictable state machine.


2. The Complete Object Graph

To understand how a cluster comes into existence, we trace the full object tree across both worker nodes and the control plane.

flowchart TD
    C["Cluster"] --> MD["MachineDeployment"]
    MD -->|Manages Replicas & Rollouts| MS["MachineSet"]
    MS -->|Maintains Host Count| M["Machine"]
    
    MD -. Specifies Blueprint .-> BT["KubeadmConfigTemplate"]
    MD -. Specifies Blueprint .-> IT["InfraMachineTemplate"]
    
    BT -. Instantiates .-> BC["KubeadmConfig (Bootstrap)"]
    IT -. Instantiates .-> IM["InfraMachine (Infrastructure)"]
    
    M -->|Refers| BC
    M -->|Refers| IM
    
    IM -->|Provisions| HOST["Cloud VM / Hypervisor Host"]
    BC -->|Injects Cloud-Init / Join Script| HOST
    HOST -->|Registers via ProviderID| NODE["Kubernetes Node (Workload Cluster)"]

Worker Node Object Pathway

  1. MachineDeployment defines the desired worker count and references infrastructure/bootstrap blueprints (Templates).
  2. MachineSet maintains a fixed count of active Machine objects.
  3. Machine represents the lifecycle of a single host.
  4. KubeadmConfig generates the cloud-init bootstrap script.
  5. InfrastructureMachine provisions the cloud VM or bare-metal host.
  6. The host executes the bootstrap payload and registers as a Node inside the target workload cluster.

Control Plane Object Pathway

Control plane nodes follow a dedicated control-loop pathway to protect API server availability and etcd quorum:

flowchart TD
    C["Cluster"] --> KCP["KubeadmControlPlane"]
    KCP -->|Directly Manages CP Hosts| M_CP["Machine (Control Plane)"]
    M_CP --> BC_CP["KubeadmConfig"]
    M_CP --> IM_CP["InfraMachine"]
    IM_CP --> HOST_CP["Control Plane VM"]
    HOST_CP --> NODE_CP["Control Plane Node"]

3. The Workload Analogy: Pods vs. Machines

The fastest way to understand CAPI’s worker abstractions is to compare them with native Kubernetes workload primitives.

flowchart LR
    subgraph K8S["Native Kubernetes Workload Model"]
        direction TB
        DEP["Deployment"] -->|Manages| RS["ReplicaSet"]
        RS -->|Maintains| POD["Pod (Container Lifecycle)"]
    end

    subgraph CAPI["Cluster API Machine Worker Model"]
        direction TB
        MD["MachineDeployment"] -->|Manages| MS["MachineSet"]
        MS -->|Maintains| M["Machine (Host Lifecycle)"]
    end
Workload PrimitiveCluster API Worker PrimitiveFunctional Scope
DeploymentMachineDeploymentDeclarative specification of desired state, rolling update strategies, and scaling boundaries.
ReplicaSetMachineSetMaintains a stable pool of identical active replicas (Pods or Machines).
PodMachineAtomic execution unit representing a single scheduled entity (Container host vs. Node host).

4. Deep-Dive Custom Resource Breakdown

4.1. Cluster: The Root Intent Object

The Cluster object represents the top-level declaration of a single Kubernetes cluster. It does not directly create cloud resources; instead, it anchors the object graph by linking infrastructure and control plane providers.

apiVersion: cluster.x-k8s.io/v1beta2
kind: Cluster
metadata:
  name: production-us-east
  namespace: default
spec:
  clusterNetwork:
    services:
      cidrBlocks: ["10.96.0.0/12"]
    pods:
      cidrBlocks: ["192.168.0.0/16"]
    serviceDomain: "cluster.local"
  infrastructureRef:
    apiGroup: infrastructure.cluster.x-k8s.io
    kind: AWSCluster
    name: production-us-east
  controlPlaneRef:
    apiGroup: controlplane.cluster.x-k8s.io
    kind: KubeadmControlPlane
    name: production-us-east-control-plane

4.2. InfrastructureCluster: The Environment Contract

InfrastructureCluster is a contract role implemented by concrete infrastructure providers (e.g., AWSCluster, AzureCluster, GCPCluster, VSphereCluster).

It provisions cluster-wide networking and gateway infrastructure:

  • Virtual Private Clouds (VPC) / Subnets / Route Tables
  • Security Groups / Firewall Rules
  • External Control Plane Load Balancers (HA Proxy / AWS ELB)
  • Target API Server Endpoints (spec.controlPlaneEndpoint)
flowchart LR
    C["Cluster"] -->|spec.infrastructureRef| IC["InfrastructureCluster (e.g. AWSCluster)"]
    IC -->|Provisions| LB["Control Plane Load Balancer"]
    IC -->|Provisions| VPC["VPC & Subnets"]
    IC -->|Populates| Status["status.controlPlaneEndpoint"]

4.3. Machine: Host Lifecycle Abstraction

A common misconception is that Machine == VM.

A Machine is a declarative custom resource in the management cluster that tracks the lifecycle state of a host intended to become a Node in the workload cluster.

apiVersion: cluster.x-k8s.io/v1beta2
kind: Machine
metadata:
  name: production-us-east-worker-0
spec:
  clusterName: production-us-east
  bootstrap:
    configRef:
      apiGroup: bootstrap.cluster.x-k8s.io
      kind: KubeadmConfig
      name: production-us-east-worker-0
  infrastructureRef:
    apiGroup: infrastructure.cluster.x-k8s.io
    kind: AWSMachine
    name: production-us-east-worker-0
  version: v1.31.0

Machine vs. Node Boundary

flowchart LR
    subgraph MGMT["Management Cluster"]
        M["Machine Resource<br/>(spec.providerID)"]
    end

    subgraph WORKLOAD["Workload Cluster"]
        N["Kubernetes Node Resource<br/>(spec.providerID)"]
    end

    M <===>|Correlated asynchronously via ProviderID| N
  • Machine: Lives in the management cluster. Controls provisioning, bootstrapping, cloud instance lifecycle, and deletion.
  • Node: Lives in the workload cluster. Managed by kubelet and the Kubernetes control plane.
  • providerID: Unique provider URI (aws:///us-east-1a/i-0123456789abcdef0) used by CAPI controllers to bind a Machine object to its registered Node.

4.4. InfrastructureMachine & BootstrapConfig

A Machine delegates specialized work via two reference pointers:

flowchart TD
    M["Machine"] -->|bootstrap.configRef| BC["BootstrapConfig (e.g., KubeadmConfig)"]
    M -->|infrastructureRef| IM["InfrastructureMachine (e.g., AWSMachine)"]

    BC -->|Generates| DATA["Bootstrap Cloud-Init Payload"]
    IM -->|Provisions| VM["Virtual Machine Instance"]

    VM -->|Executes Data Payload| JOIN["Runs kubeadm join & Registers Node"]
  • BootstrapConfig (KubeadmConfig): Responsible for generating initialization scripts (cloud-init, ignition), ignition tokens, and kubeadm join parameters.
  • InfrastructureMachine (AWSMachine, VSphereVM): Responsible for concrete provider instance parameters (instance size, root disk, AMI ID, subnets, IAM roles).

4.5. MachineSet & MachineDeployment

MachineSet ensures a target number of active Machine replicas match a specific configuration spec.

MachineDeployment wraps MachineSet to provide zero-downtime rolling updates across worker node pools.

flowchart TD
    MD["MachineDeployment (Updated Spec: Image v2)"] -->|1. Creates| MS2["New MachineSet (v2)<br/>Replicas: 0 -> 1 -> 2 -> 3"]
    MD -->|2. Scales Down| MS1["Old MachineSet (v1)<br/>Replicas: 3 -> 2 -> 1 -> 0"]

    MS2 -->|Spawns| M2["New Machine (v2)"]
    MS1 -->|Drains & Evicts| M1["Old Machine (v1)"]

When a field in a worker template changes (e.g., updating the OS image or kubelet flag):

  1. MachineDeployment creates a new MachineSet.
  2. It scales up the new MachineSet while scaling down the old MachineSet.
  3. CAPI handles node cordon, pod eviction/drain, and VM termination safely.

5. Architectural Variants: MachineDeployment vs. MachinePool

While MachineDeployment manages individual CAPI Machine objects, some cloud providers offer native auto-scaling infrastructure primitives (e.g., AWS Auto Scaling Groups, Azure VM Scale Sets).

Cluster API supports MachinePool for these environments:

flowchart TD
    subgraph MD_MODEL["MachineDeployment Strategy (CAPI-Managed Replicas)"]
        MD["MachineDeployment"] --> MS["MachineSet"] --> M1["Machine 1"] & M2["Machine 2"] & M3["Machine 3"]
        M1 --> H1["VM 1"]
        M2 --> H2["VM 2"]
        M3 --> H3["VM 3"]
    end

    subgraph MP_MODEL["MachinePool Strategy (Provider-Managed Scaling Group)"]
        MP["MachinePool"] --> IMP["InfrastructureMachinePool (e.g. AWS ASG / Azure VMSS)"]
        IMP --> ASG["Cloud Auto Scaling Group"]
        ASG --> N1["Node 1"] & N2["Node 2"] & N3["Node 3"]
    end
Operational DimensionMachineDeploymentMachinePool
GranularityExplicit CAPI Machine object per host node.Single object representing an entire scaling group.
Reconciliation UnitIndividual machine lifecycle loops.Provider scaling group API handles instance creation.
Provider PortabilityUniversal (works on cloud, hypervisors, bare-metal).Requires provider implementation (InfrastructureMachinePool).
Use CaseCustom topologies, strict node placement, bare-metal.High-volume cloud auto-scaling, spot instance pools.

6. Template Blueprints vs. Runtime Objects

A MachineDeployment cannot point to static AWSMachine or KubeadmConfig instances, as every worker host requires a unique infrastructure object and bootstrap secret.

Instead, deployment resources reference Templates:

apiVersion: cluster.x-k8s.io/v1beta2
kind: MachineDeployment
metadata:
  name: production-workers
spec:
  clusterName: production-us-east
  replicas: 3
  template:
    spec:
      clusterName: production-us-east
      bootstrap:
        configRef:
          apiGroup: bootstrap.cluster.x-k8s.io
          kind: KubeadmConfigTemplate
          name: production-workers-bootstrap
      infrastructureRef:
        apiGroup: infrastructure.cluster.x-k8s.io
        kind: AWSMachineTemplate
        name: production-workers-infra
flowchart TD
    BT["KubeadmConfigTemplate"] -. Factory Blueprint .-> BC1["KubeadmConfig 1"] & BC2["KubeadmConfig 2"] & BC3["KubeadmConfig 3"]
    IT["AWSMachineTemplate"] -. Factory Blueprint .-> IM1["AWSMachine 1"] & IM2["AWSMachine 2"] & IM3["AWSMachine 3"]

    BC1 & IM1 --> M1["Machine 1"]
    BC2 & IM2 --> M2["Machine 2"]
    BC3 & IM3 --> M3["Machine 3"]

Templates serve as immutable factory blueprints. When MachineSet provisions a new replica, it clones the template spec to produce concrete runtime instances.


7. Contract References vs. OwnerReferences

Understanding CAPI requires distinguishing between Contract References and OwnerReferences:

flowchart TD
    subgraph Ref["Contract Reference (spec.infrastructureRef / spec.bootstrap.configRef)"]
        MA["Machine"] -. Pointer / Functional Dependency .-> IMA["InfrastructureMachine"]
    end

    subgraph OwnerRef["OwnerReference (metadata.ownerReferences)"]
        CL["Cluster"] ==>|Garbage Collection Ownership| MD["MachineDeployment"]
        MD ==>|Garbage Collection Ownership| MS["MachineSet"]
        MS ==>|Garbage Collection Ownership| M["Machine"]
        M ==>|Garbage Collection Ownership| IMA
    end
ParameterContract Reference (spec.*Ref)OwnerReference (metadata.ownerReferences)
PurposeConnects functional components across providers.Defines Kubernetes garbage collection and cascading deletion.
DirectionPoints from parent to dependency (Machine → AWSMachine).Points from child to parent (AWSMachine → Machine).
MutabilitySet during creation; defines resource bindings.Injected by controllers during object instantiation.

8. Status Aggregation & Debugging Traversal

CAPI controllers communicate status asynchronously using .status.conditions. Conditions bubble up from lower-level provider resources to root intent objects:

flowchart BT
    IM["InfraMachine<br/>(Status: InfrastructureReady = True)"] -->|Status Update| M["Machine<br/>(Status: Ready = True)"]
    BC["KubeadmConfig<br/>(Status: DataSecretReady = True)"] -->|Status Update| M
    M -->|Aggregates Ready Count| MS["MachineSet<br/>(Status: 3/3 Replicas Ready)"]
    MS -->|Aggregates Ready Count| MD["MachineDeployment<br/>(Status: Available = True)"]
    MD -->|Aggregates Summary| C["Cluster<br/>(Status: ControlPlaneReady & WorkersReady)"]

Diagnostic Graph Walk Procedure

When a node fails to join a cluster, navigate the object graph from top to bottom:

flowchart TD
    STEP1["1. Check Cluster Status<br/>kubectl get cluster"] --> STEP2["2. Check Control Plane & Deployments<br/>kubectl get kcp,md"]
    STEP2 --> STEP3["3. Inspect Machine Readiness<br/>kubectl get machines"]
    STEP3 --> STEP4{"Identify Pending Machine"}
    STEP4 -->|Check Bootstrap| STEP5["Inspect KubeadmConfig<br/>kubectl describe kubeadmconfig <name>"]
    STEP4 -->|Check Provider VM| STEP6["Inspect InfrastructureMachine<br/>kubectl describe awsmachine <name>"]

9. Key Learning Takeaways

  • Three-Layer Architecture: CAPI separates objects into Cluster Intent, Machine Lifecycle, and Provider Implementation.
  • Analogy Alignment: MachineDeployment → MachineSet → Machine directly mirrors Deployment → ReplicaSet → Pod.
  • Machine != Node: Machine lives in the management cluster; Node lives in the workload cluster. They correlate via spec.providerID.
  • Blueprints via Templates: Deployments use Templates (AWSMachineTemplate, KubeadmConfigTemplate) as factories to instantiate concrete resources per machine.
  • Reference Dualism: spec.*Ref establishes functional contracts; metadata.ownerReferences dictates cascading garbage collection during deletion.

10. Self-Check Questions

  1. Why does a MachineDeployment reference an InfrastructureMachineTemplate instead of an InfrastructureMachine directly?
  2. If a Node in the workload cluster is manually deleted using kubectl delete node, how does the management cluster detect and repair the failure?
  3. What is the fundamental architectural difference between MachineDeployment and MachinePool?
  4. How do Contract References and OwnerReferences differ during a cascading cluster deletion (kubectl delete cluster <name>)?

Next Milestone in the Learning Journey

In Part 04: Building the First Cluster API Environment, we will put this object model into practice by initializing a local management cluster, deploying provider CRDs using clusterctl, and executing an end-to-end workload cluster provisioning sequence.


References & Further Reading