👥

Cluster API operator で宣言的に k8s クラスタを作成する

2024/11/19に公開

概要

Cluster API は様々なクラウド上に k8s クラスタを展開・管理するための k8s サブプロジェクトです。cluster API 固有のカスタムリソースを使うことで以下のようなリソースを管理できます。

  • クラスタ用のノードやスケーリング
  • クラスタが稼働するネットワーク、ロードバランサー等のインフラ
  • ノードの bootstrap 処理

カスタムリソースの管理がそのまま上記のリソース管理につながるため、インフラも含めてクラスタのライフサイクルをより柔軟に管理できるようになっています。また、provider と呼ばれるカスタムリソースによって複数のクラウド上におけるクラスタを一元的に管理できるようなメリットもあります。

前に書いた Flatcar Container Linux を試してみる の記事では cluster API を使って flatcar container linux ノードからなるクラスタを openstack 上に作成しました。このときは clusterctl を使って主に CLI からリソース作成を実行しましたが、cluster API では各種リソースのマニフェストや cluster API operator を使って宣言的にリソースを定義、管理することも可能なので、今回はそちらの内容をメインに取り扱います。

aws 上にクラスタを作成する

ここでは cluster API のマニフェストを利用した宣言的なアプローチで実際に aws 上にクラスタを作成していきます。単に作成するだけなら Cluster API の quickstart に沿って clusterctl を使って進めていくのが楽ですが、理解を深めるために Cluster API operator を使いつつクラスタの作成に必要なリソースを確認しながら作業していきます。

事前準備

管理クラスタの準備

cluster API を使っていずれかのクラウド上にクラスタを作成するには cluster API の実行基盤となる k8s クラスタが必要となるので、管理クラスタ用の k8s クラスタを準備しておきます。

CLI インストール

cluster API では CLI から容易にクラスタを作成するためのユーティリティツール clusterctl が提供されています。宣言的に作成する場合は必ずしも必要ではないですが、あると便利なので以下でインストールしておきます。

curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.8.5/clusterctl-linux-amd64 -o clusterctl
chmod +x clusterctl
sudo mv clusterctl /usr/local/bin

また、aws 上にクラスタを展開するためには cluster API カスタムリソースの aws infrastructure provider を利用します。この際に aws での IAM 認証情報の管理を補助する clusterawsadm が提供されているのでこちらもインストールしておきます。

curl -L https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/download/v2.7.1/clusterawsadm-linux-amd64 -o clusterawsadm
chmod +x clusterawsadm
sudo mv clusterawsadm /usr/local/bin

IAM ロールの作成

aws infrastructure provider で AWS クラスタを作成する際はノードに IAM ロール(インスタンスプロファイル)を設定することで適切なアクセス権限を設定するようになっています。デフォルトで利用されるロールやポリシーは clusterawsadmbootstrap iam create-cloudformation-stack コマンドで作成できます。

# The clusterawsadm utility takes the credentials that you set as environment
# variables and uses them to create a CloudFormation stack in your AWS account
# with the correct IAM resources.

$ clusterawsadm bootstrap iam create-cloudformation-stack

これを実行すると aws 側に cloudformation スタックが作成され、その中でデフォルトの IAM ロールやロールが作成されます。ここで作成したロールは後ほどクラスタの control plane, worker 用のノードで利用されます。

Cert manager のインストール

cluster API では証明書管理に cert manager を利用しますが、これ自体は cluster API のインストール過程には含まれていないので helm で別途インストールしておきます。

helm repo add jetstack https://charts.jetstack.io --force-update
helm repo update
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set installCRDs=true

Cluster API operator のインストール

はじめにまっさらな状態の k8s クラスタに cluster API operator をインストールします。

helm repo add capi-operator https://kubernetes-sigs.github.io/cluster-api-operator
helm install capi-operator capi-operator/cluster-api-operator --create-namespace -n capi-operator-system

これにより provider リソースを監視するための operator pod が作成されます。

$ k get pod
capi-operator-system                capi-operator-cluster-api-operator-85bbb77577-kxkwr              1/1     Running   0          2d2h

cluster API operator は k8s でよくある operator で、cluster API の provider リソースのライフサイクルを管理するコンポーネントになっています。cluster API では コンセプト にもあるように以下 4 つの provider でクラスタ関連のカスタムリソースを管理します。

  • Core Provider (cluster API 本体)
  • Bootstrap Provider
  • Infrastructure Provider
  • ControlPlane Provider

(上記にはないですが Addon Provider (helm) も使えます)

cluster API operator では上記の provider を operator pattern で取り扱い各 provider の作成やアップグレードを容易に行えるようにする立ち位置になっています。

provider のインストール

次に管理クラスタを構成する各 provider をインストールしていきます。

Core Provider

core provider は cluster API の CRD をインストールして、カスタムリソースに応じてリソースのライフサイクル管理を行う cluster api のコアな部分の役割があります。

apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: CoreProvider
metadata:
  name: cluster-api
  namespace: capi-system
spec:
  version: v1.4.3

上記のようなマニフェストでリソースを作成することにより、cluster API operator がこのリソース作成を検知して cluster API リソースを管理する controller manager pod が起動します。

$ k get pod
capi-system                         capi-controller-manager-647769958c-xrsfg                         1/1     Running   0          26h

Bootstrap Provider

Bootstrap Provider ではクラスタ証明書の作成や control plane の bootstrap 等の処理を管理します。kubeadm init のような bootstrap 処理に対応。
cluster API における provider はあくまでインターフェースのようなものであり、provider の実装はそれぞれの機能に特化したいろいろなプロジェクトのツールを利用できるようになっています。
例えば BootstrapProvider の実装では事前定義済みの以下のツールが利用できます。

  • Amazon Elastic Kubernetes Service (EKS)
  • Kubeadm
  • MicroK8s
  • Oracle Cloud Native Environment (OCNE)
  • Talos
  • K3s
  • k0smotron/k0s

各 provider がサポートしている実装の一覧は https://cluster-api.sigs.k8s.io/reference/providers を参照。
ここでは BootstrapProvider に kubeadm を使います。マニフェストは以下。

apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: BootstrapProvider
metadata:
  name: kubeadm
  namespace: capi-kubeadm-bootstrap-system
spec:
  version: v1.8.5

