Skip to content
Published on

Cluster API 徹底解説 — クラスタを Kubernetes リソースとして管理する

Authors

はじめに — クラスタが 1 つだった時代は終わった

Kubernetes 導入初期、組織あたりのクラスタは 1 つ、多くても 2〜3 個でした。しかし今は状況がまったく違います。環境別の分離(dev/stage/prod)、リージョン別の分離、規制ドメイン別の分離(ネットワーク分離が求められる金融機関ならなおさら)、テナントごとの専用クラスタ、そして店舗・工場・車両に配備されるエッジクラスタまで。数十個は当たり前で、通信事業者やリテールエッジを運用する組織は数百から数千のクラスタを回しています。

クラスタが 10 個を超えた瞬間、次の問いが日常になります。

  • 新しいクラスタを 1 つ作るのに何日かかるのか。その手順は文書化されているのか、それとも特定のエンジニアの頭の中にしかないのか。
  • クラスタ 37 番の Kubernetes バージョンはいくつか。フリート全体のバージョン分布を 1 画面で見られるか。
  • コントロールプレーンノードが死んだら、誰が、いつ、どうやって復旧するのか。
  • フリート全体を v1.31 から v1.32 に上げるのに何週間かかるのか。

これらの問いに自信を持って答えられないなら、クラスタライフサイクル管理が組織のボトルネックになっているということです。本稿で扱う Cluster API(略して CAPI)は、この問題に対する Kubernetes コミュニティの公式な解答です。中核となるアイデアはシンプルかつ急進的です。クラスタそのものを Kubernetes リソースとして宣言し、コントローラにその宣言を現実にさせる。

既存の方式はなぜ限界にぶつかるのか

コンソールクリック(ClickOps)

クラウドコンソールでボタンを押してクラスタを作る方式は、1 個作るだけなら最速です。しかし再現性がありません。3 か月後に同じ構成のクラスタを作るにはスクリーンショットと記憶に頼るしかなく、監査証跡も残しにくい。クラスタ間の設定ドリフトは必然で、「このクラスタだけなぜ違うのか」の原因を誰も説明できなくなります。

Terraform / OpenTofu

Infrastructure as Code としての Terraform は優れたツールですが、クラスタライフサイクルには構造的な弱点があります。

  • 実行時にしか動作しません。 terraform apply が終われば状態の監視も終わります。誰かがコンソールでノードグループを削除しても、次の apply まで誰も気づきません。継続的な reconcile が存在しないのです。
  • state ファイルが単一障害点です。 state のロック、バックエンド管理、破損した state の復旧は、それ自体が運用負担です。
  • ノードレベルのライフサイクル表現が弱い。「このノードが 10 分間 NotReady なら交換せよ」のような宣言は Terraform の言語では表現しにくい。
  • 数百クラスタ規模になると、ワークスペース/モジュール分割、plan の所要時間、ドリフト検知のすべてが苦痛になります。

kubeadm スクリプト

kubeadm はクラスタブートストラップの事実上の標準的な低レベルツールですが、あくまで「1 台のノードをクラスタに入れる」ためのツールです。VM のプロビジョニング、LB の構成、証明書の更新、ノードの交換、バージョンアップグレードの順序制御は、すべて利用者のシェルスクリプトに委ねられます。これらのスクリプトは時間とともに、誰も手を出せない秘伝の儀式と化します。

3 方式に共通する欠陥は 1 つです。宣言と現実を継続的に一致させる主体が存在しないこと。Kubernetes が Pod について解決したまさにその問題が、クラスタ自身については未解決のまま残っていたわけです。

Cluster API の核心哲学 — Kubernetes で Kubernetes を管理する

Cluster API は Kubernetes SIG Cluster Lifecycle が開発する公式サブプロジェクトです。その哲学は 3 文に要約できます。

  1. 宣言的 API。 クラスタ、マシン、コントロールプレーンをすべて CRD(カスタムリソース)として定義します。「ワーカー 3 台、バージョン v1.32.2」と宣言すれば終わりです。
  2. コントローラの reconcile ループ。 Deployment コントローラが Pod 数を合わせるように、CAPI コントローラはマシン数とバージョンを合わせます。現実が宣言から逸脱すれば(ノード障害、手動削除)、自動的に収束させます。
  3. プロバイダ抽象化。 AWS でも vSphere でもベアメタルでも、インフラごとの差異はプロバイダプラグインの背後に隠します。ユーザー体験は同じ kubectl と YAML のままです。

