MicroAd Developers Blog

マイクロアドのエンジニアブログです。インフラ、開発、分析について発信していきます。

MAAS 環境における Cluster API 導入ガイド

はじめに

プラットフォームエンジニアリングチームの齊藤(id:saitoperf)です。 マイクロアドでは、Kubernetes (K8s) クラスタのライフサイクル管理に Rancher を使用しています。

Rancher は、OSS の K8s クラスタ管理ツールで、クラスタの構築・可視化・バージョンアップなど、運用を効率化する様々な機能を提供しています。 Cluster API は Rancher の内部で使われていますが、今まで意識することはありませんでした。

今回は、Cluster API を使って K8s クラスタを構築して管理していく方法をご紹介します。 再現には MAAS の環境が必要になるので、Cluster API でプラットフォームを操作して K8s のライフサイクルを管理していく雰囲気を感じていただければ幸いです。

※ 本記事では見やすさを優先させるためコマンドの出力結果などを一部省略しています。

対象読者

  • K8s や MAAS を運用しているインフラエンジニア
  • Cluster API の雰囲気を知りたい方

前提知識

  • カスタムリソースなどを含む K8s の知識

MAAS について

MAAS (Metal as a Service) は Canonical 社が開発しているベアメタル管理ツールです。 MAAS を使うことで、物理サーバの OS インストールや管理がクラウドリソースのように利用可能になります。

マイクロアドで MAAS を導入した経緯などはこちらをご覧ください。

developers.microad.co.jp

今回は、Cluster API を初めて触ることもあり、データセンタの MAAS 環境とは切り離して検証したかったので、 こちらの記事で作成したオフィス内サーバルームの MAAS 環境を使いました。

developers.microad.co.jp

Cluster API について

Cluster API とは K8s クラスタのプロビジョニング・アップグレード・運用を簡素化するための宣言的 API です。 Cluster API では、以下を含む情報をマニフェストに記載することで K8s クラスタを宣言的に管理します。

  • Pod CIDR, Service CIDR
  • K8s のバージョン
  • K8s のバージョンアップポリシー
  • Control-plane や Worker の台数
  • Control-plane や Worker に使われる VM/PM (Physical Machine) のスペック
  • etc...

Cluster API では、クラスタに関して 2 つのロールが登場します。

  • Workload cluster
    • Cluster API によってライフサイクルを管理されるクラスタ
  • Management cluster
    • Workload cluster を管理するクラスタ
    • このクラスタに、Cluster API の Operator (カスタムコントローラ + CRD) をインストールする

プロバイダについて

Cluster API は 3つのレイヤで構成されており、役割ごとに独立した実装がコミュニティやベンダーから「プロバイダ」として提供されています (プロバイダの実装はこちら)。

基本的に、Control plane provider と Bootstrap provider は同じものを使うので、Infrastructure provider と Bootstrap provider の組み合わせを選んでいきます。

  • MAAS × RKE2
  • MAAS × kubeadm
  • OpenStack × RKE2
  • OpenStack × kubeadm

Image Builder について

Cluster API は、MAAS や OpenStack といったプラットフォームから VM/PM を払い出して OS をインストールする所から K8s のライフサイクルを管理していくため、OS イメージが必要になります。 OS イメージは一部提供されていますが、現行の OS や K8s バージョンから古い場合があります1。 そのため Image Builder というツールを使ってビルドします。

例えば、MAAS 用のイメージを作る場合、こちらのマトリクスで MAAS に対応している OS を確認します。 2026年4月時点では Ubuntu 22.04 と 24.04 に対応していました。

Cluster API を使って MAAS 上に K8s クラスタを構築

※ 正しくは、MAAS の API を叩いて MAAS 管理下のサーバを使って K8s クラスタを構築しますが、便宜上「MAAS 上に構築」と記載しています。