内部的には metadata.nameここ にある事前定義済み provider のリストに一致する場合は指定した provider が自動で選択され、それ以外の場合にはカスタム provider としてインストール処理が行われるようです。kubeadm は bootstrapprovider のリストにあるので自動で検出されてインストールされます。
インストールが完了すると kubeadm bootstrap provider の controller pod が起動し、この中で bootstrapprovider 関連の CRD のインストールが実行されます。

NAME                                                         READY   STATUS    RESTARTS   AGE
capi-kubeadm-bootstrap-controller-manager-6c46687b5d-7rth5   1/1     Running   0          4h32m

Controlplane Provider

control plane provider ではクラウド上に展開したクラスタの control plane の管理(ノードの追加やクラスタの kubernetes バージョンのアップグレードなど)を担当します。
こちらも bootstrapprovider と同じ kubeadm を使用します。

apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: ControlPlaneProvider
metadata:
 name: kubeadm
 namespace: capi-kubeadm-control-plane-system
spec:
 version: v1.4.3

Infrastructure Provider

Infrastructure Provider はクラスタ用のノードやインフラストラクチャを管理するコンポーネントとなっています。各クラウド上のノード、ネットワーク、セキュリティグループ、ロードバランサーなど様々なリソースのライフサイクルを管理するため、こちらも cluster API におけるコアな基盤のひとつになっています。InfrastructureProvider は各種クラウドに対応するので provider の中でも定義済み provider が最も多く、現時点で以下のクラウドがサポートされています。

リスト
  • Akamai (Linode)
  • AWS
  • Azure
  • Azure Stack HCI
  • Bring Your Own Host (BYOH)
  • CloudStack
  • CoxEdge
  • DigitalOcean
  • Equinix Metal (formerly Packet)
  • Google Cloud Platform (GCP)
  • Hetzner
  • Hivelocity
  • IBM Cloud
  • IONOS Cloud
  • KubeKey
  • KubeVirt
  • MAAS
  • Metal3
  • Microvm
  • Nested
  • Nutanix
  • Oracle Cloud Infrastructure (OCI)
  • OpenStack
  • Outscale
  • Proxmox
  • Sidero
  • Tinkerbell
  • vcluster
  • Virtink
  • VMware Cloud Director
  • vSphere
  • Vultr
  • k0smotron RemoteMachine (SSH)

今回は aws 上にクラスタを展開するので AWS infrastructure provider を使用します。

aws infrastructure provider では aws 関連のカスタムリソースが変更された際に aws の API を実行してリソースを管理するため、API を実行する IAM 認証情報が必要になります。
なので予め aws 側で IAM ユーザー等を作成し、その認証情報(aws_access_key_id, aws_secret_access_key) を以下のコマンドで base64 encode して AWS_B64ENCODED_CREDENTIALS に代入します。

export AWS_REGION=ap-northeast-1
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=yyy
export AWS_B64ENCODED_CREDENTIALS=$(clusterawsadm bootstrap credentials encode-as-profile)

上記は cluster API のクイックスタートで記載されている方法ですが、clusterawsadm bootstrap credentials は認証情報を base64 encode しているだけなのでファイルに保存して base64 コマンドで encode する方法でも代用できます。

credential
aws_access_key_id = xxx
aws_secret_access_key = xxx
region = ap-northeast-1
cat credential | base64 | tr -d '\n'

この認証情報を secret に保存します。

apiVersion: v1
kind: Secret
metadata:
  name: aws-variables
  namespace: capa-system
type: Opaque
data:
  AWS_B64ENCODED_CREDENTIALS: ...

InfrastructureProvider では configSecret に上記で作成した secret を指定します。

---
apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: InfrastructureProvider
metadata:
  name: aws
  namespace: capa-system
spec:
  version: v2.1.4
  configSecret:
    name: aws-variables
  deployment:
    containers:
     - name: manager
       args:
      # These are controller flags that are specific to a provider; usage
      # is reserved for advanced scenarios only.
        "--awscluster-concurrency": "12"
        "--awsmachine-concurrency": "11"

上記マニフェストを適用することで aws リソースを管理するための aws controller manager pod が起動します。

$ k get pod
capa-system                         capa-controller-manager-55f847998b-jwnxn                         1/1     Running   0          17h

Addon Provider

Addon Provider は k8s の addon を管理する provider で、現時点では addon provider helm のみが定義済みの AddonProvider になっています。
helm provider は文字通り展開先のクラスタ上で helm chart をインストールしたり削除する機能を持ちます。これにより helm chart でインストール可能なツールの展開や削除・バージョンアップなどの作業を自動化できます。

helm provider は以下のマニフェストでインストールできます(ちなみに capph は cluster api add-on provider helm の頭文字らしい)。

apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: AddonProvider
metadata:
  name: helm
  namespace: caaph-system
spec:
  version: v0.2.6

以上で今回の検証で使用する provider の準備ができて管理クラスタが完成しました。この時点で各 namespace 内に provider に対応する controller manager pod が作成され、cluster API 関連のリソースの作成に応じてクラスタの展開などを行います。

NAMESPACE                           NAME                                                             READY   STATUS    RESTARTS   AGE
caaph-system                        caaph-controller-manager-56869c9964-tzjr9                        1/1     Running   0          5h22m
capa-system                         capa-controller-manager-55f847998b-7cp5w                         1/1     Running   0          5h22m
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-controller-manager-6c46687b5d-7rth5       1/1     Running   0          5h22m
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-controller-manager-6cd976b78c-cp4fp   1/1     Running   0          5h22m
capi-operator-system                capi-operator-cluster-api-operator-85bbb77577-kxkwr              1/1     Running   0          5h57m
capi-system                         capi-controller-manager-c54fb98f4-ghj4m                          1/1     Running   0          5h23m

クラスタ control plane の作成

cluster API の管理クラスタのセットアップが完了したので、次にワークロードクラスタを作成するためのリソースを作っていきます。
クイックスタートでは clusterctl generate cluster コマンドでクラスタ作成用のマニフェストがいい感じに作成されるのでそのまま kubectl apply することで手早くクラスタを作成できるのですが、ここではマニフェスト内で定義されているリソースを確認しながらクラスタを作っていきます。

cluster API では k8s クラスタを表現するためのリソースは kind: Cluster となっています。

cluster の定義例
kind: Cluster
apiVersion: cluster.x-k8s.io/v1beta1
metadata:
  name: my-cluster
  namespace: capa-system
spec:
  infrastructureRef:
    kind: AWSCluster
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
    name: my-cluster-aws
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: aws-control-plane

