CloudShellでお試しするCluster API
Google Cloud Japan の RyuSA です。
みなさんは Kubernetes を利用していますか?昨今では開発環境から本番環境、はたまた自宅環境に至るまで様々な場所で Kubernetes が動いています。またセルフマネージドで Kubernetes を利用している方や Google Kubernetes Engine などのマネージド Kubernetes を利用している方もいるかと思います。Kubernetes クラスターは様々な場所で、様々な形態で起動しており、その構築方法や管理も様々でしょう。
Cluster API は「 Kubernetes を Kubernetes で管理する」ための Kubernetes サブプロジェクトのひとつです。Cluster API を利用することで、宣言的に記述されたマニフェストを使って様々な環境に Kubernetes をデプロイでき、一元的にマルチクラスターを管理できます。本記事では、オープンソースコミュニティが管理している Google Cloud 向けの Cluster API 実装である cluster-api-provider-gcp と Cloud Shell から Cluster API に触れていきます。
Cluster APIとcluster-api-provider-gcp
Cluster API は Kubernetes のコミュニティのひとつである kubernetes/sig-cluster-lifecycle がメンテナンスしている「 Kubernetes を Kubernetes で管理する」ためのサブプロジェクトです。Cluster API は専用の CRD である Cluster
MachineDeployment
KubeadmControlPlane
を提供しています。管理者はそれらのカスタム リソースを駆使してターゲット クラスターを定義し、 Cluster API は定義されたカスタムリソースを元に Kubernetes を構築します。
また Kubernetes を構築するには、ロードバランサーとサーバーが必要です。Cluster API はそれらのリソースを Cluster API から作成するための Infrastructure Provider Specification を公開しています。 Infrastructure Provider Specification に沿ったコントローラー(Provider)を実装することで Cluster API から好きなインフラストラクチャーの API を操作できるようになります。kubernetes-sigs/cluster-api-provider-gcp はオープンソースコミュニティがメンテナンスしている、Google Cloud 向けの Infrastructure Provider です。この Provider を利用することで、Cluster API を使って GCE と Cloud LoadBalancer を作成して Kubernetes をプロビジョニングできます。
Cluster API のフロー
Cluster API を使って Kubernetes を動かすには大きく分けて4つのフェーズが必要です。
- プロジェクトセットアップ
- マネジメント クラスターを構築する
- kind create cluster
- clusterctl init
- ターゲット クラスターをデプロイする
- clusterctl generate cluster
- kubectl apply
- 見守る
- :eyes:
- (Optional) Pivotする
- clusterctl move
- 今回は省略
Cloud Shell で Cluster API に触れてみる
それでは実際に Cluster API を Google Cloud で動かして、Kubernetes を起動してみましょう。まず Google Cloud ダッシュボードから Cloud Shell をアクティベートしておき、プロジェクトのセットアップを行ってからマネジメントクラスターを作成します。
プロジェクトセットアップ
まずは Cluster API で Kubernetes を利用するためのプロジェクトを作成し、またサービス アカウントとネットワークをセットアップしていきます。詳細は公式のリポジトリを参照してください。次のコマンドを実行すると Cluster API が読み取る環境変数の定義とプロジェクトを作成できます。
# 環境変数を定義
$ export GCP_REGION=asia-northeast1
$ export GCP_PROJECT=<GCP_PROJECT>
$ export KUBERNETES_VERSION=1.23.3
$ export GCP_CONTROL_PLANE_MACHINE_TYPE=n1-standard-2
$ export GCP_NODE_MACHINE_TYPE=n1-standard-2
$ export GCP_NETWORK_NAME=default
$ export CLUSTER_NAME=capi-gcp-cluster
# 後でサービスアカウントキーを出力するファイルを指定します
$ export GCP_CREDENTIAL_PATH="<path/to/gcp-credentials.json>"
$ export IMAGE_ID=projects/k8s-staging-cluster-api-gcp/global/images/cluster-api-ubuntu-2004-v1-23-3-nightly
# プロジェクトを作成
$ gcloud projects create $GCP_PROJECT
また cluster-api-provider-gcp
が作成するGCE インスタンスにはデフォルトで外部IPが付与されていません。そのため事前に default
ネットワーク上に Cloud NAT を準備しておきます。もしすでに NAT 環境がセットアップされているネットワークを利用する場合はスキップしてください。次のコマンドを実行すると Cloud Router と Cloud NAT を指定したネットワーク上に作成できます。
$ gcloud compute routers create "${CLUSTER_NAME}-myrouter" --project=${GCP_PROJECT} --region=${GCP_REGION} --network=${GCP_NETWORK_NAME}
Creating router [capi-gcp-cluster-myrouter]...done.
NAME: capi-gcp-cluster-myrouter
REGION: asia-northeast1
NETWORK: default
$ gcloud compute routers nats create "${CLUSTER_NAME}-mynat" --project="${GCP_PROJECT}" --router-region="${GCP_REGION}" --router="${CLUSTER_NAME}-myrouter" --nat-all-subnet-ip-ranges --auto-allocate-nat-external-ips
Creating NAT [capi-gcp-cluster-mynat] in router [capi-gcp-cluster-myrouter]...done.
最後にサービス アカウントのセットアップをします。このサービス アカウント capi-administrator
はマネジメント クラスター内からGoogle Cloud プロジェクトの各種リソースを操作するために利用されます。次のコマンドを実行するとサービス アカウント capi-administrator
が作成され、ロール roles/editor
が作成したサービス アカウントに紐つけられます。
$ gcloud iam service-accounts create capi-administrator --project=${GCP_PROJECT}
Created service account [capi-administrator].
$ gcloud projects add-iam-policy-binding ${GCP_PROJECT} --member serviceAccount:"capi-administrator@${GCP_PROJECT}.iam.gserviceaccount.com" --role "roles/editor"
Updated IAM policy for project <GCP_PROJECT>.
bindings:
- members: ...
# サービスアカウント キーを事前に指定したパスに出力
$ gcloud iam service-accounts keys create ${GCP_CREDENTIAL_PATH}
マネジメント クラスターを作成する
Google Cloud ダッシュボードから Cloud Shell をアクティベートしておきます。Cluster API を開始するためのマネジメント クラスターを Cloud Shell 上に作成します。
Cloud Shell には元々 Docker がインストールされていることもあり、今回は Docker コンテナ上に Kubernetes を構築できる kind をマネジメント クラスターとして利用することにします。Minikube でも動かせますので、お好みに合わせて Minikube を利用してください。
$ kind create cluster
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.25.3) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a nice day! 👋
$ kubectl version –short
Client Version: v1.26.0
Kustomize Version: v4.5.7
Server Version: v1.25.3
次に Cluster API を操作するためのコマンドラインツール clusterctl をインストールします。
$ curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.3/clusterctl-linux-amd64 -o clusterctl
$ sudo install -o root -g root -m 0755 clusterctl /usr/local/bin/clusterctl
$ clusterctl version
clusterctl version: &version.Info{Major:"1", Minor:"3", GitVersion:"v1.3.3", GitCommit:"09030092bf1e9891da4cf6b48b486cb76efaab82", GitTreeState:"clean", BuildDate:"2023-01-31T17:17:34Z", GoVersion:"go1.19.5", Compiler:"gc", Platform:"linux/amd64"}
ここまででマネジメント クラスターを初期化する準備が整いました。clusterctl とサービスアカウント キーを使ってマネジメント クラスターを初期化しましょう。次のコマンドで Google Cloud 向けの Provider である cluster-api-provider-gcp と Cluster API 標準の Provider をマネジメント クラスターにインストールできます。
$ export GCP_B64ENCODED_CREDENTIALS=$( cat "${GCP_CREDENTIAL_PATH}" | base64 | tr -d '\n' )
$ clusterctl init --infrastructure gcp
Fetching providers
Installing cert-manager Version="v1.11.0"
Waiting for cert-manager to be available...
Installing Provider="cluster-api" Version="v1.3.3" TargetNamespace="capi-system"
Installing Provider="bootstrap-kubeadm" Version="v1.3.3" TargetNamespace="capi-kubeadm-bootstrap-system"
Installing Provider="control-plane-kubeadm" Version="v1.3.3" TargetNamespace="capi-kubeadm-control-plane-system"
Installing Provider="infrastructure-gcp" Version="v1.2.1" TargetNamespace="capg-system"
Your management cluster has been initialized successfully!
You can now create your first workload cluster by running the following:
clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -
ターゲット クラスターをデプロイする
ターゲット クラスターを作成するためのマニフェストを生成します。先ほどインストールした clusterctl にはターゲット クラスターを作成するためのマニフェストを生成するコマンド clusterctl generate cluster
が用意されています。次のコマンドでコントロール プレーンノード3台とワーカーノード1台の構成の Kubernetes を作成するマニフェストを生成できます。
$ clusterctl generate cluster $CLUSTER_NAME --control-plane-machine-count=3 --worker-machine-count=1 > capi-on-gcp.yaml
$ less capi-on-gcp.yaml
生成されたマニフェストには次のようなカスタムリソース(CR)が定義されています。これらの CRD はマネジメント クラスターを初期化したタイミングで用意されており、それぞれのリソースを適切に定義することで必要なスペックの Kubernetes クラスターを作成できます。
- cluster.x-k8s.io/v1beta1
- Cluster
- MachineDeployment
- infrastructure.cluster.x-k8s.io/v1beta1
- GCPMachineTemplate
- GCPCluster
ターゲット クラスターのマニフェストをデプロイするとマネジメント クラスターにデプロイされているProviderたちが実際にクラスターを作成し始めます。
$ kubectl apply -f capi-on-gcp.yaml
cluster.cluster.x-k8s.io/capi-gcp-cluster created
gcpcluster.infrastructure.cluster.x-k8s.io/capi-gcp-cluster created
kubeadmcontrolplane.controlplane.cluster.x-k8s.io/capi-gcp-cluster-control-plane created
gcpmachinetemplate.infrastructure.cluster.x-k8s.io/capi-gcp-cluster-control-plane created
machinedeployment.cluster.x-k8s.io/capi-gcp-cluster-md-0 created
gcpmachinetemplate.infrastructure.cluster.x-k8s.io/capi-gcp-cluster-md-0 created
kubeadmconfigtemplate.bootstrap.cluster.x-k8s.io/capi-gcp-cluster-md-0 created
ターゲット クラスターを見守る
ターゲット クラスターが適切に作成されているか見てみましょう。Kubernetes のリソースとして Cluster
と KubeadmControlplane
をチェックしてみましょう。私が検証した際はだいたい10分ほどでコントロール プレーンのステータスがINITIALIZEDになりました。次のコマンドを実行すると現在のクラスターの状況を確認できます。
$ kubectl get cluster
NAME PHASE AGE VERSION
capi-gcp-cluster Provisioned 6m38s
$ kubectl get kubeadmcontrolplane
NAME CLUSTER INITIALIZED API SERVER AVAILABLE REPLICAS READY UPDATED UNAVAILABLE AGE VERSION
capi-gcp-cluster-control-plane capi-gcp-cluster true 3 3 3 6m40s v1.23.3
また、Google Cloud のコンソールから実際に作成された GCE インスタンスと Cloud LoadBalancer を確認できます。
作成できたターゲット クラスターを覗いてみましょう。Kubernetes にアクセスするためには Kubernetes によって署名された証明書が必要です。その証明書はマネジメント クラスターにSecretとして保存されています。kubectl コマンドで取得するか、clusterctl コマンドで取得できます。次のコマンドを実行すると、ターゲットクラスターにアクセスするためのファイルを取得できます。
# ターゲットクラスターのkubeconfigをローカルに保存
$ clusterctl get kubeconfig $CLUSTER_NAME > capi-on-gcp.kubeconfig
# kubectl get secret "${CLUSTER_NAME}-kubeconfig" -o jsonpath='{.data.value}' | base64 -d > capi-on-gcp.kubeconfig
# 取得したkubeconfigを指定してターゲットクラスターのノードをチェックする
$ kubectl get nodes --kubeconfig capi-on-gcp.kubeconfig
NAME STATUS ROLES AGE VERSION
capi-gcp-cluster-control-plane-56bxs NotReady control-plane,master 8m31s v1.23.3
capi-gcp-cluster-control-plane-clxxw NotReady control-plane,master 5m25s v1.23.3
capi-gcp-cluster-control-plane-xd5lj NotReady control-plane,master 7m8s v1.23.3
capi-gcp-cluster-md-0-w657q NotReady <none> 7m13s v1.23.3
このままでは各ノードが READY のステータスにはなりません。これはクラスターにまだ CNI がインストールされていないことが原因です。お好みの CNI をインストールしてクラスターを READY にセットアップしましょう。次のコマンドを実行すると、GitHub で公開されている Calico を CNI としてターゲット クラスターにインストールできます。
$ kubectl --kubeconfig=./capi-on-gcp.kubeconfig apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.24.1/manifests/calico.yaml
$ kubectl get kubeadmcontrolplane
NAME CLUSTER INITIALIZED API SERVER AVAILABLE REPLICAS READY UPDATED UNAVAILABLE AGE VERSION
capi-gcp-cluster-control-plane capi-gcp-cluster true true 3 3 3 0 10m v1.23.3
$ kubectl get nodes --kubeconfig capi-on-gcp.kubeconfig
NAME STATUS ROLES AGE VERSION
capi-gcp-cluster-control-plane-56bxs Ready control-plane,master 12m v1.23.3
capi-gcp-cluster-control-plane-clxxw Ready control-plane,master 8m58s v1.23.3
capi-gcp-cluster-control-plane-xd5lj Ready control-plane,master 10m v1.23.3
capi-gcp-cluster-md-0-w657q Ready <none> 10m v1.23.
Cluster API で遊んでみる
さてマネジメント クラスターとターゲットクラスターを作成したところで少し Cluster API と cluster-api-provider-gcp で遊んでみましょう。
まずは試しにターゲット クラスターのワーカーノードの数を1から3に増やしてみましょう。ワーカーノードの数を変更するには、MachineDeployment
リソースの .spec.replicas
を変更してマニフェストをデプロイするだけです。
$ kubectl get machinedeployment
NAME CLUSTER REPLICAS READY UPDATED UNAVAILABLE PHASE AGE VERSION
capi-gcp-cluster-md-0 capi-gcp-cluster 1 1 1 0 Running 32m v1.23.3
$ # edit capi-on-gcp.yaml
$ cat capi-on-gcp.yaml
...
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
name: capi-gcp-cluster-md-0
namespace: default
spec:
- replicas: 1
+ replicas: 3
...
$ kubectl apply -f capi-on-gcp.yaml
machinedeployment.cluster.x-k8s.io/capi-gcp-cluster-md-0 configured
$ kubectl get machinedeployment -w
NAME CLUSTER REPLICAS READY UPDATED UNAVAILABLE PHASE AGE VERSION
capi-gcp-cluster-md-0 capi-gcp-cluster 3 3 3 0 Running 39m v1.23.3
$ kubectl get nodes --kubeconfig capi-on-gcp.kubeconfig
NAME STATUS ROLES AGE VERSION
capi-gcp-cluster-control-plane-56bxs Ready control-plane,master 40m v1.23.3
capi-gcp-cluster-control-plane-clxxw Ready control-plane,master 37m v1.23.3
capi-gcp-cluster-control-plane-xd5lj Ready control-plane,master 38m v1.23.3
capi-gcp-cluster-md-0-q2c5t Ready <none> 46s v1.23.3
capi-gcp-cluster-md-0-w657q Ready <none> 39m v1.23.3
capi-gcp-cluster-md-0-zf8m8 Ready <none> 44s v1.23.3
MachineDeployment
リソースは Kubernetes における Deployment に相当するリソースで、Pod のレプリカを作成するのと同じようにリソースのスペック通りの GCE インスタンスを作成してくれます。
このように Cluster API では Kubernetes のノードや Kubeadm の設定などを宣言的なマニフェストとして管理できます。これはつまり ArgoCD や Flux2 などの GitOps ツールを使ってGoogle Cloud上に Kubernetes as a Service を構築することもできると思います。これは E2E テストやパフォーマンス試験などで使えそうですね。
一方でステートレスなワークロードのみを利用したい、もしくはターゲットクラスターの稼働時間が 24 時間以下の短期間になるなどの理由で GCE プリエンプティブルインスタンスを利用したい場合もあるでしょう。またディスクサイズやディスクタイプを変更したいこともあるでしょう。そういった Google Cloud の設定は GCPMachineTemplate
に定義できます。
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: GCPMachineTemplate
metadata:
name: sample-template
namespace: default
spec:
template:
spec:
additionalDisks:
- deviceType: pd-ssd
size: 200
additionalLabels:
foo: bar
additionalNetworkTags:
- secure
image: projects/k8s-staging-cluster-api-gcp/global/images/cluster-api-ubuntu-2004-v1-23-3-nightly
instanceType: n1-standard-2
ipForwarding: Enabled
preemptible: true
publicIP: false
rootDeviceType: pd-ssd
rootDeviceSize: 60
serviceAccounts:
email: ryusa@example.com
scopes:
- https://www.googleapis.com/auth/cloud-platform
クリーンアップ
作成したリソースなどを削除していきます。
ターゲット クラスターの削除
$ kubectl delete cluster $CLUSTER_NAME
$ rm capi-on-gcp.kubeconfig
マネジメント クラスターの削除
$ kind delete cluster
Cloud Router / Cloud NAT の削除
$ gcloud compute routers nats delete "${CLUSTER_NAME}-mynat" --project="${GCP_PROJECT}" \
--router-region="${GCP_REGION}" --router="${CLUSTER_NAME}-myrouter"
$ gcloud compute routers delete "${CLUSTER_NAME}-myrouter" --project="${GCP_PROJECT}" \
--region="${GCP_REGION}"
サービス アカウントの削除
$ gcloud iam service-accounts delete capi-administrator@${GCP_PROJECT}.iam.gserviceaccount.com
$ rm ${GCP_CREDENTIAL_PATH}
プロジェクトの削除
$ gcloud projects delete $GCP_PROJECT
最後に
Cluster API を利用することでベアメタルからクラウドまで、マネジメント クラスターと宣言的なマニフェストをベースに一括で管理できます。特にArgoCD や Flux2 などの GitOps ツールと連携したり CI 環境と統合することで、より柔軟な環境構築と継続的なオペレーションを遂行できるかと思います。
Cluster API と Google Cloud のチュートリアルは、公式ドキュメントに詳細が記載されています。ご参考ください。
Kubernetesは、いいぞ
Discussion