実施する作業

  • Step 1: Image Builder で OS イメージを作る
  • Step 2: OS イメージを MAAS にアップロードする
  • Step 3: kind に Cluster API の Operator をインストールして Management cluster 化する
  • Step 4: Cluster API のリソースを作る
  • Step 5: Control-plane の作成 (Step 4 でリソースを作ると Control-plane が作られる)
  • Step 6: Worker の作成 (Step 5 完了後に自動で作られる)

Step 1. Image Builder で OS イメージをビルド

Image Builder の公式リポジトリをクローンして、必要なパッケージをインストールします。

git clone https://github.com/kubernetes-sigs/image-builder.git
git checkout v0.1.49

# qemu 関連のパッケージをインストール
sudo apt-get update
sudo apt-get install -y qemu-system-x86 qemu-utils python3.12-venv

# Ansible のインストール
python3 -m venv .venv
source .venv/bin/activate
pip install ansible==13.3.0

# KVM 関連の操作ができるようにグループに所属させる
sudo usermod -aG kvm $USER
newgrp kvm

Image Builder のクローンが完了したら、設定を変更していきます。 まずは、最新の K8s のパッチバージョンを確認して変更します。

# v1.33 の最新パッチバージョン
curl -s https://dl.k8s.io/release/stable-1.33.txt
# kubernetes_deb_version の確認
curl -Ls https://pkgs.k8s.io/core:/stable:/v1.33/deb/Packages | grep Version

# v1.34 の最新パッチバージョン
curl -s https://dl.k8s.io/release/stable-1.34.txt
# kubernetes_deb_version の確認
curl -Ls https://pkgs.k8s.io/core:/stable:/v1.34/deb/Packages | grep Version

パッチバージョンを確認したら、image-builder/images/capi/packer/config/kubernetes.json を変更します。

-  "crictl_version": "1.34.0",
+  "crictl_version": "1.33.0",
  ...
-  "kubernetes_deb_version": "1.34.3-1.1",
+  "kubernetes_deb_version": "1.33.10-1.1",
  ...
-  "kubernetes_rpm_version": "1.34.3",
-  "kubernetes_semver": "v1.34.3",
-  "kubernetes_series": "v1.34",
+  "kubernetes_rpm_version": "1.33.10",
+  "kubernetes_semver": "v1.33.10",
+  "kubernetes_series": "v1.33",
  ...

続いて、images/capi/packer/config を編集して、イメージに追加でインストールするパッケージや Ansible のタスクで使う環境変数などを定義します。 今回は、サーバが古く grub-pc をイメージにインストールしておかないと、OS インストール時に失敗したため、grub-pc を指定しています。 また、プロキシ環境なので Ansible のタスク内でプロキシを使うようにしています。

(BIOS サポート関連のエラー issue)

-  "extra_debs": "",
+  "extra_debs": "grub-pc",
...
-  "http_proxy": "",
-  "https_proxy": "",
+  "http_proxy": "http://192...:8000",
+  "https_proxy": "http://192...:8000",

変数を設定したら、OS イメージをビルドしていきます (参考)。

cd image-builder/images/capi
source .venv/bin/activate

# Ubuntu 24.04イメージビルド
make build-maas-ubuntu-2404-efi

しばらくすると、イメージが image-builder/images/capi/output/ 配下に作られます。

ls output/
# ubuntu-2404-efi-kube-v1.33.10

# 以降の手順で分かりやすいように /tmp 配下にコピーしておきます
cp ./output/ubuntu-2404-efi-kube-v1.33.10 /tmp/

Step 2. MAAS に OS イメージをアップロード

ビルドした OS イメージを MAAS にアップロードします。

VER_K8S=1.33
VER_K8S_PATCH=1.33.10

# MAAS にログイン
maas login admin http://192...:5240/MAAS/api/2.0 h...

# MAAS にアップロード
maas admin boot-resources create \
  name="custom/ubuntu-2404-efi-kube-v${VER_K8S_PATCH}" \
  title="Ubuntu 24.04 CAPI v${VER_K8S_PATCH}" \
  architecture='amd64/generic' \
  filetype='tgz' \
  content@=/tmp/ubuntu-2404-efi-kube-v${VER_K8S_PATCH}.tar.gz

