Terraformを使ってCloud BuildからCloud Runにデプロイする仕組みを作る
以前こちらの記事でNext.jsのアプリをCloud Runにデプロイしましたが、今回はCloud Buildからデプロイするように変更したので、ハマりポイントなどを共有します。
GitHubとの接続
まずGoogle CloudのプロジェクトとGitHubリポジトリを接続します。
接続するにはアクセストークンを使ったりする方法もありますが、今回はGoogle Cloud BuildのGitHub Appsを使って接続する方法を紹介します。
-
まずCloud Buildのコンソールで
ホスト接続を作成
をクリックします。
-
GitHubを選択した状態で、リージョンを選択肢、名前をつけて
接続
をクリックします。
-
以下のようなポップアップが出てくるので
Continue
を押してしばらく待ちます。
-
インストールを選択する画面に切り替わりますので、すでにGitHub Appsをインストールしているorgはそれを使い、そうでない場合は
新しいアカウントでインストール
をクリックします。
-
GitHubの画面にリダイレクトするので、orgを選択して必要なリポジトリに対して権限を与えます。
-
そうすると以下のように接続されます。
今回は上記の接続は使わずにTerraformで管理します。
terraformでCloud Build周りを構成する
Cloud Buildのトリガーを作成する前に、リポジトリとの接続とCloud Buildのサービスアカウントへの権限付与を行います。
resource "google_cloudbuildv2_connection" "github_connection" {
location = var.region
name = "github-connection"
github_config {
app_installation_id = var.github_app_installation_id
authorizer_credential {
oauth_token_secret_version = var.github_oauth_token_secret_version
}
}
}
resource "google_cloudbuildv2_repository" "github_repository" {
name = "github-repository"
parent_connection = google_cloudbuildv2_connection.github_connection.id
remote_uri = var.github_repository_remote_uri
}
resource "google_project_iam_member" "cloudbuild_iam" {
for_each = toset([
"roles/run.admin",
"roles/iam.serviceAccountUser",
"roles/secretmanager.secretAccessor",
])
role = each.key
member = "serviceAccount:${var.project_number}@cloudbuild.gserviceaccount.com"
project = var.project_id
}
変数で指定している箇所がいくつかありますが、以下を指定してください。
-
var.github_app_installation_id
- GitHub Apps の Cloud Build アプリケーションのインストール ID
-
var.github_oauth_token_secret_version
- GitHub AppsのOAUTH TOKENのシークレット。Google Cloud BuildのGitHub Appsを接続した手順で
xxxx-github-oauthtoken-xxxx
のような名前でSecret Managerに登録されているはずなので確認してください。例えばprojects/123456789/secrets/xxxx-github-oauthtoken-xxxx/versions/1
のような形式になります。
- GitHub AppsのOAUTH TOKENのシークレット。Google Cloud BuildのGitHub Appsを接続した手順で
-
var.github_repository_remote_uri
- リポジトリのuri(https)。今回のサンプルアプリの場合
https://github.com/tokku5552/nextjs-gcp-sample.git
- リポジトリのuri(https)。今回のサンプルアプリの場合
そしてCloud Build Triggerのリソースを追加します。
resource "google_cloudbuild_trigger" "my-app_trigger" {
location = var.region
project = var.project_id
repository_event_config {
repository = google_cloudbuildv2_repository.github_repository.id
push {
branch = "^main$"
}
}
filename = "my-app/cloudbuild.yaml"
substitutions = {
_REGION = var.region
_ARTIFACT_REPOSITORY_IMAGE_NAME = "${var.region}-docker.pkg.dev/${var.project_id}/artifact-registry-nextjs-gcp-sample-app/console"
}
}
substitutions
でcloudbuild.yaml
内で使える環境変数を定義することができます。
repository_event_config
でmainブランチにpushしたときとしていますが、他にも色々設定できます。
cloudbuild.yamlを追加してビルドとデプロイを自動化する
次にcloudbuild.yaml
を追加します。
以下の記事を参考にさせてもらいました。
steps:
- name: "gcr.io/cloud-builders/docker"
entrypoint: "bash"
args:
- -c
- >-
docker buildx build
--platform linux/amd64
--build-arg DATABASE_URL=$$DATABASE_URL
--tag $_ARTIFACT_REPOSITORY_IMAGE_NAME:$SHORT_SHA
--tag $_ARTIFACT_REPOSITORY_IMAGE_NAME:latest
--file Dockerfile
--cache-from $_ARTIFACT_REPOSITORY_IMAGE_NAME:latest
--push
.
dir: "my-app"
automapSubstitutions: true
secretEnv: ["DATABASE_URL"]
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
entrypoint: gcloud
args:
- "run"
- "deploy"
- "app"
- "--image"
- "$_ARTIFACT_REPOSITORY_IMAGE_NAME:$SHORT_SHA"
- "--region"
- "$_REGION"
substitutions:
_REGION: by-terraform
_ARTIFACT_REPOSITORY_IMAGE_NAME: by-terraform
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/DATABASE_URL/versions/latest
env: DATABASE_URL
images:
- "$_ARTIFACT_REPOSITORY_IMAGE_NAME:$SHORT_SHA"
上記の記事の割とそのままなので、ここで特に解説することは無いですが、モノレポを意識してmy-app/cloudbuild.yaml
にファイルを配置したため、dir: "my-app"
を指定しています。
また、Secret ManagerからDATABASE_URLを取得していますが、build-argで渡したい場合はgcr.io/cloud-builders/docker
のentrypointをbash
にする必要があります。
最初から使える変数や、自分で定義する場合、Secret Managerから取得する場合などについては、以下の公式ページに情報がまとまっていますので参照ください。
Cloud Buildからデプロイされても良いようにCloud Runリソースを修正する
もともとのCloud Runのリソースに以下を追加して、Cloud Buildからデプロイされてimage urlなどが変更されたとしても、terraformからは検知しないようにします。
lifecycle {
ignore_changes = [
client,
client_version,
template[0].containers[0].image,
]
}
これでterraform applyすれば、以後mainブランチにマージされるたびにビルドとデプロイを行ってくれます。
まとめ
振り返って整理してみるとたいしたことないように見えますが、自分は結構時間を使ってしまいました。
特にIAM周りの設定がうまくいかずに失敗すると、Cloud Build上で404エラーとだけ出て原因がわからないのでかなり試行錯誤しました。
ただ、今回Terraformで接続部分もコード化したため、アプリケーションを環境毎に別のプロジェクトにデプロイしたい場合なんかでも使い回せるようになったと思います。
Discussion
記事ありがとうございます。
ハマったので共有です。
google_cloudbuild_trigger
の部分ですが、400エラーがでて失敗してしまいます。おそらく、GoogleCloudの仕様変更で CloudBuildのAPIを有効化しても、デフォルトのCloudBuildアカウントが作成されなくなったのが原因かもしれません(間違ってたらすみません)。
stackoverflowを参考にその場でservice_accountを作成して
google_cloudbuild_trigger
に渡せば、エラーなく通りました。参考: