こんにちは。 駅ログ!というアプリを趣味で開発しているのですが、そのバックエンドはCloud Runでホスティングされています。 これまではGitHub Actionsを使ってCI/CDを構築していたのですが、Cloud Deployを採用したので今回はそれについて書きます。
なぜCloud Deployにしたのか
Cloud Deployを採用した理由は以下の通りです。
- デプロイメントワークフローに求める機能(承認、ロールバック、etc)をサポートする機能が十分にあること
- マネージドサービスであること
- メトリクスをGCP上で取得できること
- Skaffoldがv2.0.0のBetaリリースからCloud Runをサポートしたこと
- 2022/9/13に、Cloud DeployからCloud Runにデプロイできるようになったこと
このような理由があり、Cloud Deployを使ってリリースするような流れにしようと決断しました。 承認フローなどはGitHub ActionsのEnvironment機能でも代替できますが、ロールバックなどの簡易さなどは特にCloud Deployが優れているところだと思います。またCloud Monitoringなどでパイプラインのメトリクスが取得できることは一元化にも繋がり、見通しの良いモニタリングを実施することができると考えています。
セットアップ
移行するとなると、まずはCloud DeployのDelivery PipelineやTargetの設定しなければなりません。
1. Skaffoldの設定
Skaffoldコマンドの起点となるskaffold.yml
を作成します。
apiVersion: skaffold/v3alpha1
kind: Config
metadata:
name: ekilog
manifests:
rawYaml:
- service.yaml
deploy:
cloudrun: {}
今回は特にProfileなどは使用しませんでした。ここでついでにCloud Runのserviceを定義するservice.yaml
を作成しておきます。
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: ekilog-backend
spec:
template:
spec:
containers:
- image: ekilog-backend
serviceAccountName: ekilog-backend@<PROJECT_ID>.iam.gserviceaccount.com
image
は実行時に上書きされるのでここでは何でも良いです。
2. Targetの設定
駅ログ!では2つのデプロイメント環境があるのでそれぞれCloud Deploy Targetを作成します。YAMLのAPIはFor Cloud Run Targets, Google Cloudから参照をすることができます。
Terraformで作成しようとしましたがgoogle_clouddeploy_target, v4.38.0ではまだサポートされていないので仕方なくYAMLで作成しました。いずれCloud Runを対応されると思います。
apiVersion: deploy.cloud.google.com/v1
kind: Target
metadata:
name: prod
description: Production Ekilog backend
run:
location: projects/PROJECT_ID/locations/asia-northeast1
requireApproval: "true"
executionConfigs:
- usages:
- [RENDER | DEPLOY]
serviceAccount: clouddeploy-backend@<PROJECT_ID>.iam.gserviceaccount.com
executionConfigs.*.serviceAccount
はあとのステップで作成します。
3. Delivery Pipelineの作成
以下のようにDeliveryを作成しました。Delivery PipelineはTerraformでも作成できるので、以下のように作成しました。
resource "google_clouddeploy_delivery_pipeline" "primary" {
project = google_project.ekilog.project_id
location = "asia-northeast1"
name = "ekilog-backend"
description = "Ekilog's backend pipeline"
serial_pipeline {
stages {
profiles = []
target_id = "dev"
}
stages {
profiles = []
target_id = "prod"
}
}
}
4. Cloud DeployがCloud Run Serviceをデプロイできるように設定をする
Terraformを使って諸設定をします。
4.1 Cloud Deploy用のService Accountを用意する
Cloud Deployが使うサービスアカウントは2種類あります。
- Service Agent: 主にGoogle側によって、顧客のGCP Projectで諸々操作(プロビジョニングなど)をするために使われるもの
- Execution Service Account: 顧客がDelivery Pipelineを実行したときに使われるもの。デフォルトではGCE service accountが使われる。
デフォルトのものを使うのは好ましくないので、以下のようにサービスアカウントを作成しました。
resource "google_service_account" "clouddeploy_backend" {
account_id = "clouddeploy-backend"
project = google_project.ekilog.project_id
display_name = "clouddeploy-backend"
}
4.2 権限の付与
先程作ったサービスアカウントに以下の権限を付与します。
clouddeploy.jobRunner
Project IAMroles/run.developer
Project IAM- Cloud Run service accountに対する
roles/iam.serviceAccountUser
service account IAM
resource "google_project_iam_member" "clouddeploy_backend_is_clouddeploy_job_runner" {
project = google_project.ekilog.project_id
role = "roles/clouddeploy.jobRunner"
member = "serviceAccount:${google_service_account.clouddeploy_backend.email}"
}
resource "google_project_iam_member" "clouddeploy_backend_is_run_developer" {
project = google_project.ekilog.project_id
role = "roles/run.developer"
member = "serviceAccount:${google_service_account.clouddeploy_backend.email}"
}
resource "google_service_account_iam_member" "clouddeploy_backend_is_ekilog_backend_user" {
service_account_id = google_service_account.ekilog_backend.name
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.clouddeploy_backend.email}"
}
ここまで設定を終えると、以下のようにコマンドを実行できます。
$ gcloud deploy releases create ekilog-prod-003 \
--project=<PROJECT_ID> \
--region=asia-northeast1 \
--delivery-pipeline=ekilog-backend \
--images=ekilog-backend=<IMAGE_PATH_ON_GAR>
5. GitHub Actionsから実行できるようにする
CI(今回はGitHub Actions)から実行させるには以下の権限がGitHub Actionsのservice accountに必要になります。
clouddeploy.releaser
Project IAM- Cloud Deploy service accountに対する
iam.serviceAccount.actAs
service account iam
あとはGitHub Actionsからトリガーをするだけです。