cluster のプロパティの定義は kubernetes-sigs/cluster-api@v1.8.5 にありますが、上記のように infrastructureRefcontrolPlaneRef が定義されていればひとまず動くものは作れます。
infrastructureRef でどのクラウド上でクラスタを作成するかといったインフラに関する情報を表し、具体的なプロパティはクラウド毎に固有の別リソースで定義します。aws の場合は AWSCluster が対応。
controlPlaneRef ではクラスタの control plane に関する情報を記述します。こちらも具体的な設定は KubeadmControlPlane リソースに書きます。

AWS Cluster

AWSCluster リソースは aws 上でクラスタを作成するための環境を定義する aws provider 固有のカスタムリソースとなっています。
AWSCluster では VPC やサブネット、セキュリティグループなどクラスタを動かすために必要となるインフラを自動で作成してくれるようになっていますが、リソース定義の spec でいろいろな項目をカスタマイズできるようになっています。https://cluster-api-aws.sigs.k8s.io/topics/bring-your-own-aws-infrastructure ではいろいろなカスタマイズ例が記載されています。

デフォルトの設定では aws 上に以下のリソースが自動で作成されます。

  • vpc x1
  • パブリックサブネット x3
  • プライベートサブネット x3
  • インターネットゲートウェイ
  • NAT gateway x3
  • ルートテーブル x6
  • セキュリティグループ x4
    • LB 用
    • control plane 用
    • ノード用
    • api server 用
  • Classic Load Balancer (CLB)

この設定をそのまま使ってもいいですが、今回は以下のカスタマイズを行います。

  • region: ap-northeast-1
    • リソースを作成するリージョンを指定
  • network.vpc.availabilityZoneUsageLimit: 2
    • サブネット数を 2 にする。ロードバランサーを使うので最小が 2
  • sshKeyName: [ssh-keyname]
    • ノードに ssh する場合は ssh keypair 名を指定。key pair は事前に作成しておく。
  • controlPlaneLoadBalancer.loadBalancerType: nlb
    • CLB は既に非推奨なので NLB を使うように切り替え。

マニフェストは以下のようになります。

kind: AWSCluster
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
metadata:
  name: my-cluster-aws
  namespace: capa-system
spec:
  region: ap-northeast-1
  sshKeyName: (ssh-keyname)
  controlPlaneLoadBalancer:
    loadBalancerType: nlb
  network:
    vpc:
      availabilityZoneSelection: Ordered
      availabilityZoneUsageLimit: 2

KubeadmControlPlane

KubeadmControlPlane リソースは kubeadm controlplane provider 固有のリソースでクラスタの control plane を kubeadm で管理するための設定を記述します。
これに関してはあまり詳細な設定が見つかりませんでしたが、クイックスタートで作成されるマニフェストを見てみると以下のような形式になっています。

apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: aws-control-plane
  namespace: capa-system
spec:
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        extraArgs:
          cloud-provider: external
      controllerManager:
        extraArgs:
          cloud-provider: external
    initConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          cloud-provider: external
        name: '{{ ds.meta_data.local_hostname }}'
    joinConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          cloud-provider: external
        name: '{{ ds.meta_data.local_hostname }}'
  machineTemplate:
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
      kind: AWSMachineTemplate
      name: aws-control-plane
  replicas: 1
  version: v1.30.5

kubeadmConfigSpec 内の initConfiguration, joinConfiguration は文字通り kubeadm initkubeadm join で control plane ノードを追加するときの設定に対応します。

machineTemplate は control plane を構成するノード (VM) のプロパティを記述します。ここでも具体的な設定は AWSMachineTemplate という aws infra provider 固有の別リソースで定義して参照する形式となっています。

基本的にクイックスタートのマニフェストをそのまま使用する形式で問題ないですが、今回の用途に合わせて以下の部分を変更・追加します。

  • networking.podSubnet: "10.244.0.0/16"
    • 明示的に指定しないと node の spec に podSubnet が設定されない。この状態では後述する flannel のインストールがうまく行かなかったのでここで明示的に指定する。
spec:
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        extraArgs:
          cloud-provider: external
      controllerManager:
        extraArgs:
          cloud-provider: external
      networking:
        podSubnet: "10.244.0.0/16"
  • replicas: 1
    • control plane 用のノードの台数。多いとコストが増えるので 1 に設定。
  • version: v1.30.5
    • クラスタの kubernetes のバージョンを v1.30.5 に設定。

AWSMachineTemplate

cluster API ではノード (VM) のイメージやスペック、情報などを定義する Machine リソース があります。ただ実際のプロパティは展開先のクラウドに依存する部分が多いので、具体的な設定は各 infrastructure provider に固有のカスタムリソース上で定義し、machine リソースでは infrastructureRef でその情報を参照する構成になっています。そして aws provider でノードの設定を定義するためのリソースが AWSMachineTemplate となっています。
AWSMachineTemplate ではノードに使用する AMI やインスタンスタイプなど AWS 固有の EC2 インスタンスに関する設定を行います。EC2 インスタンスの設定だけあって様々な項目がカスタマイズできるようになっていますが、ここでは以下の設定を行います。サポートされているフィールドは https://cluster-api-aws.sigs.k8s.io/crd/#infrastructure.cluster.x-k8s.io/v1beta1.AWSMachineTemplateResource を参照。

  • iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
    • インスタンスに設定されるインスタンスプロファイルを指定。これは事前準備の clusterawsadm bootstrap iam create-cloudformation-stack 内で作成される。
  • instanceType: t2.medium
    • 検証なので小さめのインスタンスを選択してコストを節約。
  • sshKeyName
    • ノードに ssh するための ssh keypair を指定。

Published AMIs によると AMI を指定しない場合は cluster API 用のパブリック AMI を自動で選択するようになっているみたいですが、実際試してみると ami が見つけられずエラーとなることがあったので ami.id でインスタンスの AMI を明示的に指定します。
clusterawsadm ami list を実行すると k8s バージョン毎に利用可能なパブリック AMI の一覧が取得できます。

