Cloud Spanner と GKE Autopilot の接続

1. はじめに

Cloud Spanner は、水平スケーリングが可能なフルマネージドでグローバルに分散されたリレーショナル データベース サービスで、パフォーマンスと高可用性を犠牲にすることなく ACID トランザクションと SQL セマンティクスを提供します。

GKE Autopilot は GKE の運用モードの一つで、ノード、スケーリング、セキュリティ、ベスト プラクティスに沿ったその他の事前構成された設定などのクラスタ構成を Google が管理します。たとえば、GKE Autopilot を使用すると、Workload Identity でサービスの権限を管理できます。

このラボの目標は、GKE Autopilot で実行されている複数のバックエンド サービスを Cloud Spanner データベースに接続するプロセスについて説明することです。

3d810aa9ec80a271.png

このラボでは、まずプロジェクトを設定して Cloud Shell を起動します。その後、Terraform を使用してインフラストラクチャをデプロイします。

完了したら、Cloud Build と Cloud Deploy を操作してゲーム データベースの初期スキーマ移行を実行し、バックエンド サービスをデプロイしてからワークロードをデプロイします。

この Codelab のサービスは、Cloud Spanner のゲーム開発のスタートガイド Codelab のものと同じです。GKE でサービスを実行して Spanner に接続するために、この Codelab を完了する必要はありません。Spanner で動作するこれらのサービスの詳細について関心をお持ちの場合は、

ワークロードとバックエンド サービスが実行されている状態で、負荷の生成を開始して、サービスが連携する様子を観察できます。

最後に、このラボで作成したリソースをクリーンアップします。

作成するアプリの概要

このラボでは、次の作業を行います。

  • Terraform を使用してインフラストラクチャをプロビジョニングする
  • Cloud Build でスキーマ移行プロセスを使用してデータベース スキーマを作成する
  • Workload Identity を利用して Cloud Spanner に接続する 4 つの Golang バックエンド サービスをデプロイする
  • バックエンド サービスの負荷のシミュレーションに使用する 4 つのワークロード サービスをデプロイします。

学習内容

  • Terraform を使用して GKE Autopilot、Cloud Spanner、Cloud Deploy パイプラインをプロビジョニングする方法
  • Workload Identity を使用して、GKE 上のサービスがサービス アカウントの権限を借用し、Cloud Spanner を操作するための IAM 権限にアクセスする仕組み
  • Locust.io を使用して GKE と Cloud Spanner で本番環境と同様の負荷を生成する方法

必要なもの

  • 請求先アカウントに接続されている Google Cloud プロジェクト。
  • ウェブブラウザ(ChromeFirefox など)

2. 設定と要件

プロジェクトを作成する

Google アカウント(Gmail または Google Apps)をお持ちでない場合は、1 つ作成する必要があります。Google Cloud Platform コンソール(console.cloud.google.com)にログインし、新しいプロジェクトを作成します。

すでにプロジェクトが存在する場合は、コンソールの左上にあるプロジェクト選択プルダウン メニューをクリックします。

6c9406d9b014760.png

[新しいプロジェクト] をクリックします。] ボタンをクリックし、新しいプロジェクトを作成します。

949d83c8a4ee17d9.png

まだプロジェクトが存在しない場合は、次のような最初のプロジェクトを作成するためのダイアログが表示されます。

870a3cbd6541ee86.png

続いて表示されるプロジェクト作成ダイアログでは、新しいプロジェクトの詳細を入力できます。

6a92c57d3250a4b3.png

プロジェクト ID を忘れないようにしてください。プロジェクト ID は、すべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているため使用できません)。以降、この Codelab では PROJECT_ID と表します。

次に、Google Cloud リソースを使用し、Cloud Spanner API を有効にするために、Developers Console で課金を有効にする必要があります。

15d0ef27a8fbab27.png

この Codelab をすべて実行しても費用はかかりませんが、より多くのリソースを使用する場合や実行したままにする場合は、コストが高くなる可能性があります(このドキュメントの最後にある「クリーンアップ」セクションをご覧ください)。Google Cloud Spanner の料金についてはこちらを、GKE Autopilot についてはこちらをご覧ください。

Google Cloud Platform の新規ユーザーの皆さんには、$300 の無料トライアルをご利用いただけます。その場合は、この Codelab を完全に無料でご利用いただけます。

Cloud Shell の設定

Google Cloud と Spanner はノートパソコンからリモートで操作でき、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。

この Debian ベースの仮想マシンには、必要な開発ツールがすべて揃っています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働するため、ネットワークのパフォーマンスと認証が大幅に向上しています。つまり、この Codelab に必要なのはブラウザだけです(はい、Chromebook で動作します)。

  1. Cloud コンソールから Cloud Shell を有効にするには、「Cloud Shell をアクティブにする」アイコン gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A をクリックします(環境のプロビジョニングと接続には少し時間がかかります)。

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

Cloud Shell に接続すると、すでに認証は完了しており、プロジェクトに各自の PROJECT_ID が設定されていることがわかります。

gcloud auth list

コマンド出力

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

コマンド出力

[core]
project = <PROJECT_ID>

なんらかの理由でプロジェクトが設定されていない場合は、次のコマンドを実行します。

gcloud config set project <PROJECT_ID>

PROJECT_ID が見つからない場合は、設定手順で使用した ID を確認するか、Cloud コンソール ダッシュボードで調べます。

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell では、デフォルトで環境変数もいくつか設定されます。これらの変数は、以降のコマンドを実行する際に有用なものです。

echo $GOOGLE_CLOUD_PROJECT

コマンド出力

<PROJECT_ID>

コードをダウンロードする