たとえるならこうです。Pod に Deployment があるように、クラスタには Cluster API があります。ReplicaSet が Pod を量産するように MachineSet がマシン(ノード VM)を量産し、Deployment が Pod をローリング更新するように MachineDeployment がノードをローリング交換します。Kubernetes 運用者がすでに知っているメンタルモデルを、クラスタの次元に引き上げたものです。

もう 1 つ重要な原則が**イミュータブルインフラ(不変インフラ)**です。CAPI はマシンを「修理」しません。設定やバージョンが変われば、新しいマシンを作り、古いマシンを破棄します。稼働中のノードに SSH で入ってパッケージをアップグレードする行為は、モデルの外側の行為です。

アーキテクチャ解剖 — management cluster と workload cluster

CAPI の世界には 2 種類のクラスタがあります。

  • Management cluster(管理クラスタ): CAPI のコントローラと CRD がインストールされたクラスタ。他のクラスタの宣言(YAML)がここに保存され、reconcile されます。
  • Workload cluster(ワークロードクラスタ): 管理クラスタが作り出す対象クラスタ。実際のアプリケーションはここで動きます。ワークロードクラスタ自身は CAPI の存在を知りません。
+----------------------------------------------------------------------+
|                      Management Cluster                              |
|                                                                      |
|  +----------------+  +----------------+  +------------------------+  |
|  | CAPI Core      |  | Bootstrap      |  | Control Plane          |  |
|  | Controller     |  | Provider       |  | Provider               |  |
|  | (Cluster,      |  | (CABPK:        |  | (KCP: KubeadmControl-  |  |
|  |  Machine, MS,  |  |  KubeadmConfig)|  |  Plane controller)     |  |
|  |  MD, MHC)      |  +----------------+  +------------------------+  |
|  +----------------+                                                  |
|  +-------------------------------+                                   |
|  | Infrastructure Provider       |   etcd に保存された宣言:           |
|  | (CAPA/CAPZ/CAPV/CAPD/Metal3)  |   Cluster, MachineDeployment,     |
|  +-------------------------------+   KubeadmControlPlane ...         |
+----------------------------------------------------------------------+
        |                        |                          |
        | プロビジョニング/reconcile|                          |
        v                        v                          v
+----------------+      +----------------+        +----------------+
| Workload       |      | Workload       |        | Workload       |
| Cluster A      |      | Cluster B      |        | Cluster C      |
| (prod-seoul)   |      | (prod-tokyo)   |        | (edge-store-7) |
+----------------+      +----------------+        +----------------+

コア CRD の関係図

CAPI の CRD は役割ごとにきれいに分かれています。まず関係を図で見てみましょう。

                         +-----------+
                         |  Cluster  |  クラスタ全体の傘リソース
                         +-----+-----+
                               |
            +------------------+-------------------+
            | controlPlaneRef                      | infrastructureRef
            v                                      v
 +----------------------+              +---------------------------+
 | KubeadmControlPlane  |              | (Infra)Cluster            |
 | (replicas, version)  |              | 例: DockerCluster,        |
 +----------+-----------+              |     AWSCluster: VPC/LB等  |
            |                          +---------------------------+
            | 作成/管理
            v
       +---------+     1:1      +------------------------+
       | Machine |------------- | (Infra)Machine         |
       +---------+              | 例: DockerMachine,     |
            ^                   |     AWSMachine: VM     |
            |                   +------------------------+
            |  作成                      ^ 1:1
   +--------+-------+                   |
   |   MachineSet   | <-- テンプレート参照: (Infra)MachineTemplate
   +--------+-------+                    KubeadmConfigTemplate
            ^
            | 作成/ローリング交換
   +--------+----------------+         +---------------------+
   |   MachineDeployment     |         | MachineHealthCheck  |
   |  (replicas, version,    |         |  異常マシンの検知 →   |
   |   rolling strategy)     |         |  remediation(交換)  |
   +-------------------------+         +---------------------+

各リソースの責務を整理すると次のとおりです。

リソースたとえ (Pod の世界)責務
ClusterNamespace に近い傘クラスタネットワーク CIDR とコントロールプレーン/インフラ参照を束ねる最上位リソース
MachinePodノード 1 台の宣言。不変 — スペックが変われば交換
MachineSetReplicaSet同一スペックのマシンのレプリカ数を維持
MachineDeploymentDeploymentMachineSet のローリング更新をオーケストレーション
KubeadmControlPlaneStatefulSet に近いコントロールプレーンのマシン群と etcd メンバーシップ、証明書、バージョン管理
KubeadmConfigcloud-init 生成器マシン起動時に実行する kubeadm init/join 設定の生成
MachineHealthChecklivenessProbe + 自動交換異常ノードを検知して remediation をトリガー