$ clusterawsadm ami list
KUBERNETES VERSION   REGION           OS               NAME                                         AMI ID
v1.30.5              ap-northeast-1   ubuntu-24.04     capa-ami-ubuntu-24.04-v1.30.5-1728924607     ami-09a92f3026810e9f2
v1.30.5              ap-northeast-1   ubuntu-22.04     capa-ami-ubuntu-22.04-v1.30.5-1728920688     ami-09122308bcde879e4
v1.30.5              ap-northeast-1   flatcar-stable   capa-ami-flatcar-stable-v1.30.5-1728926211   ami-0b81c1f65029d1a0a
v1.30.2              ap-northeast-1   ubuntu-24.04     capa-ami-ubuntu-24.04-v1.30.2-1729082892     ami-01bf294409a2c9d81
v1.30.2              ap-northeast-1   ubuntu-22.04     capa-ami-ubuntu-22.04-v1.30.2-1729078952     ami-078c506c537e24edd
v1.30.2              ap-northeast-1   flatcar-stable   capa-ami-flatcar-stable-v1.30.2-1729094390   ami-062493054dbc3c4d6
v1.29.9              ap-northeast-1   ubuntu-24.04     capa-ami-ubuntu-24.04-v1.29.9-1728670729     ami-028f63e957acb2bd5
v1.29.9              ap-northeast-1   ubuntu-22.04     capa-ami-ubuntu-22.04-v1.29.9-1728669184     ami-0f677c9f38a2afede
v1.29.9              ap-northeast-1   flatcar-stable   capa-ami-flatcar-stable-v1.29.9-1728672309   ami-04e24db7c2817bc76
v1.29.8              ap-northeast-1   ubuntu-24.04     capa-ami-ubuntu-24.04-v1.29.8-1729074685     ami-0de86d149aaaa25be
v1.29.8              ap-northeast-1   ubuntu-22.04     capa-ami-ubuntu-22.04-v1.29.8-1729071846     ami-0a1447ec5d15ad6d8
v1.29.8              ap-northeast-1   flatcar-stable   capa-ami-flatcar-stable-v1.29.8-1729076396   ami-00853021326bce295
v1.29.7              ap-northeast-1   ubuntu-24.04     capa-ami-ubuntu-24.04-v1.29.7-1729177869     ami-05e360b1eb1c2f074
v1.29.7              ap-northeast-1   ubuntu-22.04     capa-ami-ubuntu-22.04-v1.29.7-1729176322     ami-0428bf6453b1ed71d
v1.29.7              ap-northeast-1   flatcar-stable   capa-ami-flatcar-stable-v1.29.7-1729181280   ami-032c045b8e02f5259

今回は v1.30.5 のクラスタを作ろうとしているので、選択肢としては上から 3 つの v1.30.5 用の ubuntu-24.04, ubuntu-22.04, flatcar-stable があります。ここでは ubuntu-24.04 の ami-09a92f3026810e9f2 を使うことにします。
これらの設定を考慮したマニフェストは以下。

awsmachinetemplate.yml
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: AWSMachineTemplate
metadata:
  name: aws-control-plane
  namespace: capa-system
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
spec:
  template:
    spec:
      iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
      instanceType: t2.medium
      sshKeyName: [ssh_keyname]
      ami:
        id: "ami-09a92f3026810e9f2"

まとめ

以上をまとめると、aws に必要なインフラと k8s クラスタ (control plane) を作成するためのマニフェストは以下のようになります。

マニフェスト
control-plane.yml
kind: AWSCluster
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
metadata:
  name: my-cluster-aws
  namespace: capa-system
spec:
  region: ap-northeast-1
  sshKeyName: [keyname]
  controlPlaneLoadBalancer:
    loadBalancerType: nlb
  network:
    vpc:
      availabilityZoneSelection: Ordered
      availabilityZoneUsageLimit: 2

---
kind: Cluster
apiVersion: cluster.x-k8s.io/v1beta1
metadata:
  name: my-cluster
  namespace: capa-system
spec:
  infrastructureRef:
    kind: AWSCluster
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
    name: my-cluster-aws
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: aws-control-plane

---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: aws-control-plane
  namespace: capa-system
spec:
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        extraArgs:
          cloud-provider: external
      controllerManager:
        extraArgs:
          cloud-provider: external
      networking:
        podSubnet: "10.244.0.0/16"
    initConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          cloud-provider: external
        name: "{{ ds.meta_data.local_hostname }}"
    joinConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          cloud-provider: external
        name: "{{ ds.meta_data.local_hostname }}"
  machineTemplate:
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
      kind: AWSMachineTemplate
      name: aws-control-plane
  replicas: 1
  version: v1.30.5

---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: AWSMachineTemplate
metadata:
  name: aws-control-plane
  namespace: capa-system
spec:
  template:
    spec:
      iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
      instanceType: t2.medium
      sshKeyName: [keyname]
      ami:
        id: "ami-09a92f3026810e9f2"

これを適用すると、aws infrastructure controller pod がリソースの作成を検知して aws 上に各種インフラ等を作成します。pod ログを見ることでリソースが順次作成されていく様子が確認できます。

ログの一部
I1114 15:54:53.304864       1 logger.go:67] "Created subnet" controller="awscluster" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="AWSCluster" AWSCluster="capa-system/my-cluster-aws" namespace="capa-system" name="my-cluster-aws" reconcileID="a1d1af43-7565-48d6-8657-099172fefebc" cluster="capa-system/my-cluster" id="subnet-044e6839f7291a990" public=true az="ap-northeast-1a" cidr="10.0.0.0/19" ipv6=false ipv6-cidr=""
I1114 15:54:53.992968       1 logger.go:67] "Created subnet" controller="awscluster" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="AWSCluster" AWSCluster="capa-system/my-cluster-aws" namespace="capa-system" name="my-cluster-aws" reconcileID="a1d1af43-7565-48d6-8657-099172fefebc" cluster="capa-system/my-cluster" id="subnet-089f85c5a7283a2f4" public=false az="ap-northeast-1a" cidr="10.0.64.0/18" ipv6=false ipv6-cidr=""
I1114 15:54:54.492166       1 logger.go:67] "Created subnet" controller="awscluster" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="AWSCluster" AWSCluster="capa-system/my-cluster-aws" namespace="capa-system" name="my-cluster-aws" reconcileID="a1d1af43-7565-48d6-8657-099172fefebc" cluster="capa-system/my-cluster" id="subnet-0b16b0de8a5313f97" public=true az="ap-northeast-1c" cidr="10.0.32.0/19" ipv6=false ipv6-cidr=""
I1114 15:54:55.253627       1 logger.go:67] "Created subnet" controller="awscluster" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="AWSCluster" AWSCluster="capa-system/my-cluster-aws" namespace="capa-system" name="my-cluster-aws" reconcileID="a1d1af43-7565-48d6-8657-099172fefebc" cluster="capa-system/my-cluster" id="subnet-014bd88cea2db893d" public=false az="ap-northeast-1c" cidr="10.0.128.0/18" ipv6=false ipv6-cidr=""