MAAS コマンドでアップロードした OS イメージを確認します。

maas admin boot-resources read | \
  jq '.[] | select(.name == "ubuntu-2404-efi-kube-v1.33.10")'
# {
#   "id": 25,
#   "type": "Uploaded",
#   "name": "ubuntu-2404-efi-kube-v1.33.10",
#   "architecture": "amd64/generic",
#   "resource_uri": "/MAAS/api/2.0/boot-resources/25/",
#   "last_deployed": null,
#   "title": "Ubuntu 24.04 CAPI v1.33.10",
#   "subarches": "generic",
#   "base_image": "ubuntu/jammy"
# }

Step 3. kind で Management Cluster の作成

事前準備として、MAAS の Machine にタグをつけます。 タグが一致した Machine を Cluster API で使うようにします。

# タグの作成
maas admin tags create name=control-plane
maas admin tags create name=worker

# Machine 一覧を取得
maas admin machines read | jq '[.[] | {hostname, system_id}] | sort_by(.hostname)'
# [
#   {
#     "hostname": "1...",
#     "system_id": "f..."
#   },
#   {
#     "hostname": "1...",
#     "system_id": "k..."
# ...
# ]

# Worker
maas admin tag update-nodes worker        add=k...
maas admin tag update-nodes worker        add=p...
maas admin tag update-nodes worker        add=r...

# Control-plane
maas admin tag update-nodes control-plane add=8...
maas admin tag update-nodes control-plane add=4...

UI から見るとこのようにタグが付与されています。

タグ付けができたら、kind クラスタを構築します。

# kind の構成ファイル (C-plane 1台, Worker 1台)
cat << EOF > config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
EOF

# kind クラスタを構築
kind create cluster --config ./config.yaml

# kind のコンテキストを ~/.kube/config.kind に保存します
cp ~/.kube/config ~/.kube/config.kind

続いて、clusterctl コマンドを使って Management cluster を作っていきます。 clusterctl は Management cluster のライフサイクルを管理する CLI ツールになります2。 clusterctl が読み込む設定ファイルは、デフォルトで ~/.cluster-api/clusterctl.yaml になりますが、実行時に引数でパスを指定することもできます3

~/.cluster-api/clusterctl_133.yaml を作ります。

MAAS_API_KEY: h...
MAAS_ENDPOINT: http://192...:5240/MAAS
MAAS_DNS_DOMAIN: maas

# Cluster configuration
KUBERNETES_VERSION: v1.33.10
CONTROL_PLANE_MACHINE_IMAGE: custom/ubuntu-2404-efi-kube-v1.33.10
CONTROL_PLANE_MACHINE_MINCPU: 4
CONTROL_PLANE_MACHINE_MINMEMORY: 8192
CONTROL_PLANE_MACHINE_RESOURCEPOOL: default
CONTROL_PLANE_MACHINE_TAG: control-plane

WORKER_MACHINE_IMAGE: custom/ubuntu-2404-efi-kube-v1.33.10
WORKER_MACHINE_MINCPU: 4
WORKER_MACHINE_MINMEMORY: 8192
WORKER_MACHINE_RESOURCEPOOL: default
WORKER_MACHINE_TAG: worker

# カスタム変数
POD_CIDR: 172.18.0.0/17
SERVICE_CIDR: 10.43.0.0/16
KUBERNETES_VERSION_HYPHEN: v1-33-10

clusterctl CLI をインストールします4

curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.12.3/clusterctl-linux-amd64 -o clusterctl
sudo install ./clusterctl /usr/local/bin/clusterctl

~/.cluster-api/clusterctl_133.yaml を指定して、Cluster API の Operator を kind にインストールします。 これにより、kind は Management cluster になります。

clusterctl init \
  --core cluster-api:v1.12.3 \
  --infrastructure maas:v0.6.1 \
  --config ${HOME}/.cluster-api/clusterctl_133.yaml