KubeadmControlPlane(KCP)はとりわけ重要です。コントロールプレーンは etcd のクォーラム(定足数)があるため、一般のワーカーのように気軽に入れ替えられません。KCP コントローラはアップグレード時に、新しいコントロールプレーンマシンを 1 台追加し、etcd メンバーとして合流させ、古いマシンを etcd から安全に除去してから破棄する手順を自動化します。手作業なら冷や汗ものの作業が、宣言 1 行で終わります。

プロバイダモデル — 抽象化の実体

CAPI 本体はインフラのことをまったく知りません。実際の VM 作成はインフラプロバイダが、ノード初期化スクリプトの生成はブートストラッププロバイダが、コントロールプレーンのオーケストレーションはコントロールプレーンプロバイダが担当します。

インフラプロバイダ

プロバイダ対象インフラ成熟度/備考
CAPAAWS (EC2, EKS)成熟。EKS managed control plane をサポート
CAPZAzure (VM, AKS)成熟。AKS managed topology をサポート
CAPGGCP (GCE, GKE)安定しているが CAPA/CAPZ より機能の幅は狭い
CAPVvSphereオンプレミス仮想化の事実上の標準的選択肢
CAPOOpenStack通信事業者/プライベートクラウドで活発
CAPDDocker コンテナ開発/テスト/CI 専用。本番禁止
Metal3ベアメタル (Ironic ベース)BMC で物理サーバの電源/イメージを制御
BYOH既存ホストの再利用OS 導入済みのホストをノードとして編入

ブートストラップ / コントロールプレーンプロバイダ

kubeadm がデフォルト(CABPK + KCP)ですが、エコシステムはより広がっています。

プロバイダディストリビューション特徴
kubeadm (デフォルト)バニラ Kubernetes最も成熟、リファレンス実装
k3sk3s軽量エッジ環境向け。k3s-io コミュニティが維持
RKE2RKE2Rancher 系、セキュリティ強化ディストリビューション
TalosTalos LinuxSSH のない不変 OS、API のみで管理。Sidero Labs
マネージド CPEKS/AKS/GKEインフラプロバイダに内蔵された managed control plane リソース

マネージド Kubernetes との関係は後段で別途扱いますが、要点だけ言えば、CAPA の AWSManagedControlPlane のようなリソースにより EKS のコントロールプレーンでさえ CAPI 宣言の対象になり得ます。

ハンズオン — CAPD でノート PC の上にクラスタ工場を回す

CAPD(Cluster API Provider Docker)は Docker コンテナを「マシン」として扱うため、クラウドアカウントなしにノート PC で全フローを体験できます。kind、Docker、clusterctl、kubectl が必要です。

ステップ 1 — management cluster の準備と clusterctl init

# CAPD が Docker ソケットを使えるように kind クラスタを作成
cat > kind-mgmt.yaml <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: capi-mgmt
nodes:
  - role: control-plane
    extraMounts:
      - hostPath: /var/run/docker.sock
        containerPath: /var/run/docker.sock
EOF
kind create cluster --config kind-mgmt.yaml

# ClusterClass 機能を有効化してから CAPI + CAPD をインストール
export CLUSTER_TOPOLOGY=true
clusterctl init --infrastructure docker

clusterctl init は cert-manager、CAPI コア、kubeadm ブートストラップ/コントロールプレーンプロバイダ、CAPD を management cluster にインストールします。インストールの確認:

kubectl get pods -A | grep -E "capi|capd|cert-manager"
# capd-system / capi-kubeadm-bootstrap-system /
# capi-kubeadm-control-plane-system / capi-system が Running なら正常

ステップ 2 — ワークロードクラスタ宣言 YAML の全文

clusterctl generate cluster でテンプレートを生成することもできますが、学習のためにコアリソースを直接見てみます。以下はコントロールプレーン 1 台 + ワーカー 2 台のクラスタの全文です。

# dev-cluster-01.yaml — Cluster: 最上位の傘
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: dev-cluster-01
  namespace: default
  labels:
    env: dev
    region: local
spec:
  clusterNetwork:
    pods:
      cidrBlocks: ["192.168.0.0/16"]
    serviceDomain: cluster.local
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: dev-cluster-01-cp
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: DockerCluster
    name: dev-cluster-01
---
# インフラクラスタ: CAPD では LB コンテナなどを表現
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerCluster
metadata:
  name: dev-cluster-01
  namespace: default
---
# コントロールプレーンマシンのインフラテンプレート
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
metadata:
  name: dev-cluster-01-cp
  namespace: default
spec:
  template:
    spec:
      extraMounts:
        - containerPath: /var/run/docker.sock
          hostPath: /var/run/docker.sock
---
# KubeadmControlPlane: コントロールプレーンの宣言
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: dev-cluster-01-cp
  namespace: default
spec:
  replicas: 1
  version: v1.31.4
  machineTemplate:
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: DockerMachineTemplate
      name: dev-cluster-01-cp
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        certSANs: [localhost, 127.0.0.1, 0.0.0.0, host.docker.internal]
    initConfiguration:
      nodeRegistration:
        criSocket: unix:///var/run/containerd/containerd.sock
    joinConfiguration:
      nodeRegistration:
        criSocket: unix:///var/run/containerd/containerd.sock
---
# ワーカーマシンのインフラテンプレート
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
metadata:
  name: dev-cluster-01-md-0
  namespace: default
spec:
  template:
    spec: {}
---
# ワーカーマシンのブートストラップ(join)設定テンプレート
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
  name: dev-cluster-01-md-0
  namespace: default
spec:
  template:
    spec:
      joinConfiguration:
        nodeRegistration:
          criSocket: unix:///var/run/containerd/containerd.sock
---
# MachineDeployment: ワーカープールの宣言
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: dev-cluster-01-md-0
  namespace: default
spec:
  clusterName: dev-cluster-01
  replicas: 2
  selector:
    matchLabels: null
  template:
    spec:
      clusterName: dev-cluster-01
      version: v1.31.4
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: dev-cluster-01-md-0
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: DockerMachineTemplate
        name: dev-cluster-01-md-0

なお、本稿の例は広く展開されている v1beta1 API を基準にしています。最新の CAPI リリースラインでは v1beta2 への移行が進んでいるため、適用前に使用中のリリースの API バージョンを確認してください。

kubectl apply -f dev-cluster-01.yaml

ステップ 3 — 収束プロセスの観察

# ツリー形式で全体の状態を見る(最も有用なコマンド)
clusterctl describe cluster dev-cluster-01

# 個別リソースの追跡
kubectl get cluster,machinedeployment,machineset,machine
kubectl get kubeadmcontrolplane
docker ps   # CAPD が作った「マシン」コンテナが見えます

Machine が Pending → Provisioning → Provisioned → Running と遷移していく様子は、Pod のライフサイクルとまったく同じ感覚です。

ステップ 4 — kubeconfig の取得と CNI のインストール

新しいクラスタのノードは CNI がないため NotReady 状態です。これは正常です。

clusterctl get kubeconfig dev-cluster-01 > dev-01.kubeconfig

kubectl --kubeconfig dev-01.kubeconfig get nodes
# STATUS NotReady — CNI インストール前なので正常

# Calico のインストール (Cilium、Flannel など何でも可)
kubectl --kubeconfig dev-01.kubeconfig apply -f \
  https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/calico.yaml

kubectl --kubeconfig dev-01.kubeconfig get nodes
# しばらくすると全ノード Ready

この時点で気づきが訪れます。たった今、クラスタを 1 つ kubectl apply 一発で作りました。100 個作るには? YAML を 100 セット apply すればいい。そしてその YAML は Git に入れられます。

運用の深掘り — アップグレード、スケーリング、自動復旧

ローリングアップグレードの原理 — ノードはパッチせず交換する

CAPI のアップグレードは OS パッチ型ではなくマシン交換型です。version フィールドを変えると、コントローラが新バージョンのマシンを作って合流させ、古いマシンを drain してから破棄します。

# コントロールプレーンのアップグレード: KCP の version のみ変更
kubectl patch kubeadmcontrolplane dev-cluster-01-cp --type merge \
  -p '{"spec":{"version":"v1.32.2"}}'

# ワーカーのアップグレード: MachineDeployment の version を変更
kubectl patch machinedeployment dev-cluster-01-md-0 --type merge \
  -p '{"spec":{"template":{"spec":{"version":"v1.32.2"}}}}'

内部の動作手順は次のとおりです。

KCP アップグレード (replicas=3 の場合)
 1. 新しい v1.32 コントロールプレーンマシンを 1 台作成 (計 4 台)
 2. kubeadm join --control-plane で合流、etcd メンバー 4
 3. 古い v1.31 マシンを 1 台選択 → etcd member remove → drain → 削除 (計 3 台)
 4. 古いマシンがなくなるまで 1〜3 を繰り返し
 5. クォーラムは常に維持: 3 → 4 → 3 → 4 → 3

MachineDeployment アップグレード (RollingUpdate 戦略)
 1. 新バージョンの MachineSet を作成
 2. maxSurge/maxUnavailable に従って新マシンを追加、古いマシンを drain して削除
 3. 古い MachineSet の replicas が 0 になるまで繰り返し

アップグレード順序のルールも Kubernetes のバージョンスキューポリシーそのままです。コントロールプレーンが先、ワーカーは後。kubelet は kube-apiserver より最大 3 マイナーバージョン低くてよいため、ワーカーのアップグレードは段階的に進める余裕があります。

スケーリング

# ワーカー 2 台 → 5 台
kubectl scale machinedeployment dev-cluster-01-md-0 --replicas=5

# コントロールプレーン 1 台 → 3 台 (HA への移行)
kubectl scale kubeadmcontrolplane dev-cluster-01-cp --replicas=3

オートスケーリングが必要なら、cluster-autoscaler が CAPI プロバイダモードをサポートしています。クラウドのノードグループの代わりに MachineDeployment をスケール対象とする方式です。

MachineHealthCheck — ノード障害の自動復旧

MachineHealthCheck(MHC)はノードの状態を監視し、異常状態が継続するとそのマシンを削除(=新しいマシンに交換)します。これが remediation です。

apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineHealthCheck
metadata:
  name: dev-cluster-01-worker-mhc
  namespace: default
spec:
  clusterName: dev-cluster-01
  # 一度に多くのマシンを交換しすぎないための安全弁
  maxUnhealthy: 40%
  # 起動後この時間内にノードとして合流できなければ異常
  nodeStartupTimeout: 10m
  selector:
    matchLabels:
      cluster.x-k8s.io/deployment-name: dev-cluster-01-md-0
  unhealthyConditions:
    - type: Ready
      status: Unknown
      timeout: 300s
    - type: Ready
      status: "False"
      timeout: 300s

maxUnhealthy は極めて重要な安全装置です。たとえば CNI 障害で全ノードが NotReady になったとき、MHC が全マシンを入れ替えてしまう大惨事を防いでくれます。異常率がしきい値を超えると remediation を止めて人間を待ちます。

ClusterClass — クラスタのテンプレート化

クラスタ 1 つにつき YAML 7 本を管理するのは、10 個でも苦行です。ClusterClass はクラスタの「クラス(テンプレート)」を定義し、個々のクラスタは変数を埋めるだけの薄い宣言にします。

apiVersion: cluster.x-k8s.io/v1beta1
kind: ClusterClass
metadata:
  name: standard-dev
  namespace: default
spec:
  controlPlane:
    ref:
      apiVersion: controlplane.cluster.x-k8s.io/v1beta1
      kind: KubeadmControlPlaneTemplate
      name: standard-dev-cp
    machineInfrastructure:
      ref:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: DockerMachineTemplate
        name: standard-dev-cp-machine
  infrastructure:
    ref:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: DockerClusterTemplate
      name: standard-dev-infra
  workers:
    machineDeployments:
      - class: default-worker
        template:
          bootstrap:
            ref:
              apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
              kind: KubeadmConfigTemplate
              name: standard-dev-worker-bootstrap
          infrastructure:
            ref:
              apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
              kind: DockerMachineTemplate
              name: standard-dev-worker-machine
  variables:
    - name: workerReplicas
      required: true
      schema:
        openAPIV3Schema:
          type: integer
          default: 2

これでクラスタ 1 つはここまで短くなります。topology フィールドが鍵です。

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: dev-cluster-02
  namespace: default
spec:
  clusterNetwork:
    pods:
      cidrBlocks: ["192.168.0.0/16"]
  topology:
    class: standard-dev
    version: v1.31.4
    controlPlane:
      replicas: 1
    workers:
      machineDeployments:
        - class: default-worker
          name: md-0
          replicas: 3
    variables:
      - name: workerReplicas
        value: 3

managed topology の真価はクラス変更の伝播にあります。ClusterClass のマシンテンプレートを修正すると、そのクラスを使うすべてのクラスタが(ポリシーに従い)ローリング交換で追従します。数百クラスタの標準化はこのメカニズムの上で回ります。

マシンイメージ管理 — image-builder