インフラ作成では NLB 作成 ~ アクティブになるのが一番時間がかかるためその過程で pod にエラー等が出力されますが、アクティブになるとそのまま EC2 インスタンス作成や control plane bootstrap の処理が続行されます。内部的には Bootstrap provider が kubeadm の bootstrap、control plane provider が control plane の管理を行うため、これらの controller pod ログでも各リソース作成の処理が進んでいることが確認できます。

bootstrap provider pod ログ
I1114 15:59:50.650716       1 kubeadmconfig_controller.go:394] "Creating BootstrapData for the first control plane" controller="kubeadmconfig" controllerGroup="bootstrap.cluster.x-k8s.io" controllerKind="KubeadmConfig" KubeadmConfig="capa-system/aws-control-plane-q4bts" namespace="capa-system" name="aws-control-plane-q4bts" reconcileID=ef0b41b4-5c64-4faa-ae71-56c3addd6fed Machine="capa-system/aws-control-plane-pjq7k" Machine="capa-system/aws-control-plane-pjq7k" resourceVersion="3731" Cluster="capa-system/my-cluster"
controlplane provider pod ログ
I1114 16:01:17.158910       1 controller.go:276] "Reconcile KubeadmControlPlane" controller="kubeadmcontrolplane" controllerGroup="controlplane.cluster.x-k8s.io" controllerKind="KubeadmControlPlane" KubeadmControlPlane="capa-system/aws-control-plane" namespace="capa-system" name="aws-control-plane" reconcileID=8903f69f-3408-486c-9266-ab9b0c6699c4 Cluster="capa-system/my-cluster"

無事に処理が完了すると 1 つのノードから構成される k8s クラスタの control plane が完成します。 kubeadmcontrolplanes の replica (ノード数) が 1 になりますが、CNI をインストールしていないのでクラスタとしてはまだ利用できずステータスは UNAVAILABLE になっています。

$ k get kubeadmcontrolplanes.controlplane.cluster.x-k8s.io
NAME                CLUSTER      INITIALIZED   API SERVER AVAILABLE   REPLICAS   READY   UPDATED   UNAVAILABLE   AGE   VERSION
aws-control-plane   my-cluster   true                                 1                  1         1             12m   v1.30.5

クラスタにアクセスするための kubeconfig は control plane 作成に完了した時点で [cluster-name]-kubeconfig として secret に保存されるので以下で取得できます。

k get secrets my-cluster-kubeconfig -o yaml | yq -r ".data.value" | base64 -d

これで kubectl でアクセスできます。

$ export KUBECONFIG=./kubeconfig
$ k get node
NAME                                           STATUS     ROLES           AGE    VERSION
ip-10-0-89-4.ap-northeast-1.compute.internal   NotReady   control-plane   9m4s   v1.30.5

Cloud Controller のインストール

作った control plane ノードのプロパティを見てみると taint に node.cloudprovider.kubernetes.io/uninitialized が設定されていて pod 等が配置できなくなっています。これ自体は k8s で想定されている動作のようで、cloud provider を使う場合は cloud controller manager と呼ばれるコンポーネントを使ってこのあたりを管理する必要があります。詳細は以下を参照。

https://kubernetes.io/docs/tasks/administer-cluster/running-cloud-controller/

今回は aws 上でクラスタを構築しているため、AWS 用の cloud provider である cloud-provider-aws をインストールする必要があります。

https://cloud-provider-aws.sigs.k8s.io/

一方で aws cloud controller は helm chart があるので helm でインストールできます。

作成したクラスタ上に helm chart をインストールしたい場合は helm addon provider の HelmChartProxy リソースが利用できます。
HelmChartProxy では以下のように chart の repo url や release name 等を指定してデプロイします。これを作成すると helm addon provider がリソース作成を検知し、clusterSelector で指定したクラスタ上に helm chart をインストールします( matchLabels: {} の場合はすべてのクラスタが対象になる)。

apiVersion: addons.cluster.x-k8s.io/v1alpha1
kind: HelmChartProxy
metadata:
  name: cloud-controller-manager
  namespace: capa-system
spec:
  clusterSelector:
    matchLabels: {}
  releaseName: aws-cloud-controller-manager
  repoURL: https://kubernetes.github.io/cloud-provider-aws
  chartName: aws-cloud-controller-manager
  namespace: kube-system

というわけで HelmChartProxy を使って aws cloud controller をインストールすれば ok ですが、実際に試すと pod が正常に起動せずにエラーとなりました、quick start のマニフェストと比較しながら原因を調べると、どうやら helm chart では pod の引数と SA に関連づく ClusterRole system:cloud-controller-manage で以下の権限が不足しているため controller manager のコンテナがエラーで終了しているようでした。

# コンテナの引数の差分
args:
  - --v=2
  - --cloud-provider=aws
+ - --use-service-account-credentials=true
+ - --configure-cloud-routes=false

# clusterRole の差分
clusterRoleRules:
    - apiGroups:
      - ""
      resources:
      - serviceaccounts
      verbs:
      - create
+      - get
+      - list
+      - watch

HelmChartProxy では valuesTemplate で chart value の値を上書きすることができるので (helm install の -f values.yml と同じ)、values.yml の clusterRole に相当する部分を追加します。

修正後の HelmChartProxy
apiVersion: addons.cluster.x-k8s.io/v1alpha1
kind: HelmChartProxy
metadata:
  name: cloud-controller-manager
  namespace: capa-system
spec:
  clusterSelector:
    matchLabels: {}
  releaseName: aws-cloud-controller-manager
  repoURL: https://kubernetes.github.io/cloud-provider-aws
  chartName: aws-cloud-controller-manager
  namespace: kube-system
  valuesTemplate: |
    clusterRoleRules:
    - apiGroups:
      - ""
      resources:
      - events
      verbs:
      - create
      - patch
      - update
    - apiGroups:
      - ""
      resources:
      - nodes
      verbs:
      - '*'
    - apiGroups:
      - ""
      resources:
      - nodes/status
      verbs:
      - patch
    - apiGroups:
      - ""
      resources:
      - services
      verbs:
      - list
      - patch
      - update
      - watch
    - apiGroups:
      - ""
      resources:
      - services/status
      verbs:
      - list
      - patch
      - update
      - watch
    - apiGroups:
      - ""
      resources:
      - serviceaccounts
      verbs:
      - create
      - get
      - list
      - watch
    - apiGroups:
      - ""
      resources:
      - persistentvolumes
      verbs:
      - get
      - list
      - update
      - watch
    - apiGroups:
      - ""
      resources:
      - endpoints
      verbs:
      - create
      - get
      - list
      - watch
      - update
    - apiGroups:
      - coordination.k8s.io
      resources:
      - leases
      verbs:
      - create
      - get
      - list
      - watch
      - update
    - apiGroups:
      - ""
      resources:
      - serviceaccounts/token
      verbs:
      - create
    - apiGroups:
        - authentication.k8s.io
      resources:
        - tokenreviews
      verbs:
        - create
    - apiGroups:
        - authorization.k8s.io
      resources:
        - subjectaccessreviews
      verbs:
        - create