# 非推奨になった gcr.io/kubebuilder/kube-rbac-proxy が埋め込まれているので置き換える
kubectl get po -n capmaas-system
# NAME                               READY   STATUS             RESTARTS   AGE
# capmaas-controller-manager-7bc..   1/2     ImagePullBackOff   0          26s
kubectl set image -n capmaas-system deployment/capmaas-controller-manager \
  kube-rbac-proxy=quay.io/brancz/kube-rbac-proxy:v0.15.0

Step 4. Cluster API のリソース作成

まず、Cluster API のマニフェストを生成するためのテンプレートを作ります。 2026年4月時点のMAAS Provider が提供しているテンプレートを見ると、Pod CIDR などの設定がハードコードされており、このままではクラスタの作成に失敗します。 そのため、自前で cluster-template.yaml というテンプレートを作って、先ほど作った ~/.cluster-api/clusterctl_133.yaml のカスタム変数 (POD_CIDR など) を埋め込みます。

cluster-template.yaml

---

apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: "${CLUSTER_NAME}"
spec:
  clusterNetwork:
    services:
      cidrBlocks: ["${SERVICE_CIDR}"]
    pods:
      cidrBlocks: ["${POD_CIDR}"]
    serviceDomain: "cluster.local"
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: MaasCluster
    name: "${CLUSTER_NAME}"
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: "${CLUSTER_NAME}-control-plane"

---

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: MaasCluster
metadata:
  name: "${CLUSTER_NAME}"
spec:
  dnsDomain: ${MAAS_DNS_DOMAIN}

---

apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: "${CLUSTER_NAME}-control-plane"
spec:
  replicas: ${CONTROL_PLANE_MACHINE_COUNT}
  version: "${KUBERNETES_VERSION}"
  machineTemplate:
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: MaasMachineTemplate
      name: "${CLUSTER_NAME}-control-plane-${KUBERNETES_VERSION_HYPHEN}"
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        certSANs:
          - 127.0.0.1
        extraArgs:
          anonymous-auth: "true"
          authorization-mode: RBAC,Node
          default-not-ready-toleration-seconds: "60"
          default-unreachable-toleration-seconds: "60"
          disable-admission-plugins: AlwaysAdmit
          enable-admission-plugins: AlwaysPullImages,NamespaceLifecycle,ServiceAccount,NodeRestriction
        timeoutForControlPlane: 10m0s
      controllerManager:
        extraArgs:
          feature-gates: RotateKubeletServerCertificate=true
          terminated-pod-gc-threshold: "25"
          use-service-account-credentials: "true"
      dns: {}
      etcd: {}
      networking: {}
      scheduler:
        extraArgs:
    # 1台目の control-plane
    initConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          event-qps: "0"
          feature-gates: RotateKubeletServerCertificate=true
          read-only-port: "0"
        name: '{{ v1.local_hostname }}'
    # 2台目以降の control-plane
    joinConfiguration:
      controlPlane:
      discovery: {}
      nodeRegistration:
        kubeletExtraArgs:
          event-qps: "0"
          feature-gates: RotateKubeletServerCertificate=true
          read-only-port: "0"
        name: '{{ v1.local_hostname }}'
    files:
      - path: /etc/environment
        content: |
          http_proxy=http://...
          https_proxy=http://...
          no_proxy=192...
    preKubeadmCommands:
      - export http_proxy=http://...
      - export https_proxy=http://...
      - export no_proxy=192...
      - mkdir -p /etc/systemd/system/containerd.service.d
      - |
        cat <<EOF > /etc/systemd/system/containerd.service.d/http-proxy.conf
        [Service]
        Environment="HTTP_PROXY=http://...
        Environment="HTTPS_PROXY=http://...
        Environment="NO_PROXY=192...
        EOF
      - systemctl daemon-reload
      - systemctl restart containerd
      - while [ ! -S /var/run/containerd/containerd.sock ]; do echo 'Waiting for containerd...';
        sleep 1; done
      - sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab
      - swapoff -a
    useExperimentalRetryJoin: true