Cloud Shell で、このラボのコードをダウンロードできます。

git clone https://github.com/cloudspannerecosystem/spanner-gaming-sample.git

コマンド出力

Cloning into 'spanner-gaming-sample'...
*snip*

この Codelab は v0.1.3 リリースをベースにしているため、次のタグを確認してください。

cd spanner-gaming-sample
git fetch --all --tags

# Check out v0.1.3 release
git checkout tags/v0.1.3 -b v0.1.3-branch

コマンド出力

Switched to a new branch 'v0.1.3-branch'

次に、現在の作業ディレクトリを DEMO_HOME 環境変数として設定します。これにより、Codelab のさまざまな部分に取り組むための移動が容易になります。

export DEMO_HOME=$(pwd)

概要

このステップでは、新しいプロジェクトを設定し、Cloud Shell をアクティブにして、このラボのコードをダウンロードしました。

次のステップ

次に、Terraform を使用してインフラストラクチャをプロビジョニングします。

3. インフラストラクチャのプロビジョニング

概要

プロジェクトの準備ができたら、インフラストラクチャを稼働させます。これには、VPC ネットワーキング、Cloud Spanner、GKE Autopilot、GKE で実行するイメージを保存する Artifact Registry、バックエンド サービスとワークロード用の Cloud Deploy パイプライン、これらのサービスを使用するためのサービス アカウントと IAM 権限が含まれます。

その数は膨大です。幸いなことに、Terraform を使用すれば簡単に設定できます。Terraform は「Infrastructure as Code」このツールを使うと、このプロジェクトに必要なものを一連の「.tf」ファイルにできます。これにより、インフラストラクチャのプロビジョニングがシンプルになります。

Terraform に精通していることは、この Codelab を完了するための要件ではありません。以降のステップで何が行われているかを確認する場合は、infrastructure ディレクトリにある各ファイルに作成された内容をご覧ください。

  • vpc.tf
  • backend_gke.tf
  • spanner.tf
  • artifact_registry.tf
  • pipelines.tf
  • iam.tf

Terraform を構成する

Cloud Shell で、infrastructure ディレクトリに移動して Terraform を初期化します。

cd $DEMO_HOME/infrastructure
terraform init

コマンド出力

Initializing the backend...

Initializing provider plugins...
*snip*
Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

次に、terraform.tfvars.sample をコピーしてプロジェクトの値を変更し、Terraform を構成します。他の変数も変更できますが、実際の環境に合わせて変更する必要があるのはプロジェクトだけです。

cp  terraform.tfvars.sample terraform.tfvars
# edit gcp_project using the project environment variable
sed -i "s/PROJECT/$GOOGLE_CLOUD_PROJECT/" terraform.tfvars

インフラストラクチャをプロビジョニングする

今度はインフラストラクチャをプロビジョニングします。

terraform apply
# review the list of things to be created
# type 'yes' when asked

コマンド出力

Plan: 46 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

google_project_service.project["container.googleapis.com"]: Creating...
*snip*
Apply complete! Resources: 46 added, 0 changed, 0 destroyed.

作成された内容を確認する

作成された内容を確認するために Cloud コンソールでプロダクトを確認します。

Cloud Spanner

まず、ハンバーガー メニューに移動して Spanner をクリックし、Cloud Spanner を確認します。[その他の商品を表示] をクリックする必要がある場合があります見つけてください。

Spanner インスタンスのリストが表示されます。インスタンスをクリックすると、データベースが表示されます。次のようになります。

10b7fc0c4a86c59.png

GKE Autopilot

次に、ハンバーガー メニューに移動して Kubernetes Engine => Clusters をクリックして、GKE を確認します。ここでは、sample-games-gke クラスタが Autopilot モードで実行されていることがわかります。

9cecb1a702e6b7ff.png

Artifact Registry

次に、画像が保存される場所を確認します。ハンバーガー メニューをクリックして Artifact Registry=>Repositories を探します。Artifact Registry はメニューの [CI/CD] セクションにあります。

spanner-game-images という名前の Docker レジストリが表示されます。今は空です。

3f805eee312841b.png

Cloud Deploy

Cloud Deploy は、イメージをビルドして GKE クラスタにデプロイするステップを Cloud Build が提供できるようにパイプラインが作成された場所です。

ハンバーガー メニューに移動し、Cloud Deploy を見つけます。これは、メニューの [CI/CD] セクションにもあります。

ここには 2 つのパイプラインがあります。1 つはバックエンド サービス用、もう 1 つはワークロード用です。どちらも同じ GKE クラスタにイメージをデプロイしますが、これによってデプロイを分離できます。

d2e4a659145ddf5e.png

IAM

最後に、Cloud コンソールの [IAM] ページで、作成されたサービス アカウントを確認します。ハンバーガー メニューに移動して、IAM and Admin=>Service accounts を見つけます。次のようになります。

bed3d1af94974916.png

Terraform によって作成されるサービス アカウントは合計で 6 つあります。

  • デフォルトのコンピュータ サービス アカウント。これは、この Codelab では使用しません。
  • cloudbuild-cicd アカウントは、Cloud Build と Cloud Deploy のステップで使用されます。
  • 4 つの「アプリ」バックエンド サービスが Cloud Spanner とやり取りするために使用するアカウント。

次に、GKE クラスタとやり取りするように kubectl を構成します。

kubectl を構成する

# Name of GKE cluster from terraform.tfvars file
export GKE_CLUSTER=sample-game-gke 

# get GKE credentials
gcloud container clusters get-credentials $GKE_CLUSTER --region us-central1

# Check that no errors occur
kubectl get serviceaccounts

コマンド出力

#export GKE_CLUSTER=sample-game-gke