これで再度適用することで正常にインストールできました。

CNI のインストール

クラスタを使うには CNI のインストールが必要です。クイックスタートでは github 上のマニフェストを使って calico をインストールしていますが、ここではせっかくなので HelmChartProxy で flannel をインストールします。

helm-flannel.yml
apiVersion: addons.cluster.x-k8s.io/v1alpha1
kind: HelmChartProxy
metadata:
  name: flannel
  namespace: capa-system
spec:
  clusterSelector:
    matchLabels: {}
  releaseName: flannel
  repoURL: https://flannel-io.github.io/flannel/
  chartName: flannel
  namespace: kube-flannel

これでクラスタ内のノードに IP address が割り振られて正常に使用できようになります。

worker node の追加

作成したクラスタにはまだ worker node がないので MachineDeployment リソースを作成して worker node をクラスタに追加します。

Machine Deployment

MachineDeployment はクラスタ内のノードを管理するためのリソースとなっています。
k8s では pod を管理するためのリソースに deployment - replicaset - pod の関係がありますが、 cluster API では machinedeployment - machineset - machine がこの関係に対応しています。pod をノード (VM) に置き換えればだいたい同じ概念が通用します。

MachineDeployment リソースによって作成されたノードは worker node としてクラスタに追加されますが、その際の bootstrap などの設定を bootstrap.configRef で指定します。VM のスペック等は control plane と同様に infrastructureRef で参照します。

apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: md
  namespace: capa-system
spec:
  clusterName: my-cluster
  replicas: 1
  selector:
    matchLabels: null
  template:
    spec:
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: aws-ct
      clusterName: my-cluster
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
        kind: AWSMachineTemplate
        name: aws-mt-worker
      version: v1.30.5

今回は control plane と worker node で特に設定を変えないので、AWSMachineTemplate と KubeadmConfigTemplate は control plane に指定したものとほぼ同じように設定します。

マニフェスト
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: md
  namespace: capa-system
spec:
  clusterName: my-cluster
  replicas: 1
  selector:
    matchLabels: null
  template:
    spec:
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: aws-ct
      clusterName: capi-quickstart
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
        kind: AWSMachineTemplate
        name: aws-mt-worker
      version: v1.30.5
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: AWSMachineTemplate
metadata:
  name: aws-mt-worker
  namespace: capa-system
spec:
  template:
    spec:
      iamInstanceProfile: nodes.cluster-api-provider-aws.sigs.k8s.io
      instanceType: t2.medium
      sshKeyName: docker_aws
      ami:
        id: "ami-09a92f3026810e9f2"

---
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
  name: aws-ct
  namespace: capa-system
spec:
  template:
    spec:
      joinConfiguration:
        nodeRegistration:
          kubeletExtraArgs:
            cloud-provider: external
          name: "{{ ds.meta_data.local_hostname }}"

このリソースをデプロイ後、作成したクラスタを見ると worker node が 1 台追加されていることが確認できます。

$ k get node
NAME                                             STATUS     ROLES           AGE   VERSION
ip-10-0-81-101.ap-northeast-1.compute.internal   NotReady   <none>          47s   v1.30.5
ip-10-0-89-4.ap-northeast-1.compute.internal     NotReady   control-plane   14m   v1.30.5

一方で cluster API 側から見た worker node のデプロイ状況は machinedeployments リソースの replica で確認できます。

$ k get machinedeployments.cluster.x-k8s.io
NAME   CLUSTER      REPLICAS   READY   UPDATED   UNAVAILABLE   PHASE       AGE     VERSION
md     my-cluster   1                  1         1             ScalingUp   2m27s   v1.30.5

これで worker node の追加も完了したので、後は通常の k8s クラスタと同様に pod などを配置できます。

クリーンアップ

作ったリソースを削除する場合はそのまま Cluster や AWSCluster などのカスタムリソースを削除すれば ok です。

$ k delete -f aws-cluster
awscluster.infrastructure.cluster.x-k8s.io "my-cluster-aws" deleted
cluster.cluster.x-k8s.io "my-cluster" deleted
kubeadmcontrolplane.controlplane.cluster.x-k8s.io "aws-control-plane" deleted
awsmachinetemplate.infrastructure.cluster.x-k8s.io "aws-control-plane" deleted
helmchartproxy.addons.cluster.x-k8s.io "cloud-controller-manager" deleted
helmchartproxy.addons.cluster.x-k8s.io "flannel" deleted
awsmachinetemplate.infrastructure.cluster.x-k8s.io "aws-mt-worker" deleted
kubeadmconfigtemplate.bootstrap.cluster.x-k8s.io "aws-ct" deleted
Error from server (NotFound): error when deleting "aws-cluster/worker.yml": machinedeployments.cluster.x-k8s.io "md" not found

場合によっては削除実行時にプロンプトがしばらく返ってきませんが、この裏では provider がリソースの削除を検知して aws 側のリソースを削除する処理を実行しています。aws infrastructure provider pod ログを見ることで VPC やロードバランサー、EC2 インスタンスの削除が行われている様子が確認できます。aws 側リソースの依存関係も考慮しながら削除するため結構時間がかかりますが、最終的に VPC まで削除されれば作成したリソースのクリーンアップが完了となります。

Argocd でデプロイする

上で見たようにクラスタの展開に必要なリソースのプロパティを 1 つずつ設定していくのはけっこう面倒なので、シンプルな用途であれば clusterctl を使ってデプロイする方が楽です。一方で operator やマニフェストを使って宣言的にクラスタを管理する方法では以下のようなメリットがあります。

  • マニフェストに対して linter やポリシーチェックが実行でき、リソースをより厳密に管理できる。
  • argocd などのデプロイツールと統合してクラスタのライフサイクル管理を GitOps で自動化する。