---

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: MaasMachineTemplate
metadata:
  name: "${CLUSTER_NAME}-control-plane-${KUBERNETES_VERSION_HYPHEN}"
spec:
  template:
    spec:
      minCPU: ${CONTROL_PLANE_MACHINE_MINCPU}
      minMemory: ${CONTROL_PLANE_MACHINE_MINMEMORY}
      image: ${CONTROL_PLANE_MACHINE_IMAGE}
      resourcePool: ${CONTROL_PLANE_MACHINE_RESOURCEPOOL}
      tags: [ ${ CONTROL_PLANE_MACHINE_TAG } ]

---

apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: "${CLUSTER_NAME}-md-0"
spec:
  clusterName: "${CLUSTER_NAME}"
  replicas: 1
  selector:
    matchLabels:
      cluster.x-k8s.io/cluster-name: "${CLUSTER_NAME}"
  # Pod の template と同じ思想
  template:
    spec:
      clusterName: "${CLUSTER_NAME}"
      version: "${KUBERNETES_VERSION}"
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: MaasMachineTemplate
        name: "${CLUSTER_NAME}-md-0-${KUBERNETES_VERSION_HYPHEN}"
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: "${CLUSTER_NAME}-md-0"

---

apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: MaasMachineTemplate
metadata:
  name: "${CLUSTER_NAME}-md-0-${KUBERNETES_VERSION_HYPHEN}"
spec:
  template:
    spec:
      minCPU: ${WORKER_MACHINE_MINCPU}
      minMemory: ${WORKER_MACHINE_MINMEMORY}
      image: ${WORKER_MACHINE_IMAGE}
      resourcePool: ${WORKER_MACHINE_RESOURCEPOOL}
      tags: [ ${ WORKER_MACHINE_TAG } ]

---

apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
  name: "${CLUSTER_NAME}-md-0"
spec:
  template:
    spec:
      joinConfiguration:
        nodeRegistration:
          kubeletExtraArgs:
            event-qps: "0"
            feature-gates: RotateKubeletServerCertificate=true
            read-only-port: "0"
          name: '{{ v1.local_hostname }}'
      files:
        - content: |
            http_proxy=http://...
            https_proxy=http://...
            no_proxy=192...
          path: /etc/environment
      preKubeadmCommands:
        - export http_proxy=http://...
        - export https_proxy=http://...
        - export no_proxy=192...
        - mkdir -p /etc/systemd/system/containerd.service.d
        - |
          cat <<EOF > /etc/systemd/system/containerd.service.d/http-proxy.conf
          [Service]
          Environment="HTTP_PROXY=http://...
          Environment="HTTPS_PROXY=http://...
          Environment="NO_PROXY=192...
          EOF
        - systemctl daemon-reload
        - systemctl restart containerd
        - while [ ! -S /var/run/containerd/containerd.sock ]; do echo 'Waiting for containerd...';
          sleep 1; done
        - sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab
        - swapoff -a
      useExperimentalRetryJoin: true

テンプレートができたら、Cluster API のマニフェストを生成します。

VER_K8S_PATCH=1.33.10
CLUSTER=t-cluster

clusterctl generate cluster ${CLUSTER} \
  --config ${HOME}/.cluster-api/clusterctl_133.yaml \
  --target-namespace capmaas-system \
  --kubernetes-version v${VER_K8S_PATCH} \
  --control-plane-machine-count=1 \
  --worker-machine-count=2 \
  --from ./cluster-template.yaml \
  --write-to ./${CLUSTER}_${VER_K8S_PATCH}.yaml

生成したマニフェストを apply して Cluster API のリソースを kind 上に作ります。

kubectl apply -f ./${CLUSTER}_${VER_K8S_PATCH}.yaml --kubeconfig ~/.kube/config.kind

Step 5, 6. Control-plane と Worker の作成

