Cloud Deploy と Cloud Run をためす
結論
- 現在、擬似的にすべてのビルド・デプロイ作業を Cloud Build (+gcloudコマンド)で実施している
- Cloud Deploy を組み込むことは少なからずこれを崩すことであり、コストメリットが薄い
- コスト
- ブランチ・リリース戦略の見直し(mainブランチへ一本化したほうがよさそう)
- 検証環境の昇格戦略の見直し(検証・本番でビルドイメージを統一したほうがよさそう)
- Google Cloud プロジェクト構成の見直し
- DBマイグレーション実行タイミングと実行方法の調整
- メリット
- デプロイサイクルに関するメトリクスの収集
プロジェクトが黒字になり、チーム人数が10人を超えたら検討するでよさそう。
モチベーション
- デリバリパフォーマンスを計測したい
- 現状 Cloud Run へのデプロイ制御にために手動でコマンドを打つ場面があり、これを承認ボタンはいOKに置き換えたい
わかっていないので知りたいこと
- どんなメトリクスがとれるのか
- いま、Cloud Build で DB マイグレーションを実行している。Cloud Deploy によるデプロイへ置き換えたとき、これはどうすればよいだろうか
まずはチュートリアルをやってみる
全体像をつかむため、チュートリアルをやる。
サンプルは Compute Engine のサービスアカウントにロールを付与しているが、Cloud Deploy 専用のサービスアカウントを個別に作ることとした。
variable "project_id" {
description = "GCPのproject_idです"
type = string
}
variable "cloud_run_backend_service_account_name" {
description = "Cloud Run に設定するサービスアカウントです。Cloud Runサービスアカウントを利用するのに利用します。"
type = string
}
variable "cloud_run_frontend_service_account_name" {
description = "Cloud Run に設定するサービスアカウントです。Cloud Runサービスアカウントを利用するのに利用します。"
type = string
}
resource "google_service_account" "cloud_deploy_for_app" {
project = var.project_id
account_id = "cloud-deploy-for-app"
display_name = "Cloud Deploy for Applications Service Account"
}
# 参考: https://cloud.google.com/deploy/docs/deploy-app-run?hl=ja
# clouddeploy.jobRunner
resource "google_project_iam_member" "cloud_deploy_for_app_job_runner" {
project = var.project_id
role = "roles/clouddeploy.jobRunner"
member = "serviceAccount:${google_service_account.cloud_deploy_for_app.email}"
}
# roles/run.developer
resource "google_project_iam_member" "cloud_deploy_backend_is_run_developer" {
project = var.project_id
role = "roles/run.developer"
member = "serviceAccount:${google_service_account.cloud_deploy_for_app.email}"
}
# Cloud Deploy が Cloud Run サービスアカウントを利用できるように
resource "google_service_account_iam_member" "clouddeploy_is_backend_runner_user" {
service_account_id = var.cloud_run_backend_service_account_name
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.cloud_deploy_for_app.email}"
}
resource "google_service_account_iam_member" "clouddeploy_is_frontend_runner_user" {
service_account_id = var.cloud_run_frontend_service_account_name
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.cloud_deploy_for_app.email}"
}
Cloud Run のサービスアカウントがふたつあるのでそれぞれでCloud Deployが利用できるように google_service_account_iam_member
も2つ追加。
IAMリソースを作ったら、あとは流れに沿ってやるだけ。
- clouddeploy.yaml
- run-dev.yaml
- run-prod.yaml
- skaffold.yaml
これらを用意して、コマンドを実行する。
# Cloud Deploy パイプラインを作成する
gcloud deploy apply --file=clouddeploy.yaml --region=us-central1 --project=9999999999
# リリースを作成する
gcloud deploy releases create test-release-001 \
--project=999999999 \
--region=us-central1 \
--delivery-pipeline=my-run-demo-app-1 \
--images=my-app-image=gcr.io/cloudrun/hello
わかったこと
- Cloud Run のサービスを作成できる
- 任意のイメージを Cloud Run へデプロイできる
- まず 動作確認用のサービスにデプロイしたあと、昇格という形で本番用のサービスにデプロイすることを想定しているようだ
Cloud Run のパラメータとかどやって設定するの
デプロイ済みのCloud Runで手に入るマニフェストを参考にするらしい。これを kind: Service
のやつとする模様。
たぶんこのへん。
spec:
template:
metadata:
name: backend
annotations:
run.googleapis.com/client-name: gcloud
client.knative.dev/user-image: asia-northeast1-docker.pkg.dev/***
run.googleapis.com/client-version: 433.0.0
run.googleapis.com/cloudsql-instances: ***
run.googleapis.com/execution-environment: gen1
autoscaling.knative.dev/maxScale: '3'
run.googleapis.com/cpu-throttling: 'false'
spec:
containerConcurrency: 40
timeoutSeconds: 300
serviceAccountName: backend****
containers:
- image: asia-northeast1-docker.pkg.dev/***
ports:
- name: http1
containerPort: 8080
env:
- name: RAILS_ENV
value: production
- name: WORKER_COUNT
value: '1'
- name: RAILS_MAX_THREADS
value: '16'
resources:
limits:
cpu: '1'
memory: 2Gi
startupProbe:
timeoutSeconds: 240
periodSeconds: 240
failureThreshold: 1
tcpSocket:
port: 8080
上記は gcloud run deploy
を使ってオプション指定したものだが、それで手に入るマニフェストが参考にできそう。
Cloud Build との統合?
CIシステムの最後らへんで gcloud deploy releases create
を呼び出すイメージですね。
Cloud Deploy を使った方法に移行できるか?
できるかできないかでいうと、できる。たが、大幅なリリースフローの調整が必要。
そもそもいま、
- 検証用の Google Cloud プロジェクト
- 本番用の Google Cloud プロジェクト
に分かれている。ということで、Cloud Deploy の昇格機能をフルに活かすためにはまずここを同じ Cloud Deploy パイプラインにまとめなければならない。これを満たすためには、
- クロスプロジェクトで Cloud Run サービスをデプロイできる必要あり
- ソースコードのリポジトリマージフローも見直し(main, developブランチがあるがこれをmainに一本化する必要あり)
このような作業が必要になる。
DBマイグレーションどうする?
ドキュメントとしてはノータッチであった。まあ、「DBマイグレーションとアプリケーションは独立して最新版を apply できるべき」はそうなんだけど。いつかは同期するべきじゃない、ねえ奥様。最近のデプロイシステムはDBマイグレーションに関してノータッチのやつも多いけど、ちょっとわずかに言及があるだけでも嬉しいんだけどなー。
DBマイグレーションはアプリと密にするか疎にするかふたつの方法があると思っていて、密にするほうだと:
- Cloud Run 起動時に自動でマイグレーションするように
- Cloud Build のアプリビルドの中でマイグレーションを実行するように
こういったやり方がありそう。ただ、このやり方は実質的なロールバック禁止でもある。何か間違えたときは手運用でのDB操作はやらないほうがよくて、アプリケーションを修正して再リリースの流れが現実的。
疎結合にするやり方だと:
- 独立したスクリプトを用意して、リリースフローのどこかで実行する
ことになる。しかしこれは、Cloud Build や Cloud Deploy を組み合わせてデプロイするときに考慮するポイントが増えることになる。Cloud Deploy の流れに載せられないので。
いまは、「スクリプトを任意のタイミングでCloud Buildで実行できる」というようにしていたので疑似的に Cloud Build にすべてまとまった状態となっていた。これを崩す場合は DBマイグレーションの実行環境と組み込み方を改めて再構築刷る必要がありそう。
理想:
Cloud Deploy のパイプラインで「カナリアリリース後、New環境で任意のコマンドが実行できる」機能があれば最強。
To: @sbogdantbl
Thanks for getting back to me, Cloud Deploy is a great service.
Users who are highly proficient with Cloud Run are either leveraging Cloud Build, gcloud scripts, or both. I think it will take some time to adapt as knative specs for GKE appear in this abstracted situation.
Cloud Deploy's ability to automatically collect metrics about the deployment cycle and the Promote feature are attractive, and I would suggest the following as a way to leverage them with Cloud Run.
Instead of using knative specs at each step of the pipeline, gcloud commands can be used.
Cloud Run users are accustomed to deploying with gcloud commands, and it would be more applicable if the DeliveryPipeline could be defined in yaml and any command could be executed in the serialPipeline stages.
An example of the envisioned pipeline:
- check if the Artifact Registry image generated by Cloud Build exists
- Deploy to Cloud Run service A
- Deploy to Cloud Run Service B
- approve it
- Perform DB migration
- Set the traffic of Cloud Run Service A to 100%.
- Increase traffic on Cloud Run service B to 100%
- Notify Slack that the deployment is complete
This may be beyond the scope of what we envision in Cloud Deploy, but we would be happy to cover it in Cloud Deploy.