要は宣言的に記述できるので IaC の管理方法や利点を享受できる点が強みとなります。


ここでは Argocd を使って今までのクラスタ展開作業を自動化してみます。
ディレクトリ構成は以下。

.
├── argocd
│   ├── aws-cluster.yml
│   ├── cert-manager.yml
│   ├── cluster-api-operator.yml
│   ├── management.yml
├── aws-cluster
│   ├── control-plane.yml
│   ├── helm-cloud-controller.yml
│   ├── helm-flannel.yml
│   └── worker.yml
└── management-cluster
    └── provider.yml
  • management-cluster では管理クラスタを構成するための provider をインストール
  • aws-cluster では aws 上に control plane + worker node のクラスタを作成
  • argocd では上記リソースを展開する argocd project を定義。

各マニフェストは github にも保存してあります。
https://github.com/git-ogawa/zenn_resource/tree/main/articles/cluster_api

今まで使っていた管理クラスタを一度まっさらな状態にして、管理クラスタの構築から AWS 上へのクラスタ展開までを argocd 経由で行います。まずはじめに cert manager と cluster api operator を argocd で helm を使ってインストール。

$ k apply -f argocd/cert-manager.yml
$ k apply -f argocd/cluster-api-operator.yml
$ argocd app sync argocd/cert-manager
$ argocd app sync argocd/capi-operator

次に management-cluster project を sync して管理クラスタを構築します。

$ k apply -f argocd/management.yml
$ argocd app sync argocd/management-cluster.yml

正常に完了すると、管理クラスタ上に各種 provider の CRD がインストールされます。

$ k get customresourcedefinitions.apiextensions.k8s.io \
    addonproviders.operator.cluster.x-k8s.io \
    infrastructureproviders.operator.cluster.x-k8s.io \
    coreproviders.operator.cluster.x-k8s.io \
    bootstrapproviders.operator.cluster.x-k8s.io \
    controlplaneproviders.operator.cluster.x-k8s.io

NAME                                                CREATED AT
addonproviders.operator.cluster.x-k8s.io            2024-11-16T08:47:45Z
infrastructureproviders.operator.cluster.x-k8s.io   2024-11-16T08:47:45Z
coreproviders.operator.cluster.x-k8s.io             2024-11-16T08:47:45Z
bootstrapproviders.operator.cluster.x-k8s.io        2024-11-16T08:47:45Z
controlplaneproviders.operator.cluster.x-k8s.io     2024-11-16T08:47:45Z


$ k get addonproviders,infrastructureproviders,coreproviders,bootstrapproviders,controlplaneproviders -A

NAMESPACE      NAME                                           INSTALLEDVERSION   READY
caaph-system   addonprovider.operator.cluster.x-k8s.io/helm   v0.2.6             True

NAMESPACE     NAME                                                   INSTALLEDVERSION   READY
capa-system   infrastructureprovider.operator.cluster.x-k8s.io/aws   v2.1.4             True

NAMESPACE     NAME                                                 INSTALLEDVERSION   READY
capi-system   coreprovider.operator.cluster.x-k8s.io/cluster-api   v1.4.3             True

NAMESPACE                       NAME                                                  INSTALLEDVERSION   READY
capi-kubeadm-bootstrap-system   bootstrapprovider.operator.cluster.x-k8s.io/kubeadm   v1.4.3             True

NAMESPACE                           NAME                                                     INSTALLEDVERSION   READY
capi-kubeadm-control-plane-system   controlplaneprovider.operator.cluster.x-k8s.io/kubeadm   v1.4.3             True

controller manager pod も起動します。

NAMESPACE                           NAME                                                             READY   STATUS    RESTARTS   AGE
caaph-system                        caaph-controller-manager-56869c9964-tzjr9                        1/1     Running   0          31s
capa-system                         capa-controller-manager-55f847998b-7cp5w                         1/1     Running   0          24s
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-controller-manager-6c46687b5d-7rth5       1/1     Running   0          30s
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-controller-manager-6cd976b78c-cp4fp   1/1     Running   0          29s
capi-operator-system                capi-operator-cluster-api-operator-85bbb77577-kxkwr              1/1     Running   0          35m
capi-system                         capi-controller-manager-c54fb98f4-ghj4m                          1/1     Running   0          59s

これで管理クラスタの構築は完了です。

次に aws cluster project を sync して aws 上にクラスタを作成します。

$ k apply -f argocd/aws-cluster.yml
$ argocd app sync argocd/aws-cluster

インフラ構築 ~ control plane 作成 ~ worker node 追加まで実行するので結構時間がかかりますが、クラスタに必要な各リソースが自動で作成されます。すべての作業が完了したら secret から kubeconfig を取得することでクラスタにアクセスできます。

$ k get secrets my-cluster-kubeconfig -o yaml | yq -r ".data.value" | base64 -d > kubeconfig
$ export KUBECONFIG=./kubeconfig

ノードの状態は Ready

$ k get node -o wide
NAME                                              STATUS   ROLES           AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION   CONTAINER-RUNTIME
ip-10-0-124-217.ap-northeast-1.compute.internal   Ready    <none>          2m14s   v1.30.5   10.0.124.217   <none>        Ubuntu 24.04.1 LTS   6.8.0-1016-aws   containerd://1.7.20
ip-10-0-142-70.ap-northeast-1.compute.internal    Ready    control-plane   3m17s   v1.30.5   10.0.142.70    <none>        Ubuntu 24.04.1 LTS   6.8.0-1016-aws   containerd://1.7.20

pod の状態

$ k get pod -A
NAMESPACE      NAME                                                                     READY   STATUS    RESTARTS        AGE
kube-flannel   kube-flannel-ds-qrf6t                                                    1/1     Running   1 (2m5s ago)    2m47s
kube-flannel   kube-flannel-ds-xmrpt                                                    1/1     Running   1 (2m42s ago)   3m23s
kube-system    aws-cloud-controller-manager-xt8tp                                       1/1     Running   0               3m13s
kube-system    coredns-55cb58b774-q6hrp                                                 1/1     Running   0               3m42s
kube-system    coredns-55cb58b774-xrbkc                                                 1/1     Running   0               3m42s
kube-system    etcd-ip-10-0-142-70.ap-northeast-1.compute.internal                      1/1     Running   0               3m42s
kube-system    kube-apiserver-ip-10-0-142-70.ap-northeast-1.compute.internal            1/1     Running   0               3m42s
kube-system    kube-controller-manager-ip-10-0-142-70.ap-northeast-1.compute.internal   1/1     Running   0               3m42s
kube-system    kube-proxy-4tqqx                                                         1/1     Running   0               2m47s
kube-system    kube-proxy-kxkv2                                                         1/1     Running   0               3m42s
kube-system    kube-scheduler-ip-10-0-142-70.ap-northeast-1.compute.internal            1/1     Running   0               3m42s