Management cluster に Cluster API のリソースを作ると、MAAS の API を叩いて OS をデプロイしてくれます。 OS デプロイが完了すると kubeadm で K8s を bootstrap します。

まず、Control-plane が作られます。

kubectl get machine -n capmaas-system
# NAME                       READY   AVAILABLE  UP-TO-DATE  PHASE          VERSION
# t-cluster-control-plane..  False   False      True        Provisioning   v1.33.10
# t-cluster-md-0-n6sg6-x8..  False   False      True        Pending        v1.33.10

Control-plane の構築が完了すると Worker が作られます。

kubectl get machine -n capmaas-system
# NAME                       READY   AVAILABLE   UP-TO-DATE  PHASE         VERSION
# t-cluster-control-plane..  False   False       True        Running       v1.33.10
# t-cluster-md-0-n6sg6-x8..  False   False       True        Provisioning  v1.33.10

Worker の構築が完了。

kubectl get machine -n capmaas-system
# NAME                       READY   AVAILABLE  UP-TO-DATE  PHASE    VERSION
# t-cluster-control-plane..  False   False      True        Running  v1.33.10
# t-cluster-md-0-n6sg6-x8..  False   False      True        Running  v1.33.10

Workload Cluster の kubeconfig を取得して、~/.kube/config.workload に保存します。

clusterctl get kubeconfig t-cluster -n capmaas-system > ~/.kube/config.workload
kubectl get po -A --kubeconfig ~/.kube/config.workload
# NAMESPACE     NAME                                  READY   STATUS 
# kube-system   coredns-674b8bbfcf-f7g7b              0/1     Pending
# kube-system   coredns-674b8bbfcf-pnfnq              0/1     Pending
# kube-system   etcd-1407-016-06                      1/1     Running
# kube-system   kube-apiserver-1407-016-06            1/1     Running
# kube-system   kube-controller-manager-1407-016-06   1/1     Running
# kube-system   kube-proxy-jqhsg                      1/1     Running
# kube-system   kube-proxy-kjjgg                      1/1     Running
# kube-system   kube-scheduler-1407-016-06            1/1     Running

構築後の設定

本記事では省略しますが、Cluster API を使わない場合と同様で CNI をインストールする必要があります。 CNI インストール後に cluster リソースを確認すると AVAILABLETrue になっており、これにて Workload cluster の構築が完了になります!

# ※ 見やすいように分割して表示しています
kubectl get cluster -n capmaas-system --kubeconfig ~/.kube/config.kind
# NAME       AVAILABLE
# t-cluster  True     

# CP DESIRED  CP AVAILABLE  CP UP-TO-DATE
# 1           1             1            

# W DESIRED  W AVAILABLE  W UP-TO-DATE
# 1          1            1           

# PHASE
# Provisioned

Pivot の手順 (セルフホスト化)

ここまでで、kind を使って Workload cluster のデプロイを実施しましたが、今後も kind を使って管理していくわけにはいかないので、 今作成した Workload cluster を新たに Management cluster にさせます。 Management cluster の移行には Pivot という方法が用意されています。

まず、Workload Cluster に CAPI をインストールします。

clusterctl init \
  --config ${HOME}/.cluster-api/clusterctl_133.yaml \
  --kubeconfig ~/.kube/config.workload \
  --core cluster-api:v1.12.3 \
  --bootstrap kubeadm \
  --control-plane kubeadm \
  --infrastructure maas:v0.6.1

# 非推奨になった gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 が埋め込まれているので置き換える
kubectl --kubeconfig ~/.kube/config.workload set image -n capmaas-system deployment/capmaas-controller-manager \
  kube-rbac-proxy=quay.io/brancz/kube-rbac-proxy:v0.15.0

続いて、clusterctl move コマンドを使って kind (現 Management cluster) から Workload cluster へオブジェクトを移動します。

clusterctl move \
  --kubeconfig ~/.kube/config.kind \
  --to-kubeconfig ~/.kube/config.workload \
  --namespace capmaas-system