# gcloud container clusters get-credentials $GKE_CLUSTER --region us-central1
Fetching cluster endpoint and auth data.
kubeconfig entry generated for sample-game-gke.

# kubectl get serviceaccounts
NAME              SECRETS   AGE
default           0         37m
item-app          0         35m
matchmaking-app   0         35m
profile-app       0         35m
tradepost-app     0         35m

概要

これでプライベート ネットワーキング用に、Cloud Spanner インスタンスと GKE Autopilot クラスタをすべて VPC にプロビジョニングすることができました。

さらに、バックエンド サービスとワークロード用に 2 つの Cloud Deploy パイプラインと、ビルドされたイメージを保存する Artifact Registry リポジトリが作成されました。

最後に、Workload Identity と連携するようにサービス アカウントを作成して構成し、バックエンド サービスが Cloud Spanner を使用できるようにしました。

また、バックエンド サービスとワークロードをデプロイした後、Cloud Shell で GKE クラスタを操作するように kubectl が構成されています。

次のステップ

サービスを使用する前に、データベース スキーマを定義する必要があります。次はこれを設定します。

4. データベース スキーマを作成する

概要

バックエンド サービスを実行する前に、データベース スキーマが設定されていることを確認する必要があります。

デモ リポジトリの $DEMO_HOME/schema/migrations ディレクトリのファイルを見ると、スキーマを定義する一連の .sql ファイルがあることがわかります。これは、スキーマの変更がリポジトリ自体で追跡され、アプリケーションの特定の機能に関連付けることができる開発サイクルを模倣しています。

このサンプル環境では、Cloud Build を使用したスキーマ移行を適用するツールです。

Cloud Build

$DEMO_HOME/schema/cloudbuild.yaml ファイルには、実行する手順が記述されています。

serviceAccount: projects/${PROJECT_ID}/serviceAccounts/cloudbuild-cicd@${PROJECT_ID}.iam.gserviceaccount.com
steps:
- name: gcr.io/cloud-builders/curl
 id: fetch-wrench
 args: ['-Lo', '/workspace/wrench.tar.gz', 'https://github.com/cloudspannerecosystem/wrench/releases/download/v1.4.1/wrench-1.4.1-linux-amd64.tar.gz' ]

- name: gcr.io/cloud-builders/gcloud
 id: migrate-spanner-schema
 entrypoint: sh
 args:
 - '-xe'
 - '-c'
 - |
   tar -xzvf wrench.tar.gz

   chmod +x /workspace/wrench

   # Assumes only a single spanner instance and database. Fine for this demo in a dedicated project
   export SPANNER_PROJECT_ID=${PROJECT_ID}
   export SPANNER_INSTANCE_ID=$(gcloud spanner instances list | tail -n1 | awk '{print $1}')
   export SPANNER_DATABASE_ID=$(gcloud spanner databases list --instance=$$SPANNER_INSTANCE_ID | tail -n1 | awk '{print $1}')

   if [ -d ./migrations ]; then
     /workspace/wrench migrate up --directory .
   else
     echo "[Error] Missing migrations directory"
   fi
timeout: 600s

基本的には、次の 2 つのステップがあります。

  • Cloud Build ワークスペースにレンチをダウンロードする
  • レンチの移行を実行して

Spanner のプロジェクト、インスタンス、データベースの環境変数は、レンチが書き込みエンドポイントに接続するために必要です。

Cloud Build は cloudbuild-cicd@${PROJECT_ID}.iam.gserviceaccount.com サービス アカウントとして実行されているため、これらの変更を行うことができます。

serviceAccount: projects/${PROJECT_ID}/serviceAccounts/cloudbuild-cicd@${PROJECT_ID}.iam.gserviceaccount.com

このサービス アカウントには Terraform によって追加された spanner.databaseUser ロールがあるため、サービス アカウントで DDL をアップデートできます。

スキーマの移行

$DEMO_HOME/schema/migrations ディレクトリのファイルに基づいて実行される 5 つの移行手順があります。players テーブルとインデックスを作成する 000001.sql ファイルの例を次に示します。

CREATE TABLE players (
   playerUUID STRING(36) NOT NULL,
   player_name STRING(64) NOT NULL,
   email STRING(MAX) NOT NULL,
   password_hash BYTES(60) NOT NULL,
   created TIMESTAMP,
   updated TIMESTAMP,
   stats JSON,
   account_balance NUMERIC NOT NULL DEFAULT (0.00),
   is_logged_in BOOL,
   last_login TIMESTAMP,
   valid_email BOOL,
   current_game STRING(36)
) PRIMARY KEY (playerUUID);

CREATE UNIQUE INDEX PlayerAuthentication ON players(email) STORING(password_hash);
CREATE UNIQUE INDEX PlayerName ON players(player_name);
CREATE INDEX PlayerGame ON players(current_game);

スキーマの移行を送信する

ビルドを送信してスキーマ移行を実行するには、schema ディレクトリに切り替えて、次の gcloud コマンドを実行します。

cd $DEMO_HOME/schema
gcloud builds submit --config=cloudbuild.yaml

コマンド出力