本番では「kubeadm と kubelet が焼き込まれた」ゴールデンイメージを使います。Kubernetes SIG が管理する image-builder プロジェクトが、Packer + Ansible ベースで AWS AMI、Azure イメージ、vSphere OVA、Raw/QCOW2(ベアメタル)などをビルドしてくれます。

git clone https://github.com/kubernetes-sigs/image-builder.git
cd image-builder/images/capi
# 例: vSphere 向け Ubuntu 22.04 + K8s v1.31 の OVA をビルド
make build-node-ova-vsphere-ubuntu-2204

イメージのバージョン管理がそのままノードのバージョン管理です。「K8s バージョン + OS パッチレベル」をイメージタグに固定すれば、クラスタの全ノードがビット単位で同一になります。イミュータブルインフラの土台です。

GitOps との結合 — クラスタフリートを Git で統治する

CAPI リソースは結局のところ普通の Kubernetes YAML なので、Argo CD や Flux で管理クラスタに sync すればクラスタの GitOpsが完成します。

+-----------+     push      +-----------+     sync      +---------------------+
| Platform  | ------------> |    Git    | ------------> | Management Cluster  |
| Team      |   PR レビュー  |  fleet-   |  Argo CD /    |  CAPI コントローラが  |
|           |               |  repo     |  Flux         |  reconcile          |
+-----------+               +-----------+               +----------+----------+
                                                                   |
                                              作成/アップグレード/復旧 v
                                              +---------------------------+
                                              | Workload Clusters (フリート)|
                                              +---------------------------+

リポジトリ構成の例は次のとおりです。

fleet-repo/
├── clusterclasses/
│   ├── standard-prod.yaml
│   └── standard-edge.yaml
├── clusters/
│   ├── prod/
│   │   ├── prod-seoul-01.yaml      # topology ベースの薄い宣言
│   │   └── prod-tokyo-01.yaml
│   ├── stage/
│   │   └── stage-seoul-01.yaml
│   └── edge/
│       ├── store-0001.yaml
│       └── store-0002.yaml
└── addons/
    ├── cni/                        # 新規クラスタに入る基本アドオン
    └── monitoring/

このパターンの運用効果は強力です。

  • クラスタの作成/変更が PR レビューを通過しなければなりません。監査証跡は Git の履歴そのものです。
  • バージョンアップグレードが「YAML の version フィールドを変える PR」1 つに標準化されます。
  • 新規クラスタに CNI・モニタリングなどのアドオンを自動展開するには、Argo CD ApplicationSet の cluster generator、または CAPI の ClusterResourceSet を使います。

フリート規模のネーミングとラベル戦略

数十〜数百クラスタでは、一貫したネーミングとラベルが自動化の取っ手になります。

ネーミング規則の例: <用途>-<リージョン>-<連番>
  prod-seoul-01, stage-tokyo-01, edge-store-0042

推奨ラベル (Cluster リソースに付与):
  env: prod | stage | dev
  region: seoul | tokyo | ...
  tier: core | edge
  team: payments | search | ...
  upgrade-wave: "1" | "2" | "3"   # アップグレードの波を制御

upgrade-wave ラベルは特に有用です。第 1 波(社内向けクラスタ)に新バージョンを先に適用して数日間寝かせ、問題なければ第 2 波、第 3 波へ広げる運用を、ラベルセレクタベースの自動化で構築できます。

pivot、バックアップ/DR、バージョンスキュー — 管理クラスタ自体の運用

鶏と卵 — management cluster は誰が作るのか

よくあるブートストラップパターンはこうです。

  1. ローカルに一時的な kind クラスタを作り clusterctl init
  2. 一時クラスタから「本物の」management cluster(クラウド/オンプレ)をワークロードクラスタとして作成
  3. clusterctl move ですべての CAPI リソースを新クラスタへ移行(pivot)
  4. 以後、management cluster が自分自身を管理(self-hosted)
# 新クラスタにプロバイダをインストールしてからリソースを移行
clusterctl init --kubeconfig mgmt-real.kubeconfig --infrastructure aws
clusterctl move --to-kubeconfig mgmt-real.kubeconfig

clusterctl move は Cluster をはじめとする所有チェーンのオブジェクトとシークレット(kubeconfig、CA、etcd 証明書)を丸ごと移します。移行中は reconcile が一時停止(pause)されるため、ワークロードクラスタは影響を受けません。

management cluster が死んだらワークロードも死ぬのか

いいえ。これが CAPI 設計の重要な美徳です。ワークロードクラスタは management cluster なしでも完全に独立して動作します。 失うのはライフサイクル操作(作成/アップグレード/自動復旧)だけです。それでも DR 計画は必要です。

  • CAPI リソースの原本が Git にあるなら(GitOps)、新しい management cluster を作って再 sync するのが第一の復旧経路です。ただしクラスタのシークレット(CA など)は Git にないため、バックアップが必須です。
  • Velero などで management cluster の CAPI ネームスペース(リソース+シークレット)を定期バックアップします。
  • 復旧リハーサルを必ず実施すべきです。「バックアップがある」と「復旧できる」は別の命題です。

バージョンスキューとアップグレード順序

管理体系そのもののバージョンも管理対象です。

アップグレード順序 (上から下へ)
 1. management cluster の Kubernetes バージョン
    (CAPI リリースがサポートする範囲を確認)
 2. clusterctl バイナリ
 3. CAPI コア + プロバイダ:  clusterctl upgrade plan / apply
 4. ワークロードクラスタ群の K8s バージョン (wave 単位でローリング)
    - 各クラスタ内部: コントロールプレーン → ワーカー
    - マイナーバージョンの飛び越え禁止 (1.30 → 1.32 は不可、1.31 経由)

CAPI の contract バージョン(インフラプロバイダとの互換契約)も併せて確認が必要です。コアだけ上げてプロバイダを放置すると、reconcile が止まる事故が起きます。clusterctl upgrade plan が互換マトリクスを計算してくれるので、必ず plan を先に見ます。

限界と現実 — 導入前に知っておくべきこと

学習コストと求められる運用成熟度

CAPI は抽象化レイヤが多い。問題が起きたら Cluster → KCP → Machine → InfraMachine → cloud-init ログまで降りていくデバッグチェーンを辿れなければなりません。Kubernetes のコントローラパターン(owner reference、condition、finalizer)に不慣れなチームには急な坂です。

プロバイダの成熟度のばらつき

CAPA/CAPZ/CAPV には大規模本番事例が多くありますが、一部のプロバイダは機能の幅もドキュメントも薄い。導入前に、当該プロバイダのリリース周期、Issue への応答速度、ClusterClass 対応の有無を必ず確認してください。

マネージド Kubernetes(EKS/AKS/GKE)との関係

「どうせ EKS を使うのに CAPI は必要か?」は正当な問いです。答えは組織の形によります。

  • EKS クラスタが 3 つだけなら、Terraform や eksctl で十分な可能性が高い。
  • しかし EKS + オンプレミス vSphere + エッジベアメタルが混在するフリートなら、CAPI は単一の API で全体を覆える事実上唯一の選択肢に近い。CAPA の AWSManagedControlPlane や CAPZ の AKS サポートのように、マネージドコントロールプレーンも CAPI リソースとして宣言できるからです。

Crossplane / Terraform との比較

観点Cluster APICrossplaneTerraform
主目的K8s クラスタライフサイクル専用汎用クラウドリソースの組み立て汎用 IaC
動作モデルコントローラの常時 reconcileコントローラの常時 reconcile実行時 apply
ノード/マシン抽象化第一級概念 (Machine など)なし (マネージド K8s 中心)モジュールで間接表現
コントロールプレーンのオーケストレーションKCP が etcd まで自動化マネージド CP に委任自前実装が必要
自動復旧MachineHealthCheck 内蔵リソースドリフト補正レベルなし
クラスタ外リソース (DB など)対象外強み強み
状態ストアetcd (K8s ネイティブ)etcdstate ファイル

3 者は敵ではなく分業関係であることが多い。実際に「VPC/IAM は Terraform、クラスタは CAPI、クラスタが使う DB は Crossplane」のようにレイヤを分ける組織がよくあります。

どんな組織に合うか — 意思決定ガイド

質問 1. 管理するクラスタが 5 個未満で増える計画もない
  → CAPI は過剰。マネージド K8s + IaC で十分。

質問 2. クラスタが 10 個以上、またはテナント/エッジで増える予定
  → CAPI の有力候補。特にクラスタ作成をセルフサービス化したいなら。

質問 3. オンプレミス (vSphere/ベアメタル) やマルチクラウドが混在
  → CAPI の最も強いユースケース。単一宣言モデルの価値が最大化。

質問 4. チームに K8s コントローラ/CRD の運用経験があるか
  → なければ、まず小さな規模 (CAPD 実習、dev クラスタ) で筋肉をつける。

質問 5. プラットフォームチームが存在するか
  → CAPI はプラットフォームチームの道具。専任のオーナーがいなければ放置されて腐ります。