# Performing move...
# Discovering Cluster API objects
# Moving Cluster API objects Clusters=1
# Moving Cluster API objects ClusterClasses=0
# Waiting for all resources to be ready to move
# Creating objects in the target cluster
# Deleting objects from the source cluster

Management cluster 兼 Workload cluster になるため、ライフサイクルを自分自身で管理できるようになります。 Cluster API のリソースが見えるか確認します。 セルフホスト化したので自分のリソースが見えています。

kubectl get machine --kubeconfig ~/.kube/config.workload -n capmaas-system
# NAME                        READY   AVAILABLE  UP-TO-DATE  PHASE    VERSION
# t-cluster-control-plane-..  True    True       True        Running  v1.33.10
# t-cluster-md-0-n6sg6-x82..  True    True       True        Running  v1.33.10

# ※ 見やすいように分割して表示しています
kubectl get cluster --kubeconfig ~/.kube/config.workload -n capmaas-system
# NAME       AVAILABLE
# t-cluster  True     

# CP DESIRED  CP AVAILABLE  CP UP-TO-DATE
# 1           1             1            

# W DESIRED  W AVAILABLE  W UP-TO-DATE
# 1          1            1           

# PHASE
# Provisioned

Kubernetes バージョンアップ

※ 以下の手順は v1.34 のイメージを、Image builder でビルドして MAAS にアップロードされていることが前提になります。

マニフェスト生成

~/.cluster-api/clusterctl_134.yaml を作ります。 ~/.cluster-api/clusterctl_133.yaml との差分はこのようになります。

-KUBERNETES_VERSION: v1.33.10
+KUBERNETES_VERSION: v1.34.3

-CONTROL_PLANE_MACHINE_IMAGE: custom/ubuntu-2404-efi-kube-v1.33.10
+CONTROL_PLANE_MACHINE_IMAGE: custom/ubuntu-2404-efi-kube-v1.34.3

-WORKER_MACHINE_IMAGE: custom/ubuntu-2404-efi-kube-v1.33.10
+WORKER_MACHINE_IMAGE: custom/ubuntu-2404-efi-kube-v1.34.3

-KUBERNETES_VERSION_HYPHEN: v1-33-10
+KUBERNETES_VERSION_HYPHEN: v1-34-3

テンプレートから Cluster API のマニフェストを生成します。

VER_K8S_PATCH=1.34.3
CLUSTER=t-cluster

clusterctl generate cluster ${CLUSTER} \
  --config ${HOME}/.cluster-api/clusterctl_134.yaml \
  --target-namespace capmaas-system \
  --kubernetes-version v${VER_K8S_PATCH} \
  --control-plane-machine-count=1 \
  --worker-machine-count=2 \
  --from ./cluster-template.yaml \
  --write-to ./${CLUSTER}_${VER_K8S_PATCH}.yaml

v1.33 との差分は以下のようになります。

diff t-cluster_1.33.10.yaml t-cluster_1.34.3.yaml  -u
--- t-cluster_1.33.10.yaml
+++ t-cluster_1.34.3.yaml

@@ -104,19 +104,19 @@
 kind: KubeadmControlPlane
 metadata:
   name: t-cluster-control-plane
 spec:
-      name: t-cluster-control-plane-v1-33-10
+      name: t-cluster-control-plane-v1-34-3
-  version: v1.33.10
+  version: v1.34.3
 ---
 apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
 kind: MaasMachineTemplate
 metadata:
-  name: t-cluster-control-plane-v1-33-10
+  name: t-cluster-control-plane-v1-34-3
 spec:
   template:
     spec:
-      image: custom/ubuntu-2404-efi-kube-v1.33.10
+      image: custom/ubuntu-2404-efi-kube-v1.34.3

@@ -145,18 +145,18 @@
 kind: MachineDeployment
 metadata:
   name: t-cluster-md-0
 spec:
-        name: t-cluster-md-0-v1-33-10
-      version: v1.33.10
+        name: t-cluster-md-0-v1-34-3
+      version: v1.34.3
 ---
 apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
 kind: MaasMachineTemplate
 metadata:
-  name: t-cluster-md-0-v1-33-10
+  name: t-cluster-md-0-v1-34-3
 spec:
   template:
     spec:
-      image: custom/ubuntu-2404-efi-kube-v1.33.10
+      image: custom/ubuntu-2404-efi-kube-v1.34.3

バージョンアップ実施

v1.34 のマニフェストを適用します。

VER_K8S_PATCH=1.34.3
CLUSTER=t-cluster

kubectl apply -f ./${CLUSTER}_${VER_K8S_PATCH}.yaml

まず、Control-plane が作られます。

kubectl get machine -n capmaas-system
# NAME                       READY  AVAILABLE  UP-TO-DATE  PHASE         VERSION
# t-cluster-control-plane..  False  False      True        Provisioning  v1.34.3
# t-cluster-control-plane..  True   True       False       Running       v1.33.10
# t-cluster-md-0-lrs27-z2..  True   True       False       Running       v1.33.10

# Cilium の Pod が作られる (↓様子の例)
# cilium-d6qxz          0/1     Pending          
# cilium-envoy-5mwhr    0/1     Pending          
# cilium-d6qxz          0/1     Init:0/6         
# cilium-envoy-5mwhr    0/1     ContainerCreating
# cilium-d6qxz          0/1     Init:0/6         
# cilium-d6qxz          0/1     Init:1/6         

新しい Control-plane の準備ができると古い Control-plane が削除されます。

kubectl get machine -n capmaas-system
# NAME                       READY  AVAILABLE  UP-TO-DATE  PHASE     VERSION
# t-cluster-control-plane..  True   True       True        Running   v1.34.3
# t-cluster-control-plane..  False  False      False       Deleting  v1.33.10
# t-cluster-md-0-lrs27-z2..  True   True       False       Running   v1.33.10

古い Control-plane が削除されると新しい Worker が作られます。

kubectl get machine -n capmaas-system
# NAME                       READY  AVAILABLE  UP-TO-DATE  PHASE         VERSION
# t-cluster-control-plane..  True   True       True        Running       v1.34.3
# t-cluster-md-0-h4jd7-5l..  False  False      True        Provisioning  v1.34.3
# t-cluster-md-0-lrs27-z2..  True   True       False       Running       v1.33.10

新しい Worker の準備ができると古い Worker が削除され、最終的に新しいサーバのみになります。

kubectl get machine -n capmaas-system
# NAME                       READY  AVAILABLE  UP-TO-DATE  PHASE    VERSION
# t-cluster-control-plane..  True   True       True        Running  v1.34.3
# t-cluster-md-0-h4jd7-5l..  True   True       True        Running  v1.34.3

おわりに

今回は、Cluster API を使って MAAS 上で K8s のライフサイクルを管理する方法について紹介しました。

検証して分かった課題を以下にまとめます。

  • Control-plane のエンドポイントを提供するためのプロキシ (HAProxyなど) は別で作らないといけないが、バージョンアップやノード障害時のセルフヒーリングのたびにバックエンドが変わるので、外部プロキシとの連携が必要になる
    • OpenStack provider を使えば、Octavia と連携していい感じに control-plane 用のプロキシを設定してくれるっぽい?5
  • バージョンアップは、OS インストールから実施するので Rancher と比較して時間がかかる & 余分にサーバが必要

MAAS プロバイダよりも OpenStack プロバイダの方が活発に開発されているため、今後は OpenStack プロバイダの検証や Rancher 環境で Cluster API を上手に使っていく方法を模索していきたいと考えています。

新卒インフラエンジニア絶賛採用中

マイクロアドでは技術への探究心があり、最新の技術・動向について積極的に学び活かす意欲を持った仲間を募集しています! またインフラエンジニアだけでなく、サーバサイド、機械学習エンジニアなど幅広く募集しています! 気になった方は以下からご応募ください!

recruit.microad.co.jp