Cloud Deploy でデプロイ後に統合テストを実行する
概要
Cloud Deploy にはデプロイした後に検証する機能(VERIFY)があり、これは統合テストを実施するのに役立ちます。
本記事では認証付き Cloud Run に対して、APIテストを実行できるような Cloud Deploy の設定を紹介します。
前提
Cloud Deployの基本的な使い方についてはこちらを参照ください。
検証機能の使い方
3ステップで説明します。
- 検証機能を有効化する
- 検証内容を記述する
- 必要な権限を設定する
1.検証機能を有効化する
デリバリーパイプラインとターゲット定義を clouddeploy.yaml
に記述している場合、次の内容を記載します。
-
serialPipeline.stages.strategy.standard
にverify: true
を設定 -
executionConfigs.usages
にVERIFY
フェーズを追加
apiVersion: deploy.cloud.google.com/v1
kind: DeliveryPipeline
metadata:
name: run-verify-pipeline
description: main application pipeline
serialPipeline:
stages:
- targetId: run-verify-target
profiles: [prod]
# 検証機能を有効化する
strategy:
standard:
verify: true
---
apiVersion: deploy.cloud.google.com/v1
kind: Target
metadata:
name: run-verify-target
description: Cloud Run production service
executionConfigs:
- usages:
- RENDER
- DEPLOY
- VERIFY # VERIFYフェーズを追加
serviceAccount: my-cloud-deploy@sample-project.iam.gserviceaccount.com
artifactStorage: gs://clouddeploy_bucket
executionTimeout: 3600s
run:
location: projects/sample-project/locations/asia-northeast1
参考:構成スキーマリファレンス
次のコマンドで上記内容を反映します。
gcloud deploy apply --file=clouddeploy.yaml \
--project=$(PROJECT) \
--region=$(REGION)
Terraform の場合
デリバリーパイプラインとターゲット定義をyamlではなく、Terraform で管理している場合は次のように設定します。
resource "google_clouddeploy_delivery_pipeline" "run-verify-pipeline" {
location = "asia-northeast1"
name = "run-verify-pipeline"
description = "verify検証用のデリバリーパイプライン"
project = "sample-project"
serial_pipeline {
stages {
# 検証機能を有効化する
strategy {
standard {
verify = true
}
}
profiles = ["prod"]
target_id = google_clouddeploy_target.verify_target.target_id
}
}
}
resource "google_clouddeploy_target" "verify_target" {
location = "asia-northeast1"
name = "run-verify-target"
description = "verify検証用のターゲット"
execution_configs {
artifact_storage = google_storage_bucket.cloud_deploy.url
service_account = google_service_account.cloud_deploy.email
usages = [
"RENDER",
"DEPLOY",
"VERIFY" # VERIFYフェーズを追加
]
}
run {
location = "projects/sample-project/locations/asia-northeast1"
}
}
2.検証内容を記述する
skaffold.yaml
に verify
フェーズを追加して、実行するコンテナやコマンドを記述します。
ここでは Cloud Run API を検証するために2つの検証ステップ(curl-test
, integration-test
)を実行させます。
apiVersion: skaffold/v4beta11
kind: Config
metadata:
name: deploy-run-quickstart
profiles:
- name: prod
manifests:
rawYaml:
- run.yaml
deploy:
cloudrun: {}
verify:
- name: curl-test
container:
name: verification-test-contaier2
image: gcr.io/cloud-builders/gcloud
command: ["/bin/sh"]
args:
- '-c'
- |
IDENTITY_TOKEN=$(gcloud auth print-identity-token)
STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $IDENTITY_TOKEN" $CLOUD_RUN_SERVICE_URLS/healthcheck)
if [ "$STATUS_CODE" -eq 200 ]; then
echo "Verification successful: Status code is 200"
exit 0
else
echo "Verification failed: Status code is $STATUS_CODE"
exit 1
fi
- name: integration-test
container:
name: integration-test-container
image: asia-northeast1-docker.pkg.dev/sample-project/sample-repo/hello-world:latest
command: ["/bin/sh"]
args:
- '-c'
- |
APP_URL=$CLOUD_RUN_SERVICE_URLS go test -v app1/tests
curl-test
curl-test
は Cloud Run に用意したヘルスチェックエンドポイントを直接叩いて、ステータスコードをチェックする簡易的なテストです。
認証付き Cloud Run にリクエストを投げるには Authorization
ヘッダーにIDトークンを含める必要があり、そのIDトークンを gcloud auth print-identity-token
で取得しています。
$CLOUD_RUN_SERVICE_URLS
は Cloud Deploy のターゲットタイプが Run の場合に Cloud Run サービスのURLを参照できる環境変数です。VERIFY 実行環境で使用可能な環境変数は他にもいくつかあります。
integration-test
integration-test
は _test.go
で用意したテストコードを、go test
コマンドで実行するものです。テストコードのサンプルとして、次のようなものを用意しました。
package tests
import (
"bytes"
"context"
"errors"
"fmt"
"google.golang.org/api/idtoken"
"io"
"net/http"
"os"
"testing"
)
func Test_HealthCheck(t *testing.T) {
tests := []struct {
name string
StatusCode int
responseBody string
}{
{
name: "/healthcheck を叩いたら、ステータスコード 200 で ok が返されること",
StatusCode: http.StatusOK,
responseBody: "ok",
},
}
for _, tt := range tests {
res, err := newRequest("/healthcheck")
if err != nil {
t.Fatalf("Failed to post request: %v", err)
}
if res.StatusCode != tt.StatusCode {
t.Fatalf("expect = %v, actual = %v", tt.StatusCode, res.StatusCode)
}
body, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("Failed to read response body: %v", err)
}
if string(body) != tt.responseBody {
t.Fatalf("expect = %#v, actual = %#v", tt.responseBody, string(body))
}
}
}
func newRequest(path string) (*http.Response, error) {
client, err := idtoken.NewClient(context.Background(), os.Getenv("APP_URL"))
if err != nil {
return nil, errors.New(fmt.Sprintf("idtoken.NewClient: %v", err))
}
return client.Get(os.Getenv("APP_URL") + path)
}
Goコードから Cloud Run API を叩き、ステータスコードとレスポンスボディをチェックしています。テストコードをGoコードとして記述することできめ細かいチェックが行い易くなります。
また、idtoken を使うとIDトークンを取得するところからAuthorization
ヘッダーに含めるところまでを内部的にやってくれるため、開発者はその辺を意識せずにコーディングできるのがありがたいです。
3.検証に必要な権限を設定する
検証機能は Cloud Build 上で実行されます。
上記でいうところの curl-test
と integration-test
のコンテナは Cloud Build 上で構築され、そこから検証対象である Cloud Run にAPIリクエストを投げます。
この時、Cloud Build 実行アカウントは executionConfigs.serviceAccount
で指定したものであり、このアカウントから Cloud Run にアクセスできる必要があります。
そのためにはまず roles/run.invoker
ロールが必要です。
加えて、先述の検証コードでIDトークンを取得するには roles/iam.serviceAccountTokenCreator
ロールを自身に付与します。
gcloud iam service-accounts add-iam-policy-binding my-cloud-deploy@${PROJECT_ID}.iam.gserviceaccount.com \
--member='serviceAccount:my-cloud-deploy@${PROJECT_ID}.iam.gserviceaccount.com' \
--role='roles/iam.serviceAccountTokenCreator'
resource "google_service_account_iam_binding" "create_my_token" {
service_account_id = google_service_account.my_cloud_deploy.id
role = "roles/iam.serviceAccountTokenCreator"
members = [
"serviceAccount:${google_service_account.my_cloud_deploy.email}",
]
}
最初は roles/iam.serviceAccountTokenCreator
ロールが無くても idtoken
パッケージを使えばトークンを取得できると思っていましたが、Cloud Build 環境ではダメでした。
また、このロールを付与しなくても Cloud Run にアクセスする方法があるのではと調べましたが、自分のニーズに合う方法は見つかりませんした(備考欄を参照)。
検証機能を動かす
gcloud deploy release create を実行してリリースを作成すると、Deploy フェーズが完了した後に Verify フェーズが実行されます。
gcloud deploy releases create sample-release-001 \
--delivery-pipeline=run-verify-pipeline \
--skaffold-file=skaffold.yaml \
--gcs-source-staging-dir=gs://clouddeploy_bucket/source \
--images my-app-image=$(IMAGE_PATH) \
--project=$(PROJECT) \
--region=$(REGION)
![]() |
---|
備考
IDトークンを取得する方法はいくつかあります。(参考1, 参考2)
- 認証ライブラリ(
idtoken
)を使用する- 実行アカウントがサービスアカウントならば使用可能だが、ユーザアカウントは使用不可。
- メタデータサーバーを使用する
- Google Cloud の外部から Workload Identity 連携を使用する
- Google Cloud の外部からダウンロードしたサービスアカウントキーを使用する
- 接続サービスを使用して ID トークンを生成する
- Cloud Scheduler や Workflows を介してアクセスする方法
- サービスアカウントの権限を借用して ID トークンを生成する
-
gcloud auth print-identity-token
コマンドを使用する
また、メタデータサーバにアクセスする際の注意点として次のものがありました。
メンテナンス イベントにより、メタデータ サーバーの利用可能状態が最大 1 秒中断されることがあります。このとき、メタデータ サーバーは Error 503 HTTP サーバー レスポンスを返すことがあります。アプリケーションをメンテナンス イベントに耐えられるようにするには、メタデータ サーバーをクエリするアプリケーションに再試行ロジックを実装することをおすすめします。
参考
- Google Cloud Deploy にデプロイ後に検証を行う機能を導入
- こちらの記事も参考にさせていただきました。ありがとうございます。
Discussion