トラブルシューティング — マシンが止まったとき見る順序

マシンが Provisioning で止まるのは CAPI 運用で最もよくある症状です。診断の順序を体に染み込ませておけば、大半は 20 分以内に原因へたどり着けます。

# 1. 全体像: どこで止まったかツリーで確認
clusterctl describe cluster dev-cluster-01 --show-conditions all

# 2. Machine の conditions とイベント
kubectl describe machine dev-cluster-01-md-0-xxxxx
kubectl get events --field-selector involvedObject.kind=Machine

# 3. インフラマシン (プロバイダ側) の状態 — VM は実際に起動したか
kubectl describe dockermachine dev-cluster-01-md-0-xxxxx
# AWS なら: kubectl describe awsmachine ...

# 4. ブートストラップシークレットは作られたか (cloud-init データ)
kubectl get secret | grep dev-cluster-01-md-0

# 5. コントローラログ — レイヤごとに
kubectl logs -n capi-system deploy/capi-controller-manager
kubectl logs -n capi-kubeadm-bootstrap-system \
  deploy/capi-kubeadm-bootstrap-controller-manager
kubectl logs -n capi-kubeadm-control-plane-system \
  deploy/capi-kubeadm-control-plane-controller-manager
kubectl logs -n capd-system deploy/capd-controller-manager

# 6. マシン内の cloud-init ログ (インフラごとにアクセス方法が異なる)
# CAPD: docker exec でコンテナに入って
#   cat /var/log/cloud-init-output.log  (または journalctl -u kubelet)

よく遭遇する原因の一覧です。

症状よくある原因
InfraMachine が作られないプロバイダ未インストール、credential シークレットの誤り、クォータ超過
VM は起動したが join しないイメージに kubeadm/kubelet がない、CP エンドポイントへのネットワーク遮断
最初の CP マシンで停止LB/エンドポイント未作成、certSANs の不一致、etcd の起動失敗
ノードが NotReady のままCNI 未インストール (正常な段階)、CNI 設定の誤り
アップグレード中に停止PDB により drain 不能、maxSurge のリソース不足
削除が終わらないfinalizer 待ち — インフラ削除の失敗をプロバイダログで確認

とりわけ PDB(PodDisruptionBudget)による drain のデッドロックはアップグレードの定番イシューです。minAvailable がレプリカ数と等しい PDB があると、drain は永遠に終わりません。nodeDrainTimeout を設定しておけば、一定時間後に強制的に先へ進められます。

導入チェックリスト

[ ] 管理対象クラスタ数/増加計画が CAPI 導入を正当化するか
[ ] インフラプロバイダ (CAPA/CAPV/Metal3 など) の成熟度を検証したか
[ ] CAPD ベースのローカル実習でチーム全員が全フローを体験したか
[ ] ゴールデンイメージのパイプライン (image-builder) を構築したか
[ ] ClusterClass で標準クラスタ形状をテンプレート化したか
[ ] MachineHealthCheck の maxUnhealthy 安全弁を設定したか
[ ] GitOps リポジトリ構成と PR レビューポリシーを定義したか
[ ] ネーミング/ラベル/アップグレード wave 戦略を文書化したか
[ ] management cluster のバックアップ (Velero など) と復旧リハーサルを完了したか
[ ] clusterctl upgrade plan に基づく定期アップグレードカレンダーがあるか
[ ] マシン停止のトラブルシューティング・ランブックを作成したか
[ ] PDB/nodeDrainTimeout ポリシーをワークロードチームと合意したか

おわりに

Cluster API の本質は「クラスタ作成ツール」ではなく、クラスタに対する運用モデルの置き換えです。コンソールクリックとスクリプトの世界では、クラスタは手で捏ねる工芸品でした。CAPI の世界では、クラスタは Deployment が量産する Pod のように、宣言で定義されコントローラが守る普通のリソースです。ノードが死ねば交換され、バージョンを上げればローリングで収束し、すべての変更が Git の履歴に残ります。

もちろんタダではありません。抽象化レイヤの学習コスト、プロバイダエコシステムを見極める目、そして management cluster という新しい運用対象が生まれます。クラスタ 5 個以下の組織には過剰な装備かもしれません。しかし、クラスタがまもなく数十個になる運命なら — そしてほとんどのプラットフォーム組織はその運命を避けられません — 早く学ぶほど利息のつく技術です。今日、ノート PC の上で kind と CAPD による 30 分の実習から始めてみてください。

参考資料