というわけで最初にマニフェストを作ってしまえばデプロイの作業は argocd で自動化でき、クラスタのライフサイクル管理も Gitops に組み込むことができます。

別クラウド上にクラスタを作成する

aws 上にクラスタを作成する際は aws infrastructure provider を利用しましたが、別のクラウド上にクラスタを追加したい場合は対応する provider をインストールしてリソースを展開することで簡単に追加できます。ここでは openstack 上に新しくクラスタを作成してみます。

openstack 上に作成する場合は openstack 用の infrastructure provider を追加する必要があります。これは argocd デプロイ時に使用したマニフェストに openstack という名前の InfrastructureProvider リソースを追加すれば ok。

provider.yml
---
apiVersion: v1
kind: Namespace
metadata:
  name: capo-system

---
apiVersion: v1
kind: Secret
metadata:
  name: openstack-cloud-config
  namespace: capo-system
data:
  cacert: ...
  clouds.yaml: ...

---
apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: InfrastructureProvider
metadata:
  name: openstack
  namespace: capo-system
spec:
  version: v0.11.2

クラスタの展開には aws クラスタと同様に control plane, worker node, CNI, cloud controller のデプロイが必要です。aws のときと比較すると AWSCluster など aws 固有のリソースを OpenstackCluster 等 openstack 用のカスタムリソースに置き換える必要がありますが、基本的なリソースの構造は aws クラスタのときと変わらないので細かい部分は割愛します。クラスタ作成のためのリソースを新しく作った openstack-cluster ディレクトリに入れて argocd のプロジェクトを追加します。

.
├── argocd
│   └── openstack-cluster.yml
└── openstack-cluster
    ├── control-plane.yml
    ├── helm-cloud-controller.yml
    ├── helm-flannel.yml
    └── worker.yml

上記のマニフェストも github repo に上げてあるので詳細はそちらを参照。
クラスタ作成時は aws のときと同様に provider をインストールしたのち、上記のディレクトリを app sync します。

# openstack infrastructure provider のインストール
$ argocd app sync argocd/management-cluster.yml

# openstack cluster 作成
$ k apply -f argocd/openstack-cluster.yml
$ argocd app sync argocd/openstack-cluster.yml

少し待つと openstack 上にクラスタが作成されます。
cluster API では cluster リソースで作成済みのクラスタを確認できます。ただ k get cluster ではどのクラスタがどのクラウド上に展開されているかは区別できません(cluster が存在する namespace である程度推測はできますが)。

$ k get cluster -A -o wide
NAMESPACE     NAME          CLUSTERCLASS   PHASE         AGE     VERSION
capa-system   my-cluster                   Provisioned   8m10s
capo-system   my-cluster2                  Provisioned   8m1s

describe して infrastructure Ref などを見れば確認可能。

$ k describe cluster -A
Name:         my-cluster
Namespace:    capa-system
Labels:       app.kubernetes.io/instance=aws-cluster
Annotations:  <none>
API Version:  cluster.x-k8s.io/v1beta1
Kind:         Cluster

Spec:
  Control Plane Ref:
    API Version:  controlplane.cluster.x-k8s.io/v1beta1
    Kind:         KubeadmControlPlane
    Name:         aws-control-plane
    Namespace:    capa-system
  Infrastructure Ref:
    API Version:  infrastructure.cluster.x-k8s.io/v1beta2
    Kind:         AWSCluster
    Name:         my-cluster-aws
    Namespace:    capa-system


Name:         my-cluster2
Namespace:    capo-system
Labels:       app.kubernetes.io/instance=openstack-cluster
              cloud=openstack
Annotations:  <none>
API Version:  cluster.x-k8s.io/v1beta1
Kind:         Cluster

Spec:
  Cluster Network:
    Pods:
      Cidr Blocks:
        192.168.0.0/16
    Service Domain:  cluster.local
  Control Plane Endpoint:
    Host:  192.168.3.195
    Port:  6443
  Control Plane Ref:
    API Version:  controlplane.cluster.x-k8s.io/v1beta1
    Kind:         KubeadmControlPlane
    Name:         openstack-control-plane
    Namespace:    capo-system
  Infrastructure Ref:
    API Version:  infrastructure.cluster.x-k8s.io/v1beta1
    Kind:         OpenStackCluster
    Name:         my-cluster-openstack
    Namespace:    capo-system

複数クラウド上で多数のクラスタを動かすような環境ではどのクラウド上で動いているか区別できるようにリソース名やラベルを適切に設定する、用途に合わせて ClusterClass を作って割り当てるなどの工夫が必要になります。

cluster API では展開先クラスタに合わせた infrastructure provider を利用する構成となっているため、複数のクラウドに展開されたクラスタを 1 つの管理クラスタで一括で管理できるようになっています。

その他

Feature Gate の有効化

clutser API ではいくつかの機能は Feature Gate として実装されています。例えば bootstrap における ignition format のサポート などがこれに対応。
clsuterctl で各種 provider をインストールする場合は対応する機能を環境変数に設定した上で clusterctl init を実行すれば有効化されますが、cluster API operator を使う場合は以下のように provider マニフェストの spec.manager.featureGates に key-value で指定することで有効化されます。

https://cluster-api-operator.sigs.k8s.io/03_topics/02_configuration/05_provider-spec-configuration.html?highlight=fea#provider-spec

spec:
 manager:
   featureGates:
     FeatureA: true
     FeatureB: false

例えば flatcar イメージでクラスタを構成する場合は ignition 形式で bootstrap を記述する必要があるため、bootstrapprovider の定義に KubeadmBootstrapFormatIgnition: true を追加します。

apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: BootstrapProvider
metadata:
  name: kubeadm
  namespace: capi-kubeadm-bootstrap-system
spec:
  version: v1.8.5
  manager:
    featureGates:
      KubeadmBootstrapFormatIgnition: true

イメージのビルド

aws などのパブリッククラウドでは既に登録済みの cluster API 用のイメージを使ってノードを作成できますが、自分でビルドしたイメージを使用することもできます。
イメージ作成には以下の image-builder が活用可能。

リンク

Discussion