Cluster API
Kubernetes cluster의 생성, 확장, 업그레이드, 삭제를 Kubernetes resource로 선언하고 controller가 맞춰 주는 Cluster API의 구조와 동작
Cluster API(CAPI)는 Kubernetes cluster 자체를 Kubernetes resource로 다루는 프로젝트다.
Cluster,Machine,MachineDeployment같은 object를 management cluster에 apply하면, controller가 VM이나 bare metal 서버를 만들고 kubeadm으로 node를 묶어 workload cluster를 완성한다.
Intro
cluster 하나를 만드는 도구는 이미 많다. kubeadm을 직접 실행해도 되고, 클라우드마다 EKS, AKS, GKE 같은 관리형 서비스가 있다.
그런데 cluster가 수십 개로 늘어나 public cloud, 사내 bare metal, VM에 나뉘어 배치되면 cluster마다 생성 스크립트가 다르고, 업그레이드 절차도 다르며, 어떤 cluster가 어떤 버전으로 실행 중인지도 한곳에서 확인할 수 없다.
Cluster API는 이 문제를 Kubernetes가 Pod를 다루는 방식으로 푼다.
Deployment에 replica 3을 적으면 controller가 Pod 3개를 유지하듯이, MachineDeployment에 replica 3을 적으면 controller가 node 3개를 유지한다.
cluster의 원하는 상태는 YAML로 기록되고, 실제 상태를 원하는 상태에 맞추는 일은 reconcile loop가 맡는다. 그래서 cluster 생성, scale out, 업그레이드, 삭제를 모두 kubectl apply와 kubectl delete로 처리할 수 있다.
이 글은 2026년 10월 기준 최신 릴리스인 CAPI v1.14와
v1beta2API를 기준으로 설명한다.
Management and Workload Clusters
CAPI는 cluster를 두 종류로 나눈다.
- management cluster는 CAPI controller와 provider controller가 실행되고,
Cluster나Machine같은 CAPI resource가 저장되는 cluster다. - workload cluster는 management cluster가 만들어 낸 cluster다. 사용자의 애플리케이션은 이 cluster에서 실행된다.
만들어냈다는건, 실제로 Management Cluster에서 Workload Cluster를 Provision 했음을 의미한다.
management cluster도 일반 Kubernetes cluster이므로, 첫 management cluster는 CAPI 없이 만들어야 한다.
보통은 kind(k8s in docker)로 로컬에 임시 management cluster를 띄우고, 그 cluster로 첫 workload cluster를 만든다.
그다음 clusterctl move로 CAPI resource를 새 cluster로 옮기고, 그 cluster를 영구 management cluster로 사용한다.
clusterctl은 CAPI 프로젝트가 공식으로 제공하는 CLI이다.
공식 문서는 이때 쓰는 임시 management cluster를 bootstrap cluster라고 부른다.
flowchart LR
subgraph MC["Management cluster"]
CORE["CAPI core controllers"]
BS["Bootstrap provider<br/>(kubeadm)"]
CP["Control plane provider<br/>(KubeadmControlPlane)"]
INFRA["Infrastructure provider<br/>(CAPA, CAPZ, CAPM3 ...)"]
end
INFRA -->|"create VM / server"| CLOUD["Cloud API, BMC"]
CLOUD --> WC1["Workload cluster A"]
CLOUD --> WC2["Workload cluster B"]
CORE -.->|"watch nodes via kubeconfig"| WC1
CORE -.->|"watch nodes via kubeconfig"| WC2
management cluster는 workload cluster의 API server에 직접 접속한다.
workload cluster의 kubeconfig는 management cluster의 Secret(<cluster-name>-kubeconfig)으로 저장되고, controller는 이 kubeconfig로 workload cluster의 Node를 조회해 Machine과 연결한다.
따라서 management cluster에서 workload cluster의 API server까지 네트워크 경로가 열려 있어야 한다.
Workload Cluster에서 InBound 허용을 해야한다
Core Objects
CAPI resource는 Kubernetes의 workload resource와 거의 같은 계층 구조를 가진다.
| CAPI resource | 대응하는 Kubernetes resource | 역할 |
|---|---|---|
Cluster | (없음) | cluster 전체. network 설정과 control plane, infrastructure 참조를 가진다 |
Machine | Pod | node 하나. 생성 후 spec을 바꾸지 않는다 |
MachineSet | ReplicaSet | 같은 template의 Machine을 N개 유지한다 |
MachineDeployment | Deployment | MachineSet을 교체하며 rolling update를 수행한다 |
MachinePool | (없음) | AWS ASG처럼 provider가 그룹 단위로 관리하는 node 묶음 |
MachineHealthCheck | (liveness probe와 유사) | 조건을 만족하지 않는 Machine을 지우고 새로 만들게 한다 |
ClusterClass | (없음) | cluster 모양을 template으로 정의한다 |
Cluster는 직접 resource를 만들지 않고, 다른 resource를 가리키는 참조 두 개를 가진다.
하나는 control plane resource(예: KubeadmControlPlane, KThreesControlPlane)이고, 다른 하나는 infrastructure resource(예: DockerCluster, AWSCluster)다.
Cluster CR이 KubeadmControlPlane이나 AWSCluster 같은 CR을 참조하는 방식. Cluster CR을 제거하면 같이 제거된다.
Machine도 마찬가지로 bootstrap resource(KubeadmConfig, KThreesConfig)와 infrastructure machine resource(DockerMachine, Metal3Machine)를 참조한다.
CAPI core는 이 참조를 따라가며 상태를 수집하고, 실제 작업은 각 provider controller가 수행한다.
flowchart TD
C["Cluster"] --> KCP["ControlPlane<br/>(KubeadmControlPlane, KThreesControlPlane)"]
C --> IC["InfraCluster<br/>(DockerCluster, AWSCluster ...)"]
KCP --> CPM["Machine x3<br/>(control plane)"]
MD["MachineDeployment"] --> MS["MachineSet"]
MS --> WM["Machine xN<br/>(worker)"]
CPM --> KC1["KubeadmConfig / KThreesConfig"]
CPM --> IM1["InfraMachine"]
WM --> KC2["KubeadmConfig / KThreesConfig"]
WM --> IM2["InfraMachine"]
MHC["MachineHealthCheck"] -.->|"selects"| WM
control plane Machine은 MachineDeployment가 아니라 KubeadmControlPlane이 직접 소유한다. control plane node를 교체할 때는 etcd member를 추가하고 제거하는 순서를 지켜야 하므로, 일반 worker처럼 MachineSet에 맡기지 않는다.
즉 Workload Cluster의 ControlPlane용 Machine(Node)는 Management Cluster ControlPlane CR이 소유하고, 나머지는 MachineDeployment를 사용한다.
MachineHealthCheck
MachineHealthCheck는 label selector로 Machine을 고르고, 연결된 Node가 일정 시간 동안 특정 condition에 머물면 그 Machine을 unhealthy로 표시한다.
기본 remediation은 Machine을 삭제하는 것이고, 소유자인 MachineSet이 replica 수를 맞추려고 새 Machine을 만든다. v1beta2에서는 판단 기준이 checks 아래로, 동작이 remediation 아래로 정리됐다.
apiVersion: cluster.x-k8s.io/v1beta2
kind: MachineHealthCheck
metadata:
name: md-0-unhealthy-5m
spec:
clusterName: capi-quickstart
selector:
matchLabels:
cluster.x-k8s.io/deployment-name: capi-quickstart-md-0
checks:
nodeStartupTimeoutSeconds: 600
unhealthyNodeConditions:
- type: Ready
status: Unknown
timeoutSeconds: 300
- type: Ready
status: "False"
timeoutSeconds: 300
이처럼 MachineHealthCheck는 고장 난 node를 고치지 않고 새 node로 교체한다. bare metal 환경에서는 서버를 재부팅하는 external remediation(Metal3의 Metal3Remediation 등)을 붙일 수도 있다.
ClusterClass and Topology
Cluster, KubeadmControlPlane, MachineDeployment, 각종 template을 cluster마다 따로 쓰면 YAML이 수백 줄이 된다. ClusterClass는 이 묶음을 template 하나로 정의하고, Cluster는 spec.topology에 class 이름과 버전, replica 수, 변수만 적는다.
StorageClass와 비슷
다음은 CAPD의 development flavor가 생성하는 quick-start ClusterClass에서 주요 부분만 남긴 예시다. ClusterClass는 resource를 직접 만들지 않고, control plane, infrastructure, worker class마다 사용할 template을 templateRef로 가리킨다.
apiVersion: cluster.x-k8s.io/v1beta2
kind: ClusterClass
metadata:
name: quick-start
spec:
controlPlane:
templateRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: KubeadmControlPlaneTemplate
name: quick-start-control-plane
machineInfrastructure:
templateRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: DevMachineTemplate
name: quick-start-control-plane
infrastructure:
templateRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: DevClusterTemplate
name: quick-start-cluster
workers:
machineDeployments:
- class: default-worker
bootstrap:
templateRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1beta2
kind: KubeadmConfigTemplate
name: quick-start-default-worker-bootstraptemplate
infrastructure:
templateRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: DevMachineTemplate
name: quick-start-default-worker-machinetemplate
healthCheck:
checks:
unhealthyNodeConditions:
- type: Ready
status: Unknown
timeoutSeconds: 300
variables:
- name: imageRepository
required: true
schema:
openAPIV3Schema:
type: string
default: ""
patches:
- name: imageRepository
enabledIf: '{{ ne .imageRepository "" }}'
definitions:
- selector:
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: KubeadmControlPlaneTemplate
matchResources:
controlPlane: true
jsonPatches:
- op: add
path: /spec/template/spec/kubeadmConfigSpec/clusterConfiguration/imageRepository
valueFrom:
variable: imageRepository
Cluster는 이 ClusterClass를 classRef로 참조하고, class에 정의된 worker class(default-worker)를 골라 replica 수를 지정한다.
apiVersion: cluster.x-k8s.io/v1beta2
kind: Cluster
metadata:
name: capi-quickstart
spec:
topology:
classRef:
name: quick-start
version: v1.37.0
controlPlane:
replicas: 3
workers:
machineDeployments:
- class: default-worker
name: md-0
replicas: 3
topology controller는 이 Cluster를 보고 필요한 KubeadmControlPlane, MachineDeployment, infrastructure template을 생성하고 관리한다.
업그레이드도 spec.topology.version 한 줄을 바꾸면 된다. controller가 control plane을 먼저 올리고, control plane이 끝난 뒤 worker MachineDeployment를 순서대로 올린다.
이 기능은 ClusterTopology feature gate로 켜며, clusterctl init 전에 CLUSTER_TOPOLOGY=true를 설정한다.
Provider Model
CAPI core는 서버를 만드는 방법을 알지 못한다. 서버를 만드는 일은 세 종류의 provider가 맡고, 각 provider는 CAPI가 정한 contract(필드와 condition의 규약)를 구현한다.
- bootstrap provider는 node가 cluster에 합류하는 데 필요한 cloud-init이나 Ignition 데이터를 만든다. 기본은 kubeadm(CABPK)이고, 결과물은 Secret으로 저장된다.
- control plane provider는 control plane을 만들고 업그레이드한다. 기본 구현인
KubeadmControlPlane(KCP)은 control planeMachine을 만들고 etcd 상태를 확인하며 하나씩 교체한다. - infrastructure provider는 실제 compute와 network를 만든다. VM, 로드밸런서, 보안 그룹, bare metal provisioning이 여기에 속한다.
cloud-init은 VM이나 서버가 처음 부팅될 때 초기 설정을 자동으로 실행하는 도구인데 사실상 업계 표준이다.
infrastructure provider는 환경마다 다르다.
| Provider | 대상 | InfraMachine 예시 | 비고 |
|---|---|---|---|
| CAPA | AWS | AWSMachine | EC2 기반 self-managed와 EKS 기반 managed 모두 지원 |
| CAPZ | Azure | AzureMachine | VM과 AKS 지원 |
| CAPG | Google Cloud | GCPMachine | GCE VM과 GKE 지원 |
| CAPM3 | bare metal (Metal3) | Metal3Machine | Ironic이 관리하는 BareMetalHost를 골라 OS 이미지를 설치 |
| CAPT | bare metal (Tinkerbell) | TinkerbellMachine | Tinkerbell workflow로 PXE 부팅과 이미지 설치 |
| CAPD | Docker | DockerMachine, DevMachine | container를 node로 사용. 개발과 테스트 전용 |
CAPM3와 CAPT는 bare metal 서버를 cloud VM처럼 다루게 해 준다.
둘 다 서버를 미리 inventory로 등록해 두고, Machine이 생기면 inventory에서 서버 하나를 골라 이미지를 설치하는 방식이다.
CAPM3는 Ironic이 BMC(IPMI, Redfish)를 통해 전원과 부팅을 제어하고, CAPT는 Tinkerbell이 DHCP, iPXE, workflow 엔진으로 같은 일을 한다.
Ironic은 OpenStack 프로젝트에서 나온 BM Provisioning 서비스이고, BMC를 사용해 물리 서버를 VM처럼 API로 on/off 하고 OS 설치하게 해준다. IPMI/Redfish는 BMC를 제어하는 프로토콜이다.
CAPI core는 이 차이를 알지 못하므로, cloud VM이든 bare metal 서버든 같은 Machine과 MachineDeployment로 다룬다.
Reconcile Flow
Cluster를 apply했을 때 각 controller가 어떤 순서로 동작하는지 살펴본다. 각 controller는 서로를 직접 호출하지 않고, 상대 resource의 status 필드와 condition을 보고 다음 단계로 넘어간다.
sequenceDiagram
autonumber
actor U as User
participant API as Mgmt API server
participant CC as Cluster controller
participant IC as Infra provider
participant KCP as KCP controller
participant BS as Kubeadm bootstrap
participant MC as Machine controller
participant WC as Workload cluster
U->>API: kubectl apply (Cluster, KCP, MD, templates)
CC->>API: set owner refs on InfraCluster and KCP
IC->>IC: create network, load balancer
IC->>API: InfraCluster status.initialization.provisioned = true, spec.controlPlaneEndpoint
KCP->>API: create first control plane Machine + KubeadmConfig + InfraMachine
BS->>API: generate kubeadm init cloud-init Secret
IC->>IC: create VM/server with bootstrap data
IC->>API: InfraMachine provisioned, providerID
WC-->>WC: kubeadm init, API server starts
KCP->>API: write CLUSTER_NAME-kubeconfig Secret
MC->>WC: find Node by providerID, set Machine.status.nodeRef
KCP->>API: create remaining control plane Machines (kubeadm join)
Note over API,WC: MachineDeployment -> MachineSet -> worker Machines follow the same join path
CC->>API: Cluster conditions Available = True
- bootstrap provider는 infrastructure provider가 control plane endpoint를 기록한 뒤에야 cloud-init을 만든다. 이 endpoint가 있어야
kubeadm join명령을 생성할 수 있기 때문이다. - infrastructure provider는 bootstrap Secret이 생겨야 VM을 만든다. 그래서
Machine이Pending에 오래 머물면 bootstrap provider를,Provisioning에 머물면 infrastructure provider를 먼저 확인한다. Machine과 Node는providerID로 연결된다. VM은 실행됐는데Machine.status.nodeRef가 비어 있다면 kubelet이 join에 실패해 Node가 등록되지 않은 경우가 많다. 반대로nodeRef는 채워졌는데 Node가NotReady라면 CNI가 아직 설치되지 않은 경우가 많다.
CAPI는 CNI를 설치하지 않는다. workload cluster가 생성된 뒤 사용자가 직접 설치하거나, ClusterResourceSet이나 add-on provider(Helm 기반 CAAPH 등)로 자동 설치하도록 설정해야 한다.
Upgrades and Rollout
CAPI의 Machine은 immutable이다. CAPI는 실행 중인 node의 kubelet을 그 자리에서 업그레이드하지 않고, 새 버전의 node를 만든 뒤 옛 node를 지운다.
이 방식을 immutable infrastructure라고 부른다. node를 그 자리에서 수정하면 node마다 설정이 조금씩 달라지지만, 매번 같은 template으로 새로 만들면 모든 node가 template과 일치한다.
worker 업그레이드는 Deployment의 rolling update와 같은 방식으로 동작한다.
MachineDeployment의 template(Kubernetes 버전, 이미지, instance type)을 바꾸면 controller가 새 MachineSet을 만들고, maxSurge와 maxUnavailable에 맞춰 새 MachineSet의 replica를 늘리면서 옛 MachineSet의 replica를 줄인다.
옛 Machine을 지우기 전에는 Node를 cordon하고 drain한다.
flowchart LR
subgraph Before
MSA["MachineSet v1.36<br/>replicas 3 → 0"]
end
subgraph After
MSB["MachineSet v1.37<br/>replicas 0 → 3"]
end
MD2["MachineDeployment<br/>version v1.37"] --> MSA
MD2 --> MSB
MSA -->|"cordon, drain, delete"| X["old Machines"]
MSB -->|"create, join"| Y["new Machines"]
control plane 교체는 Cluster.spec.controlPlaneRef가 가리키는 control plane provider가 맡는다.
버전 업그레이드나, 디스크 크기, VM 이미지 교체 등을 위해 MachineTemplate 변경하는 경우에 Machine 교체가 일어나는 경우가 될 수 있겠다.
CAPI core는 교체 절차를 정하지 않으므로 구체적인 방식은 provider마다 다르다. 다만 etcd를 control plane node에 함께 두는 provider는 대체로 같은 순서를 따른다.
새 control plane Machine을 하나 추가해 etcd member로 합류시키고, 옛 Machine의 etcd member를 제거한 뒤 그 Machine을 지운다.
이 과정을 한 대씩 반복하므로 3대 구성에서도 etcd quorum이 유지된다. KubeadmControlPlane과 k3s용 KThreesControlPlane이 모두 이 방식으로 동작하며, 지울 Machine이 etcd leader라면 먼저 다른 member로 leadership을 넘긴다.
한편 control plane provider와 관계없이 CAPI core가 보장하는 동작도 있다.
topology 기반 cluster에서 spec.topology.version을 바꾸면, topology controller는 control plane이 새 버전에 도달한 뒤에야 worker MachineDeployment의 교체를 시작한다.
또한 Kubernetes minor 버전은 한 번에 건너뛸 수 없으므로 v1.35에서 v1.37로 가려면 v1.36을 거쳐야 한다. v1beta2 ClusterClass에 spec.kubernetesVersions로 사용할 버전 목록을 적어 두면, spec.topology.version을 v1.37로 바꿨을 때 topology controller가 v1.36을 거치는 upgrade plan을 계산하고, control plane을 v1.36, v1.37 순서로 업그레이드한다. 계산된 plan은 Cluster의 status.controlPlane.upgradePlan에서 확인할 수 있다.
다만 교체 속도를 조절하는 설정은 provider마다 다르다.
KubeadmControlPlane은 rollout strategy의maxSurge로 한 번에 추가할Machine수를 정하지만,KThreesControlPlane에는 이 필드가 없고 한 대씩 교체하는 방식으로 고정되어 있다.
node drain도 CAPI core의 Machine controller가 수행하므로 control plane과 worker에 똑같이 적용된다. drain은 PodDisruptionBudget을 지킨다.
그래서 PodDisruptionBudget이 적용된 Pod가 이미 unhealthy 상태라면 eviction이 계속 거부되고, 옛 Machine은 Deleting 단계에 머문다.
최근 CAPI는 일부 변경을 node 교체 없이 적용하는 in-place update 기능을 실험적으로 추가하고 있다. 다만 기본 동작은 여전히 교체 방식이다. 이미지가 크고 부팅이 느린 node에서는 provider가 제공하는 surge 설정과 drain timeout을 먼저 조정하는 편이 현실적이다.
References
- Cluster API Book
- Cluster API Quick Start
- Cluster API Concepts
- Cluster API Version Support
- Configure a MachineHealthCheck
- ClusterClass
- kubernetes-sigs/cluster-api Releases
- Cluster API Provider AWS
- Cluster API Provider Azure
- Cluster API Provider GCP
- Cluster API Provider Metal3
- Cluster API Provider Tinkerbell