このチュートリアルでは、ノードの自動プロビジョニングを使用してマルチテナントの Google Kubernetes Engine(GKE)クラスタをスケーリングする方法と、Workload Identity を使用して Cloud Storage バケットなどのリソースに対するテナント アクセスを制御する方法について説明します。このガイドは、デベロッパーとアーキテクトを対象としており、Kubernetes と GKE の基本的な知識があることを前提にしています。概要についての説明が必要な場合は、GKE の概要をご覧ください。
クラスタ マルチテナンシーは、多くの場合に、コストの削減、またはテナント間でのオペレーションを標準化することを目的として実装されます。コストの削減を全面的に実現するには、クラスタ リソースを効率的に使用できるようにクラスタのサイズを設定する必要があります。クラスタを自動スケーリングする場合は、追加するクラスタノードのサイズを適切なものにすることで、リソースの浪費を最小限に抑えることも必要です。
このチュートリアルでは、ノードの自動プロビジョニングを使用してクラスタをスケーリングします。ノードの自動プロビジョニングによって、クラスタ リソースの使用量を最適化し、それによって保留中のワークロードに最適なクラスタノードを追加することで費用を制御できます。
目標
- ノードの自動プロビジョニングと Workload Identity を有効にした GKE クラスタを作成する。
- マルチテナンシーに対応したクラスタを設定する。
- ノードの自動プロビジョニングによって最適化されたサイズのノードが作成および破棄される方法を示すために、クラスタにジョブを送信する。
- taint とラベルを使用して、各テナント用に専用のノードプールを作成するようにノードの自動プロビジョニングに指示する。
- Workload Identity を使用して、Cloud Storage バケットなどのテナント固有のリソースへのアクセスを制御する。
費用
このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。
始める前に
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, activate Cloud Shell.
At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.
- Cloud Shell で、GKE と Cloud Build API 用の API を有効にします。
gcloud services enable container.googleapis.com \ cloudbuild.googleapis.com
このオペレーションには数分かかることがあります。
環境の準備
このセクションでは、このチュートリアルで必要なコードを取得し、チュートリアル全体を通して使用する値で環境を設定します。
Cloud Shell で、このチュートリアルで使用する環境変数を定義します。
export PROJECT_ID=$(gcloud config get-value project)
このチュートリアル用のコードを格納する GitHub リポジトリのクローンを作成します。
git clone https://github.com/GoogleCloudPlatform/solutions-gke-autoprovisioning
リポジトリ ディレクトリに移動します。
cd solutions-gke-autoprovisioning
Google プロジェクト ID を使用して、Kubernetes YAML のジョブ構成ファイルを更新します。
sed -i "s/MY_PROJECT/$PROJECT_ID/" manifests/bases/job/base-job.yaml
Cloud Build ジョブを送信してコンテナ イメージをビルドします。
gcloud builds submit pi/ --tag gcr.io/$PROJECT_ID/generate-pi
このイメージは、円周率の近似値を生成する Go プログラムです。このコンテナ イメージは後で使用します。
Cloud Build がイメージをプロジェクトの Container Registry にエクスポートします。
GKE クラスタの作成
このセクションでは、ノードの自動プロビジョニングと Workload Identity を有効にした GKE クラスタを作成します。クラスタ作成プロセスの詳細は次のとおりです。
- クラスタに CPU とメモリの上限を指定します。ノードの自動プロビジョニングでは、クラスタに対してノードを追加または削除する際に、これらの上限が考慮されます。詳細については、GKE ドキュメントのノード自動プロビジョニングの有効化をご覧ください。
- 自動プロビジョニングされたノードプール内のノードで使用されるデフォルトのサービス アカウントとスコープを指定します。これらの設定を使用して、プロビジョニングされたノードのアクセス権限を制御できます。詳細については、GKE ドキュメントの自動プロビジョニングされたノードの ID のデフォルトの設定をご覧ください。
- 使用率を優先する自動スケーリング プロファイルを設定します。このプロファイルは、未使用のリソースを最小限に抑えるためにクラスタをすぐにスケールダウンするようクラスタ オートスケーラーに指示します。これにより、バッチまたはジョブ中心のワークロードのリソースの効率性も向上します。この設定は、クラスタ内のすべてのノードプールに適用されます。
- Workload Identity を有効にするには、ワークロード プールを指定します。
クラスタを作成するには:
サービス アカウントを作成します。
gcloud iam service-accounts create nap-sa
このサービス アカウントは、自動プロビジョニングされたノードによって使用されます。
Container Registry で使用されている Cloud Storage バケットからイメージを pull する権限を新しいサービス アカウントに付与します。
gcloud storage buckets add-iam-policy-binding \ gs://artifacts.$PROJECT_ID.appspot.com \ --member=serviceAccount:nap-sa@$PROJECT_ID.iam.gserviceaccount.com \ --role=roles/storage.objectViewer
ノードの自動プロビジョニングと Workload Identity を有効にした GKE クラスタを作成します。
gcloud container clusters create multitenant \ --release-channel=regular \ --zone=us-central1-c \ --num-nodes=2 \ --machine-type=n1-standard-2 \ --workload-pool=${PROJECT_ID}.svc.id.goog \ --autoscaling-profile=optimize-utilization \ --enable-autoprovisioning \ --autoprovisioning-service-account=nap-sa@${PROJECT_ID}.iam.gserviceaccount.com \ --autoprovisioning-scopes=\ https://www.googleapis.com/auth/devstorage.read_write,\ https://www.googleapis.com/auth/cloud-platform \ --min-cpu 1 \ --min-memory 1 \ --max-cpu 50 \ --max-memory 256 \ --enable-network-policy \ --enable-ip-alias
デフォルトのクラスタ名とコンピューティング ゾーンを設定します。
gcloud config set container/cluster multitenant gcloud config set compute/zone us-central1-c
マルチテナンシーに対応したクラスタの設定
マルチテナントの Software-as-a-Service(SaaS)アプリを実行する場合、通常はテナントを分離する必要があります。テナントを分離すると、不正使用されたテナントからの被害を最小限に抑えることができます。また、テナント間でクラスタ リソースを均等に割り当て、各テナントが使用しているリソース数を追跡することもできます。Kubernetes はテナント間の分離の完全な安全性を保証することはできませんが、特定のユースケースに対して十分と考えられる機能を備えています。GKE マルチテナンシー機能の詳細については、GKE ドキュメントの概要とベスト プラクティス ガイドをご覧ください。
サンプルアプリで、2 つのテナント、tenant1
と tenant2
を作成します。各テナントとその Kubernetes リソースをそれぞれ独自の Namespace に分離します。他の Namespace との通信を妨げて、テナントの分離を実現するシンプルなネットワーク ポリシーを作成します。後で、Node Taints と nodeSelector
フィールドを使用して、異なるテナントの Pod が同じノードでスケジュールされないようにします。専用ノードでテナントのワークロードを実行することで、さらなる分離を指定できます。
Kustomize を使用して、クラスタに送信する Kubernetes マニフェストを管理します。Kustomize を使用すると、複数の目的のために YAML ファイルを組み合わせてカスタマイズできます。
名前空間、サービス アカウント、
tenant1
のネットワーク ポリシー リソースを作成します。kubectl apply -k manifests/setup/tenant1
出力は次のようになります。
namespace/tenant1-ns created serviceaccount/tenant1-ksa created networkpolicy.networking.k8s.io/tenant1-deny-from-other-namespaces created
tenant2
のクラスタ リソースを作成します。kubectl apply -k manifests/setup/tenant2
ノードの自動プロビジョニングの動作を確認する
GKE クラスタは、1 つ以上のノードプールで構成されます。ノードプール内のすべてのノードは同じマシンタイプであるため、CPU とメモリの量は同じになります。ワークロード リソースの需要が変動する場合は、クラスタ内で異なるマシンタイプのノードプールを複数使用することでメリットが得られます。このように、クラスタ オートスケーラーは最も適切なタイプのノードを追加できます。これにより、リソースの効率が向上するだけでなく、コストも削減できます。ただし、多数のノードプールを維持すると、管理オーバーヘッドが増えます。また、専用ノードプールでテナント ワークロードを実行する際は、マルチテナント クラスタでは実用的ではない場合があります。
代わりに、ノードの自動プロビジョニングを使用してクラスタ オートスケーラーを拡張できます。ノードの自動プロビジョニングが有効になっていると、クラスタ オートスケーラーは保留中の Pod の仕様に基づいて、新しいノードプールを自動的に作成できます。結果として、クラスタ オートスケーラーは最も適切なタイプのノードを作成できますが、ユーザーが自身でノードプールの作成や管理を行う必要はありません。ノードの自動プロビジョニングを使用すると、クラスタは過剰なプロビジョニングを行うことなく効率的に自動スケーリングでき、それによって費用を削減できます。
さらに、保留中の Pod にワークロードの分離の制約がある場合、ノードの自動プロビジョニングにより、制約を満たすノードを作成できます。このようにして、ノードの自動プロビジョニングを使用して、単一のテナントのみが使用するノードプールを自動的に作成できます。
このセクションでは、さまざまなジョブをクラスタに送信して、ノードの自動プロビジョニングの動作を確認します。ジョブは、先ほど作成した generate-pi
イメージを使用します。
シンプルなジョブの送信
まず、クラスタにシンプルなジョブを送信します。ジョブは、テナント固有の制約を指定していません。ジョブの CPU リクエストとメモリ リクエストを処理するのに十分な空きスペースがクラスタにあります。そのため、このジョブがデフォルトのノードプール内の既存のノードのいずれかにスケジュールされることが想定されます。追加のノードはプロビジョニングされません。
クラスタ内のノードプールを一覧表示します。
gcloud container node-pools list
デフォルト プールが 1 つだけ表示されます。
ジョブの構成をコンソールに出力します。
kubectl kustomize manifests/jobs/simple-job/
出力は次のようになります。
apiVersion: batch/v1 kind: Job metadata: name: pi-job spec: ...
この構成では、Node Taints やセレクタは指定されません。
ジョブを送信します。
kubectl apply -k manifests/jobs/simple-job/
クラスタ内のノードプールを監視します。
watch -n 5 gcloud container node-pools list
デフォルトのプールは引き続き 1 つです。新しいノードプールは作成されません。
約 30 秒後に
Control+C
を押してノードプールの監視を停止します。クラスタ内のノードを監視します。
kubectl get nodes -w
作成中の新しいノードは表示されません。
1 分間監視を行った後、
Control+C
を押して監視を停止します。クラスタ内のジョブを一覧表示します。
kubectl get jobs --all-namespaces
出力は次のようになります。
NAMESPACE NAME COMPLETIONS DURATION AGE default pi-job 1/1 14s 21m
Completions
列の値1/1
は、合計 1 つのジョブのうち、1 つのジョブが完了したことを示しています。
テナント固有の制約があるジョブの送信
このセクションでは、別のジョブを送信して、ノードの自動プロビジョニングがワークロードの別の制約を満たしていることを確認します。ジョブの構成には、テナント固有のノードセレクタとテナント固有の toleration が含まれます。ジョブは、セレクタの Key-Value ペアと一致するラベルを持つノードにのみスケジュール設定できます。容認機能は Node Taints と連携して動作します。これにより、ノードにスケジュール設定できるジョブの制限も行われます。ノードの自動プロビジョニングに関するベスト プラクティスは、ノードセレクタとワークロード分離用の toleration の両方を含めることです。
セレクタの制約を満たすノードがないため、このジョブをデフォルトのノードプールにスケジュール設定できません。そのため、ノードの自動プロビジョニングにより、セレクタの要件を満たすノードラベルを持つ新しいノードプールが作成されます。ノードの自動プロビジョニングでは、ジョブ構成の toleration に一致するテナント固有の taint もノードに追加されます。一致する容認機能を備えた Pod のみをプール内のノードにスケジュール設定できます。これにより、テナント ワークロードの分離をさらに強化できます。
クラスタ内のノードプールを一覧表示します。
gcloud container node-pools list
デフォルト プールが 1 つだけ表示されます。
ジョブの構成をコンソールに出力します。
kubectl kustomize manifests/jobs/one-tenant/
この構成には、テナント固有のノードセレクタの要件と容認機能が含まれています。出力は次のようになります。
apiVersion: batch/v1 kind: Job metadata: name: tenant1-pi-job spec: ...
ジョブを送信します。
kubectl apply -k manifests/jobs/one-tenant/
クラスタ内のノードプールを監視します。
watch -n 5 gcloud container node-pools list
しばらくすると、新しいノードプールが表示されます。出力は次のようになります。
NAME MACHINE_TYPE DISK_SIZE_GB default-pool n1-standard-2 100 nap-n1-standard-1-15jwludl n1-standard-1 100
ノードプール名の先頭には
nap-
が付きます。これは、ノードの自動プロビジョニングによって作成されたことを示します。ノードプール名には、プール内のノードのマシンタイプ(例:n1-standard-1
)も含まれます。クラスタ内のノードを監視します。
kubectl get nodes -w
約 1 分後、新しいノードがリストに表示されます。ノード名には、
nap-
ノードプールの名前が含まれます。新しいノードのステータスは、最初はNot Ready
ステータスになります。しばらくすると、新しいノードのステータスがReady
に変わります。これにより、ノードは保留中の作業を承認できるようになります。ノードの監視を停止するには、
Control+C
を押します。Node Taints を一覧表示します。
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
新しいノードには、Key-Value ペア
tenant: tenant1
のNoSchedule
taint があることが確認できす。したがって、tenant: tenant1
に対応する容認機能を持つ Pod のみをノードにスケジュール設定できます。クラスタ内のジョブを監視します。
kubectl get jobs -w --all-namespaces
しばらくすると、
tenant1-pi-job
が1/1
完了となります。これは、正常に完了したことを示しています。ジョブの監視を停止するには、
Control+C
を押します。クラスタ内のノードプールを監視します。
watch -n 5 gcloud container node-pools list
しばらくすると、
nap-
プールが削除され、クラスタのデフォルト ノードプールが再び 1 つのみになったことを確認できます。ノードの自動プロビジョニングによってnap-
ノードプールが削除されました。これは、プールの制約に一致する保留中の作業がなくなったためです。ノードプールの監視を停止するには、
Control+C
を押します。
テナントに関連した制約のある 2 つの大規模なジョブを送信する
このセクションでは、テナント固有の制約を持つ 2 つのジョブを送信し、各ジョブのリソース リクエストも増やします。ここでもノードセレクタの制約により、これらのジョブはデフォルトのノードプールにスケジュール設定できません。各ジョブには独自のセレクタ制約があるため、ノードの自動プロビジョニングによって 2 つの新しいノードプールが作成されます。このようにすると、ノードの自動プロビジョニングを使用してテナントジョブを分離した状態で保持できます。ジョブには前のジョブよりも多数のリソース リクエストがあるため、ノードの自動プロビジョニングによって前回のジョブより大きなマシンタイプのノードプールが作成されます。
クラスタ内のノードプールを一覧表示します。
gcloud container node-pools list
デフォルト プールが 1 つだけ表示されます。
結合された構成を出力します。
kubectl kustomize manifests/jobs/two-tenants/
この構成には 2 つの個別のジョブがあり、それぞれにテナント固有のノードセレクタと許容値が設定され、リソース リクエストが増分されています。
出力は次のようになります。
apiVersion: batch/v1 kind: Job metadata: name: tenant1-larger-pi-job spec: ...
ジョブを送信します。
kubectl apply -k manifests/jobs/two-tenants/
クラスタ内のノードプールを監視します。
watch -n 5 gcloud container node-pools list
しばらくすると、2 つのノードプールが追加されます。出力は次のようになります。
NAME MACHINE_TYPE DISK_SIZE_GB default-pool n1-standard-2 100 nap-n1-standard-2-6jxjqobt n1-standard-2 100 nap-n1-standard-2-z3s06luj n1-standard-2 100
ノードプール名には、ノードの自動プロビジョニングによって作成されたことを示す
nap-
という接頭辞が付きます。ノードプール名には、プール内のノードのマシンタイプ(例:n1-standard-2
)も含まれます。ノードの監視を停止するには、
Control+C
を押します。クラスタ内のノードを監視します。
kubectl get nodes -w
約 1 分後、2 つのノードがリストに表示されます。ノード名には、関連付けられている
nap-
ノードプールの名前が含まれます。新しいノードのステータスは、最初はNot Ready
になります。しばらくすると、新しいノードのステータスがReady
に変わります。これにより、ノードが保留中の作業を受け入れられるようになります。ノードの監視を停止するには、
Control+C
を押します。Node Taints を一覧表示します。
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
新しいノードには
NoSchedule
taint があり、1 つは Key-Value ペアtenant: tenant1
で、もう 1 つはtenant: tenant2
であることを確認できます。対応するテナントの容認機能を備えた Pod のみをノードにスケジュール設定できます。クラスタ内のジョブを監視します。
kubectl get jobs -w --all-namespaces
しばらくすると、
tenant1-larger-pi-job
とtenant2-larger-pi-job
がそれぞれ1/1
完了に変わったことを確認できます。これは、ジョブが正常に完了したことを示します。ジョブの監視を停止するには、
Control+C
を押します。クラスタ内のノードプールを監視します。
watch -n 5 gcloud container node-pools list
しばらくすると、両方の
nap-
プールが削除され、再度クラスタのデフォルト ノードプールが 1 つのみになったことを確認できます。ノードの自動プロビジョニングによってnap-
ノードプールが削除されました。これは、プールの制約に一致する保留中の作業がなくなったためです。ノードプールの監視を停止するには、
Control+C
を押します。
Google Cloud リソースへのアクセス制御
クラスタ内のテナントを分離した状態で保持することに加えて、通常は、Cloud Storage バケットや Pub/Sub トピックなどの Google Cloud リソースへのテナント アクセスを制御する必要があります。たとえば、各テナントで他のテナントからアクセスできるように設定できない Cloud Storage バケットが必要になる場合があります。
Workload Identity を使用すると、Kubernetes サービス アカウントと Google Cloud サービス アカウント間のマッピングを作成できます。その後、適切な Identity and Access Management(IAM)ロールを Google Cloud サービス アカウントに割り当てることができます。これにより、最小権限の原則が適用され、テナントのジョブは割り当てられたリソースにアクセスできますが、他のテナントが所有するリソースにはアクセスできません。
GKE Workload Identity を設定する
Kubernetes サービス アカウントと作成する Google Cloud サービス アカウントのマッピングを構成します。
tenant1
の Google Cloud サービス アカウントを作成します。gcloud iam service-accounts create tenant1-gsa
tenant1
の Kubernetes サービス アカウントに IAM 権限を付与し、tenant1
に対応する Google Cloud サービス アカウントを使用できるようにします。gcloud iam service-accounts add-iam-policy-binding \ tenant1-gsa@${PROJECT_ID}.iam.gserviceaccount.com \ --role roles/iam.workloadIdentityUser \ --member "serviceAccount:${PROJECT_ID}.svc.id.goog[tenant1-ns/tenant1-ksa]"
Google Cloud サービス アカウントで Kubernetes サービス アカウントにアノテーションを付けて、サービス アカウント間のマッピングを完了します。
kubectl annotate serviceaccount tenant1-ksa -n tenant1-ns \ iam.gke.io/gcp-service-account=tenant1-gsa@${PROJECT_ID}.iam.gserviceaccount.com
Cloud Storage バケットへの書き込みを行うジョブを送信する
このセクションでは、特定の Kubernetes サービス アカウントとして実行されるジョブで、マッピングされた Google Cloud サービス アカウントの IAM 権限を使用できることを確認します。
tenant1
の新しい Cloud Storage バケットを作成します。export BUCKET=tenant1-$PROJECT_ID gcloud storage buckets create gs://$BUCKET --uniform-bucket-level-access --location=us-central1
一意の名前にするには、バケット名に対してプロジェクト ID をサフィックスとして使用します。
Cloud Storage バケットを使用するように、ジョブの構成ファイルを更新します。
sed -i "s/MY_BUCKET/$BUCKET/" \ manifests/jobs/write-gcs/bucket-write.yaml
tenant1
サービス アカウントにバケット内のオブジェクトへの読み取りと書き込みを行う権限を付与します。gcloud storage buckets add-iam-policy-binding \ gs://$BUCKET \ --member=serviceAccount:tenant1-gsa@$PROJECT_ID.iam.gserviceaccount.com \ --role=roles/storage.objectAdmin
ジョブ構成を出力します。
kubectl kustomize manifests/jobs/write-gcs/
出力は次のようになります。
apiVersion: batch/v1 kind: Job metadata: name: tenant1-pi-job-gcs spec: ...
新しいバケット名は引数として
generate-pi
コンテナに渡され、ジョブは適切なtenant1-ksa
Kubernetes サービス アカウントを指定します。ジョブを送信します。
kubectl apply -k manifests/jobs/write-gcs/
前のセクションと同様に、ノードの自動プロビジョニングによって、新しいノードプールと、ジョブを実行する新しいノードが作成されます。
ジョブの Pod を監視します。
kubectl get pods -n tenant1-ns -w
この場合は、ノードプールではなく Pod を監視します。Pod がさまざまなステータスに移行します。数分後、ステータスが
Completed
に変わります。このステータスは、ジョブが正常に完了したことを示します。監視を停止するには、
Control+C
を押します。ファイルが Cloud Storage バケットに書き込まれていることを確認します。
gcloud storage ls gs://$BUCKET --long
1 つのファイルが表示されます。
クリーンアップするには、ジョブを削除します。
kubectl delete job tenant1-pi-job-gcs -n tenant1-ns
このジョブは、次のセクションで再度送信します。
IAM 権限の取り消し
最後に、Google Cloud サービス アカウントから IAM 権限を取り消して、マッピングされた Kubernetes サービス アカウントが Cloud Storage バケットにアクセスできなくなったことを確認します。
Cloud Storage バケットに書き込みを行う Google Cloud サービス アカウントの権限を取り消します。
gcloud storage buckets remove-iam-policy-binding \ gs://$BUCKET \ --member=serviceAccount:tenant1-gsa@$PROJECT_ID.iam.gserviceaccount.com \ --role=roles/storage.objectAdmin
前と同じジョブを送信します。
kubectl apply -k manifests/jobs/write-gcs/
ジョブの Pod のステータスをもう一度確認します。
kubectl get pods -n tenant1-ns -w
数分後、ステータスが
Error
に変わります。これは、ジョブが失敗したことを示します。ジョブが Google Cloud サービス アカウントにマッピングされる Kubernetes サービス アカウントとして実行され、Cloud Storage バケットへの書き込み権限がなくなったため、これは予想されるエラーです。Pod の監視を停止するには、
Control+C
を押します。バケット内のファイルを一覧表示します。
gcloud storage ls gs://$BUCKET --long
バケットには単一のファイルが表示されます。新しいファイルは書き込まれていません。
クリーンアップ
課金を停止する最も簡単な方法は、チュートリアル用に作成した Google Cloud プロジェクトを削除することです。
プロジェクトを削除する
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
GKE クラスタの削除
プロジェクトを削除しない場合は、GKE クラスタを削除します。
gcloud container clusters delete multitenant
次のステップ
- GKE のマルチテナンシーについて詳細を確認する。
- クラスタ オートスケーラーについて調べる。
- Google Cloud に関するリファレンス アーキテクチャ、図、ベスト プラクティスを確認する。Cloud アーキテクチャ センターをご覧ください。