Creating temporary tarball archive of 8 file(s) totalling 11.2 KiB before compression.
Uploading tarball of [.] to [gs://(project)_cloudbuild/source/(snip).tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/(project)/locations/global/builds/7defe982-(snip)].
Logs are available at [ https://console.cloud.google.com/cloud-build/builds/7defe982-(snip)?project=(snip) ].

gcloud builds submit only displays logs from Cloud Storage. To view logs from Cloud Logging, run:
gcloud beta builds submit

ID: 7defe982-(snip)
CREATE_TIME: (created time)
DURATION: 3M11S
SOURCE: gs://(project)_cloudbuild/source/(snip).tgz
IMAGES: -
STATUS: SUCCESS

上記の出力には、Created Cloud Build プロセスへのリンクが含まれています。これをクリックすると、Cloud コンソールのビルドが表示され、ビルドの進捗状況をモニタリングして状況を確認できます。

11b1cf107876d797.png

概要

このステップでは、Cloud Build を使用して、5 つの異なる DDL オペレーションを適用した最初のスキーマ移行を送信しました。これらのオペレーションは、データベース スキーマの変更を必要とする特徴がいつ追加されたかを表します。

通常の開発シナリオでは、サービス停止を避けるために、現在のアプリケーションと下位互換性のあるスキーマ変更を行います。

下位互換性のない変更については、アプリケーションとスキーマへの変更を段階的にデプロイして、サービス停止を防ぐことをおすすめします。

次のステップ

スキーマを配置したら、次はバックエンド サービスをデプロイします。

5. バックエンド サービスをデプロイする

概要

この Codelab のバックエンド サービスは、次の 4 種類のサービスを表す golang REST API です。

  • プロフィール: サンプルの「ゲーム」に登録して認証する機能をプレーヤーに提供します。
  • マッチメイキング: プレーヤーのデータを操作してマッチメイキング機能を支援し、作成されたゲームに関する情報を追跡し、ゲームが終了したときにプレーヤーの統計情報を更新します。
  • アイテム: プレーヤーがゲームのプレイ中にゲームのアイテムとお金を獲得できるようにします。
  • Tradepost: プレーヤーがトレードポストでアイテムを売買できます

d36e958411d44b5d.png

これらのサービスについて詳しくは、Cloud Spanner でゲーム開発を始めるの Codelab をご覧ください。ここでの目的上、これらのサービスを GKE Autopilot クラスタで実行する必要があります。

これらのサービスは Spanner データを変更できる必要があります。そのために、各サービスに「databaseUser」というサービス アカウントをなります。

Workload Identity を使用すると、Kubernetes サービス アカウントがサービスの権限をGoogle Cloud サービス アカウントを作成する必要があります。

  • サービスの Google Cloud サービス アカウント(GSA)リソースを作成する
  • そのサービス アカウントに databaseUser ロールを割り当てます。
  • そのサービス アカウントに workloadIdentityUser ロールを割り当てる。
  • GSA を参照する Kubernetes サービス アカウント(KSA)を作成します

大まかな図は次のようになります。

a8662d31d66b5910.png

サービス アカウントと Kubernetes サービス アカウントは Terraform によって作成されました。Kubernetes サービス アカウントは、kubectl を使用して確認できます。

# kubectl get serviceaccounts
NAME              SECRETS   AGE
default           0         37m
item-app          0         35m
matchmaking-app   0         35m
profile-app       0         35m
tradepost-app     0         35m

ビルドの仕組みは次のとおりです。

serviceAccount: projects/${PROJECT_ID}/serviceAccounts/cloudbuild-cicd@${PROJECT_ID}.iam.gserviceaccount.com
steps:

#
# Building of images
#
 - name: gcr.io/cloud-builders/docker
   id: profile
   args: ["build", ".", "-t", "${_PROFILE_IMAGE}"]
   dir: profile
   waitFor: ['-']
 - name: gcr.io/cloud-builders/docker
   id: matchmaking
   args: ["build", ".", "-t", "${_MATCHMAKING_IMAGE}"]
   dir: matchmaking
   waitFor: ['-']
 - name: gcr.io/cloud-builders/docker
   id: item
   args: ["build", ".", "-t", "${_ITEM_IMAGE}"]
   dir: item
   waitFor: ['-']
 - name: gcr.io/cloud-builders/docker
   id: tradepost
   args: ["build", ".", "-t", "${_TRADEPOST_IMAGE}"]
   dir: tradepost
   waitFor: ['-']

#
# Deployment
#
 - name: gcr.io/google.com/cloudsdktool/cloud-sdk
   id: cloud-deploy-release
   entrypoint: gcloud
   args:
     [
       "deploy", "releases", "create", "${_REL_NAME}",
       "--delivery-pipeline", "sample-game-services",
       "--skaffold-file", "skaffold.yaml",
       "--skaffold-version", "1.39",
       "--images", "profile=${_PROFILE_IMAGE},matchmaking=${_MATCHMAKING_IMAGE},item=${_ITEM_IMAGE},tradepost=${_TRADEPOST_IMAGE}",
       "--region", "us-central1"
     ]

artifacts:
 images:
   - ${_REGISTRY}/profile
   - ${_REGISTRY}/matchmaking
   - ${_REGISTRY}/item
   - ${_REGISTRY}/tradepost

substitutions:
 _PROFILE_IMAGE: ${_REGISTRY}/profile:${BUILD_ID}
 _MATCHMAKING_IMAGE: ${_REGISTRY}/matchmaking:${BUILD_ID}
 _ITEM_IMAGE: ${_REGISTRY}/item:${BUILD_ID}
 _TRADEPOST_IMAGE: ${_REGISTRY}/tradepost:${BUILD_ID}
 _REGISTRY: us-docker.pkg.dev/${PROJECT_ID}/spanner-game-images
 _REL_NAME: rel-${BUILD_ID:0:8}
options:
 dynamic_substitutions: true
 machineType: E2_HIGHCPU_8
 logging: CLOUD_LOGGING_ONLY
  • Cloud Build コマンドはこのファイルを読み取り、記載されている手順に従います。まず、サービス イメージをビルドします。次に、gcloud deploy create コマンドを実行します。これにより、各デプロイ ファイルが配置されている場所を定義する $DEMO_HOME/backend_services/skaffold.yaml ファイルが読み取られます。
apiVersion: skaffold/v2beta29
kind: Config
deploy:
 kubectl:
   manifests:
     - spanner_config.yaml
     - profile/deployment.yaml
     - matchmaking/deployment.yaml
     - item/deployment.yaml
     - tradepost/deployment.yaml
  • Cloud Deploy は、各サービスの deployment.yaml ファイルの定義に従います。Service のデプロイ ファイルには、Service を作成するための情報が含まれています。この場合は、ポート 80 で実行されている clusterIP です。

<ph type="x-smartling-placeholder"></ph>"ClusterIP" タイプは、バックエンド サービス Pod が外部 IP を持つことがなくなるため、内部 GKE ネットワークに接続できるエンティティのみがバックエンド サービスにアクセスできます。プレーヤーは、Spanner データにアクセスして変更するため、これらのサービスにはプレーヤーが直接アクセスできるようにすべきではありません。

apiVersion: v1
kind: Service
metadata:
 name: profile
spec:
 type: ClusterIP
 selector:
   app: profile
 ports:
 - port: 80
   targetPort: 80

Cloud Deploy は、Kubernetes Service を作成するだけでなく、Kubernetes Deployment も作成します。profile サービスのデプロイ セクションを見てみましょう。

---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: profile
spec:
 replicas: 2 # EDIT: Number of instances of deployment
 selector:
   matchLabels:
     app: profile
 template:
   metadata:
     labels:
       app: profile
   spec:
     serviceAccountName: profile-app
     containers:
     - name: profile-service
       image: profile
       ports:
         - containerPort: 80
       envFrom:
         - configMapRef:
             name: spanner-config
       env:
         - name: SERVICE_HOST
           value: "0.0.0.0"
         - name: SERVICE_PORT
           value: "80"
       resources:
         requests:
           cpu: "1"
           memory: "1Gi"
           ephemeral-storage: "100Mi"
         limits:
           cpu: "1"
           memory: "1Gi"
           ephemeral-storage: "100Mi"

上部には、サービスに関するメタデータが表示されます。ここで最も重要なのは、この Deployment によって作成されるレプリカの数を定義することです。

replicas: 2 # EDIT: Number of instances of deployment

次に、アプリを実行するサービス アカウントと使用するイメージを確認します。これらは、Terraform から作成された Kubernetes サービス アカウントと、Cloud Build ステップで作成されたイメージと一致します。

spec:
  serviceAccountName: profile-app
  containers:
    - name: profile-service
      image: profile

その後、ネットワーキングと環境変数に関する情報を指定します。

spanner_config は、アプリケーションが Spanner に接続するために必要なプロジェクト、インスタンス、データベースの情報を指定する Kubernetes ConfigMap です。

apiVersion: v1
kind: ConfigMap
metadata:
  name: spanner-config
data:
  SPANNER_PROJECT_ID: ${project_id}
  SPANNER_INSTANCE_ID: ${instance_id}
  SPANNER_DATABASE_ID: ${database_id}
ports:
  - containerPort: 80
envFrom:
  - configMapRef:
    name: spanner-config
env:
  - name: SERVICE_HOST
    value: "0.0.0.0"
  - name: SERVICE_PORT
    value: "80"

SERVICE_HOSTSERVICE_PORT は、サービスがバインドする場所を把握するために必要な追加の環境変数です。

最後のセクションでは、このデプロイの各レプリカで許可されるリソースの数を GKE に指示します。これは、GKE Autopilot が必要に応じてクラスタをスケーリングするためにも使用されます。

resources:
  requests:
    cpu: "1"
    memory: "1Gi"
    ephemeral-storage: "100Mi"
  limits:
    cpu: "1"
    memory: "1Gi"
    ephemeral-storage: "100Mi"

この情報をもとに、バックエンド サービスをデプロイします。

バックエンド サービスをデプロイする

前述のように、バックエンド サービスのデプロイでは Cloud Build を使用します。スキーマ移行の場合と同様に、gcloud コマンドラインを使用してビルド リクエストを送信できます。

cd $DEMO_HOME/backend_services
gcloud builds submit --config=cloudbuild.yaml

コマンド出力

Creating temporary tarball archive of 66 file(s) totalling 864.6 KiB before compression.
Uploading tarball of [.] to [gs://(project)_cloudbuild/source/(snip).tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/(project)/locations/global/builds/30207dd1-(snip)].
Logs are available at [ https://console.cloud.google.com/cloud-build/builds/30207dd1-(snip)?project=(snip) ].

gcloud builds submit only displays logs from Cloud Storage. To view logs from Cloud Logging, run:
gcloud beta builds submit

ID: 30207dd1-(snip)
CREATE_TIME: (created time)
DURATION: 3M17S
SOURCE: gs://(project)_cloudbuild/source/(snip).tgz
IMAGES: us-docker.pkg.dev/(project)/spanner-game-images/profile:30207dd1-(snip) (+3 more)
STATUS: SUCCESS

schema migration ステップの出力とは異なり、このビルドの出力は、いくつかのイメージが作成されたことを示しています。これらは Artifact Registry リポジトリに保存されます。

gcloud build ステップの出力には、Cloud コンソールへのリンクが含まれています。ご覧ください。

Cloud Build から成功通知を受け取ったら、Cloud Deploy に移動してから sample-game-services パイプラインに移動し、デプロイの進行状況をモニタリングします。

df5c6124b9693986.png

Service がデプロイされたら、kubectl で Pod のstatus:

kubectl get pods

コマンド出力

NAME                           READY   STATUS    RESTARTS   AGE
item-6b9d5f678c-4tbk2          1/1     Running   0          83m
matchmaking-5bcf799b76-lg8zf   1/1     Running   0          80m
profile-565bbf4c65-kphdl       1/1     Running   0          83m
profile-565bbf4c65-xw74j       1/1     Running   0          83m
tradepost-68b87ccd44-gw55r     1/1     Running   0          79m

次に、サービスを調べて ClusterIP が動作していることを確認します。

kubectl get services

コマンド出力

NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
item          ClusterIP   10.172.XXX.XXX   <none>        80/TCP    84m
kubernetes    ClusterIP   10.172.XXX.XXX   <none>        443/TCP   137m
matchmaking   ClusterIP   10.172.XXX.XXX   <none>        80/TCP    84m
profile       ClusterIP   10.172.XXX.XXX   <none>        80/TCP    84m
tradepost     ClusterIP   10.172.XXX.XXX   <none>        80/TCP    84m

Cloud コンソールで GKE UI に移動して、WorkloadsServicesConfigMaps を表示することもできます。

ワークロード

da98979ae49e5a30.png

サービス

406ca2fe7ad4818b.png

ConfigMap

a0ebd34ee735ee11.png

3b9ef91c77a4e7f0.png

概要

このステップでは、4 つのバックエンド サービスを GKE Autopilot にデプロイしました。Cloud Build のステップを実行し、Cloud Deploy と Kubernetes の Cloud コンソールから進行状況を確認できました。

また、これらのサービスが Workload Identity を利用して、Spanner データベースに対してデータの読み取りと書き込みを行うための適切な権限を持つサービス アカウントの権限を借用する方法についても学びました。

次のステップ

次のセクションでは、ワークロードをデプロイします。

6. ワークロードをデプロイする

概要

バックエンド サービスがクラスタで実行されるようになったので、ワークロードをデプロイします。

dd900485e2eeb611.png

ワークロードには外部からアクセスでき、この Codelab の目的上、バックエンド サービスごとにワークロードが 1 つあります。

これらのワークロードは、これらのサンプル サービスで想定される実際のアクセス パターンを模倣した、Locust ベースの負荷生成スクリプトです。

Cloud Build プロセス用のファイルは次のとおりです。

  • $DEMO_HOME/workloads/cloudbuild.yaml(Terraform によって生成)
  • $DEMO_HOME/workloads/skaffold.yaml
  • ワークロードごとに deployment.yaml ファイル

ワークロードの deployment.yaml ファイルは、バックエンド サービスのデプロイ ファイルとは若干異なります。

matchmaking-workload の例を次に示します。

apiVersion: v1
kind: Service
metadata:
 name: matchmaking-workload
spec:
 type: LoadBalancer
 selector:
   app: matchmaking-workload
 ports:
 - port: 8089
   targetPort: 8089
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: matchmaking-workload
spec:
 replicas: 1 # EDIT: Number of instances of deployment
 selector:
   matchLabels:
     app: matchmaking-workload
 template:
   metadata:
     labels:
       app: matchmaking-workload
   spec:
     serviceAccountName: default
     containers:
     - name: matchmaking-workload
       image: matchmaking-workload
       ports:
         - containerPort: 8089
       resources:
         requests:
           cpu: "500m"
           memory: "512Mi"
           ephemeral-storage: "100Mi"
         limits:
           cpu: "500m"
           memory: "512Mi"
           ephemeral-storage: "100Mi"

ファイルの上部でサービスが定義されます。この場合、LoadBalancer が作成され、ワークロードはポート 8089 で実行されます。

LoadBalancer により、ワークロードへの接続に使用できる外部 IP が提供されます。

apiVersion: v1
kind: Service
metadata:
 name: matchmaking-workload
spec:
 type: LoadBalancer
 selector:
   app: matchmaking-workload
 ports:
 - port: 8089
   targetPort: 8089

Deployment セクションの一番上には、ワークロードに関するメタデータが表示されます。この例では、レプリカは 1 つだけデプロイされます。

replicas: 1 

ただし、コンテナの仕様は異なります。たとえば、Kubernetes サービス アカウントとして default を使用しています。ワークロードは GKE クラスタで実行されているバックエンド サービス以外の Google Cloud リソースに接続する必要はないため、このアカウントには特別な権限はありません。

もう一つの違いは、これらのワークロードには環境変数が必要ないことです。その結果、デプロイの仕様が短くなります。

spec:
  serviceAccountName: default
  containers:
    - name: matchmaking-workload
      image: matchmaking-workload
  ports:
    - containerPort: 8089

リソースの設定はバックエンド サービスと同様です。これにより、GKE Autopilot はクラスタ上で実行されているすべての Pod のリクエストを満たすために必要なリソースの数が認識されます。

ワークロードのデプロイに進みましょう。

ワークロードをデプロイする

以前と同様に、gcloud コマンドラインを使用してビルド リクエストを送信できます。

cd $DEMO_HOME/workloads
gcloud builds submit --config=cloudbuild.yaml

コマンド出力

Creating temporary tarball archive of 18 file(s) totalling 26.2 KiB before compression.
Some files were not included in the source upload.

Check the gcloud log [/tmp/tmp.4Z9EqdPo6d/logs/(snip).log] to see which files and the contents of the
default gcloudignore file used (see `$ gcloud topic gcloudignore` to learn
more).

Uploading tarball of [.] to [gs://(project)_cloudbuild/source/(snip).tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/(project)/locations/global/builds/(snip)].
Logs are available at [ https://console.cloud.google.com/cloud-build/builds/0daf20f6-(snip)?project=(snip) ].

gcloud builds submit only displays logs from Cloud Storage. To view logs from Cloud Logging, run:
gcloud beta builds submit

ID: 0daf20f6-(snip)
CREATE_TIME: (created_time)
DURATION: 1M41S
SOURCE: gs://(project)_cloudbuild/source/(snip).tgz
IMAGES: us-docker.pkg.dev/(project)/spanner-game-images/profile-workload:0daf20f6-(snip) (+4 more)
STATUS: SUCCESS

Cloud コンソールで Cloud Build のログと Cloud Deploy パイプラインをチェックして、ステータスを確認してください。ワークロードの場合、Cloud Deploy パイプラインは sample-game-workloads です。

デプロイが完了したら、Cloud Shell で kubectl を使用してステータスを確認します。

kubectl get pods

コマンド出力

NAME                                    READY   STATUS    RESTARTS   AGE
game-workload-7ff44cb657-pxxq2          1/1     Running   0          12m
item-6b9d5f678c-cr29w                   1/1     Running   0          9m6s
item-generator-7bb4f57cf8-5r85b         1/1     Running   0          12m
matchmaking-5bcf799b76-lg8zf            1/1     Running   0          117m
matchmaking-workload-76df69dbdf-jds9z   1/1     Running   0          12m
profile-565bbf4c65-kphdl                1/1     Running   0          121m
profile-565bbf4c65-xw74j                1/1     Running   0          121m
profile-workload-76d6db675b-kzwng       1/1     Running   0          12m
tradepost-68b87ccd44-gw55r              1/1     Running   0          116m
tradepost-workload-56c55445b5-b5822     1/1     Running   0          12m

ワークロード サービスで LoadBalancer が動作していることを確認します。

kubectl get services 

コマンド出力

NAME                   TYPE          CLUSTER-IP  EXTERNAL-IP     PORT(S)        AGE
game-workload          LoadBalancer  *snip*      35.XX.XX.XX   8089:32483/TCP   12m
item                   ClusterIP     *snip*      <none>         80/TCP          121m
item-generator         LoadBalancer  *snip*      34.XX.XX.XX   8089:32581/TCP   12m
kubernetes             ClusterIP     *snip*      <none>          443/TCP        174m
matchmaking            ClusterIP     *snip*      <none>          80/TCP         121m
matchmaking-workload   LoadBalancer  *snip*      34.XX.XX.XX   8089:31735/TCP   12m
profile                ClusterIP     *snip*      <none>          80/TCP         121m
profile-workload       LoadBalancer  *snip*      34.XX.XX.XX   8089:32532/TCP   12m
tradepost              ClusterIP     *snip*      <none>          80/TCP         121m
tradepost-workload     LoadBalancer  *snip*      34.XX.XX.XX   8089:30002/TCP   12m

概要

これで、ワークロードを GKE クラスタにデプロイできました。これらのワークロードには追加の IAM 権限は必要なく、外部からは LoadBalancer サービスを使用してポート 8089 でアクセスできます。

次のステップ

バックエンド サービスとワークロードを実行したら、次は「再生」終了です。

7. ゲームのプレイを開始

概要

サンプル「ゲーム」のバックエンド サービスさらに「プレーヤー」を生成する手段もやり取りできます

各ワークロードは Locust を使用して、Google のサービス API に対する実際の負荷をシミュレートします。このステップでは、いくつかのワークロードを実行して GKE クラスタと Spanner で負荷を発生させ、Spanner にデータを保存します。

各ワークロードの説明は次のとおりです。

  • item-generator ワークロードは、プレーヤーが「プレイ中」の過程で獲得できる game_items のリストを生成する簡単なワークロードです。できます。
  • profile-workload は、プレーヤーの登録とログインをシミュレートします。
  • matchmaking-workload は、プレーヤーがゲームに割り当てられるためにキューに入れられる状態をシミュレートします。
  • game-workload は、プレーヤーがゲームのプレイ中に game_items とお金を獲得する行為をシミュレートします。
  • tradepost-workload は、プレーヤーが取引ポストでアイテムを売買できる状態をシミュレートします。

この Codelab では、特に item-generatorprofile-workload の実行について説明します。

アイテム生成ツールを実行する

item-generator は、item バックエンド サービス エンドポイントを使用して、game_items を Spanner に追加します。これらの項目は、game-workloadtradepost-workload が正常に機能するために必要です。

まず、item-generator サービスの外部 IP を取得します。Cloud Shell で次のコマンドを実行します。

# The external IP is the 4th column of the output
kubectl get services | grep item-generator | awk '{print $4}'

コマンド出力

{ITEMGENERATOR_EXTERNAL_IP}

新しいブラウザタブを開いて、http://{ITEMGENERATOR_EXTERNAL_IP}:8089 を指定します。次のようなページが表示されます。

817307157d66c661.png

usersspawn はデフォルトの 1 のままにします。host, に「http://item」と入力します。詳細オプションをクリックし、実行時間に「10s」と入力します。

構成は次のようになります。

f3143165c6285c21.png

「Start swarming」をクリック!

POST /items エンドポイントで発行されているリクエストの統計情報の表示が開始されます。10 秒後に読み込みが停止します。

Charts をクリックすると、これらのリクエストのパフォーマンスに関するグラフがいくつか表示されます。

abad0a9f3c165345.png

ここで、データが Spanner データベースに入力されているかどうかを確認します。

そのためには、ハンバーガー メニューをクリックして [Spanner] に移動します。このページから、sample-instancesample-database に移動します。[Query] をクリックします。

game_items の数を選択します。:

SELECT COUNT(*) FROM game_items;

下部に結果が表示されます。

137ce291a2ff2706.png

game_items のシードはあまり必要ありません。ですが、プレーヤーはすぐに手に入れられるようになりました。

プロファイル ワークロードを実行する

game_items をシード化したら、次のステップは、プレーヤーがゲームをプレイできるように登録することです。

profile-workload は Locust を使用して、プレーヤーがアカウントの作成、ログイン、プロフィール情報の取得、ログアウトをシミュレートします。これらはすべて、一般的な本番環境に似たワークロードで profile バックエンド サービスのエンドポイントをテストします。

これを実行するには、profile-workload 外部 IP を取得します。

# The external IP is the 4th column of the output
kubectl get services | grep profile-workload | awk '{print $4}'

コマンド出力

{PROFILEWORKLOAD_EXTERNAL_IP}

新しいブラウザタブを開いて、http://{PROFILEWORKLOAD_EXTERNAL_IP}:8089 を指定します。前のページと同様の Locust ページが表示されます。

この場合は、ホストに http://profile を使用します。また、詳細オプションでランタイムは指定しません。また、users を 4 に指定すると、一度に 4 つのユーザー リクエストがシミュレートされます。

profile-workload テストは次のようになります。

f6e0f06efb0ad6e.png

「Start swarming」をクリック!

以前と同様に、さまざまな profile REST エンドポイントの統計情報が表示されるようになります。グラフをクリックすると、すべてのパフォーマンスが表示されます。

4c2146e1cb3de23e.png

概要

このステップでは、game_items を生成し、Cloud コンソールの Spanner クエリ UI を使用して game_items テーブルに対してクエリを実行しました。

また、プレーヤーがゲームに登録できるようにして、Locust がバックエンド サービスに対して本番環境と同様のワークロードを作成する方法を確認しました。

次のステップ

ワークロードを実行したら、GKE クラスタと Spanner インスタンスの動作を確認します。

8. GKE と Spanner の使用状況を確認する

プロファイル サービスを実行したところで、今度は GKE Autopilot クラスタと Cloud Spanner の動作を確認します。

GKE クラスタを確認する

Kubernetes クラスタに移動します。ワークロードとサービスをデプロイしたため、vCPU とメモリの合計に関する詳細がクラスタに追加されています。クラスタにワークロードがない場合、この情報は利用できませんでした。

61d2d766c1f10079.png

次に、sample-game-gke クラスタをクリックして、[オブザーバビリティ] タブに切り替えます。

fa9acc7e26ea04a.png

ワークロードとバックエンド サービスは default で実行されるため、default Kubernetes Namespace の CPU 使用率は kube-system Namespace を超えているはずです。更新されていない場合は、profile workload が引き続き実行されていることを確認し、グラフが更新されるまで数分待ちます。

どのワークロードが最もリソースを消費しているかを確認するには、Workloads ダッシュボードに移動します。

各ワークロードを個別に確認するのではなく、ダッシュボードの [オブザーバビリティ] タブに移動します。profileprofile-workload の CPU が増加したことがわかります。

f194b618969cfa9e.png

では、Cloud Spanner を確認しましょう。

Cloud Spanner インスタンスを確認する

Cloud Spanner のパフォーマンスを確認するには、Spanner に移動し、sample-instance インスタンスと sample-game データベースをクリックします。

左側のメニューに [システム分析情報] タブが表示されます。

216212182a57dfd1.png

Spanner インスタンスの全般的なパフォーマンスの把握に役立つグラフは、CPU utilizationtransaction latency and lockingquery throughput など多数あります。

システム分析情報に加えて、[オブザーバビリティ] セクションの他のリンクを調べると、クエリ ワークロードに関する詳細情報を取得できます。

  • Query Insights は、Spanner のリソースを使用している上位 N 個のクエリを特定するのに役立ちます。
  • トランザクションとロックの分析情報は、レイテンシの高いトランザクションの特定に役立ちます。
  • Key Visualizer でアクセス パターンを可視化し、データ内のホットスポットを特定できます。

概要

このステップでは、GKE Autopilot と Spanner の基本的なパフォーマンス指標を確認する方法を学習しました。

たとえば、プロファイル ワークロードが実行されている状態で、players テーブルに対してクエリを実行し、テーブルに保存されているデータに関する詳細情報を取得します。

次のステップ

次はクリーンアップです。

9. クリーンアップ

クリーンアップを行う前に、取り上げられていない他のワークロードを自由に確認してください。具体的には matchmaking-workloadgame-workloadtradepost-workload です。

「プレイ」が終わったらプレイグラウンドをきれいにすることができます。これはとても簡単です。

まず、ブラウザで profile-workload がまだ実行中の場合は、それを実行して停止します。

13ae755a11f3228.png

テストしたワークロードごとに、同じことを行います。

次に、Cloud Shell でインフラストラクチャ フォルダに移動します。Terraform を使用してインフラストラクチャを destroy します。

cd $DEMO_HOME/infrastructure
terraform destroy
# type 'yes' when asked

コマンド出力

Plan: 0 to add, 0 to change, 46 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

*snip*

Destroy complete! Resources: 46 destroyed.

Cloud コンソールで、SpannerKubernetes ClusterArtifact RegistryCloud DeployIAM に移動して、すべてのリソースが削除されたことを確認します。

10. 完了

これで、サンプルの golang アプリケーションを GKE Autopilot にデプロイし、Workload Identity を使用して Cloud Spanner に接続できました。

さらに、このインフラストラクチャは Terraform を使用して繰り返し可能な方法で簡単に設定、削除できました。

この Codelab で操作した Google Cloud サービスの詳細を確認できます。

次のステップ

GKE Autopilot と Cloud Spanner がどのように連携するのかについて基本をご理解いただけたと思います。次は、これらのサービスと連携する独自のアプリケーションを構築してみましょう。