TerraformでCloud Build v2とCloud Run v2を作成する
Terraformを使ってCloud BuildとCloud Runを作って、GitHubにコードをpushするとCloud BuildでbuildしてCloud Runにデプロイするところまでやったのでそのメモです
こちらの続きでもあります
Cloud BuildとCloud Runはそれぞれ2世代目を使います
やりたいこと
GitHubにコードをpushするとCloud Buildがそれを検知してコードをBuild
Build後にArtifact Registryにコンテナイメージをアップロード
Cloud Runにアップロードされたコンテナイメージをデプロイ
Cloud Build作成準備
GitHubのPersonal Access Token発行
TerraformでCloud Buildを作成する前にCloud BuildとGitHubを連携するための準備をします
Cloud BuildとGitHubを接続するためにGitHubのPersonal Access Tokenを発行します
ユーザーの設定からDeveloper Settingsを開きます
Personal Access Tokensを選択し、Tokens(classic)を選択します
Fine-grained tokensも試してみたのですが、Cloud Buildで権限が無いってエラーが出たのでclassicの方で作成しました
Generate New Tokenをクリックすると以下のような画面になります
Tokenの設定は以下の通りにします
- Expirationはno expirationを選択
- repoにチェックを入れる
- user欄のread:userにチェックを入れる
発行されたトークンは後ほど使うのでメモしておくか、Terraform CloudのVariablesに突っ込んでおきましょう
GitHubにCloud Build Appをインストール
GitHubにCloud Build Appをインストールします
Repository AccessでCloud BuildでBuildしたいRepositoryを選択します
インストール後にCloud Build Appの設定ページにアクセスします
こちらのページのURL末尾はGitHubのapp_installation_idになるのでメモっておきます
https://github.com/settings/installations/{こちらの数字がID}
これで準備が完了しました
Cloud BuildのリソースをTerraformを使って作成
いよいよTerraformを使ってCloud Buildを作成していきます
API有効化
いつものようにAPIを有効化しないとTerraformで操作できなくなるのでAPIをTerraformで有効化します
services.tf
locals {
services = toset([
"cloudbuild.googleapis.com",
"iam.googleapis.com",
"secretmanager.googleapis.com",
...
])
}
resource "google_project_service" "services" {
project = var.project_id
service = each.value
for_each = local.services
}
CloubBuildで使用するsecret作成
先程作成したGitHubのトークンとapp_installation_idを格納するsecretを作成します
自分はGITHUB_TOKENの値をTerraform Cloudから入れたので以下のようにしてます
secret_manager.tf
resource "google_secret_manager_secret" "github_token_secret" {
secret_id = "github-token-secret"
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "github_token_secret_version" {
secret = google_secret_manager_secret.github_token_secret.id
secret_data = var.GITHUB_TOKEN
}
CloubBuild作成
TerraformでCloud BuildとIAMの設定を行います
cloud_build.tf
resource "google_cloudbuild_trigger" "trigger" {
location = var.region
name = "your-trigger"
repository_event_config {
repository = google_cloudbuildv2_repository.your_repository.id
push {
branch = "main"
}
}
filename = "cloudbuild.yaml"
}
resource "google_cloudbuildv2_connection" "your_connection" {
location = var.region
name = "your-connection"
github_config {
app_installation_id = var.GITHUB_APP_INSTALLATION_ID
authorizer_credential {
oauth_token_secret_version = google_secret_manager_secret_version.github_token_secret_version.id
}
}
}
resource "google_cloudbuildv2_repository" "your_repository" {
location = var.region
name = "your-repository"
parent_connection = google_cloudbuildv2_connection.your_connection.name
remote_uri = var.GITHUB_REPOSITORY_URI
}
data "google_iam_policy" "secret_accessor" {
binding {
role = "roles/secretmanager.secretAccessor"
members = ["serviceAccount:cloudbuildのサービスエージェントのメールアドレス"]
}
}
resource "google_secret_manager_secret_iam_policy" "policy" {
secret_id = google_secret_manager_secret.github_token_secret.secret_id
policy_data = data.google_iam_policy.secret_accessor.policy_data
}
cloud_build.tfの説明を行っていきます
目標としてはCloud Buildのtriggerを作ることです
triggerを作るためにはcloud buildのrepositoryにGitHubのリポジトリを登録する必要があります
GitHubのリポジトリをCloud Buildに登録する必要があります
なので下記の3つのリソースを作成する必要があるわけですね
- google_cloudbuild_trigger
- google_cloudbuildv2_connection
- google_cloudbuildv2_repository
また、Cloud BuildではGITHUB_TOKENのsecretにアクセスするのでSecret Accessor権限を付与する必要があります
付与するのはCloud Buildのデフォルトサービスアカウントではなく、サービスエージェントでした
dataブロックでpolicy_dataに記述できるように付与するroleと誰に付与するかを定義しておきます
google_secret_manager_secret_iam_policyリソースを作成してdataブロックで定義したpolicy_dataを付与します
cloudbuild.yamlについては今回は割愛します
おまけ
今回はCloud Buildをasia-east1
のリージョンで立てました
本当はasia-northeast1
に立てたかったのですが、無料プランから有料プランにアップグレードしないとエラーが出ます
Concurrent Build CPUs (Regional Public Pool) per region per build_origin (default)
こちらはquotaで引っかかっていて、特定の地域以外は0に設定されていて使えなくなっています
quotaはIAMページの割当とシステム上限のタブからCloud Build APIを選択すると確認することができます
Cloud Run作成
ここからはCloud Runを作成していきます
Cloud Buildの設定変更
Cloub Buildのサービスアカウントの権限でCloud Runを有効化しておきます
このあたりもTerraformでやりたかったのですが、やり方が分からなかったのでコンソールから直接行いました
どなたかやり方ご存じの方はコメントで教えていただけると助かります
API有効化
いつもの
services.tf
locals {
services = toset([
"run.googleapis.com",
...
])
}
resource "google_project_service" "services" {
project = var.project_id
service = each.value
for_each = local.services
}
Cloud Run作成
TerraformでCloud Runを作成します
cloud_run.tf
resource "google_cloud_run_v2_service" "your_service" {
name = "your-service"
location = var.region
ingress = "INGRESS_TRAFFIC_ALL"
template {
scaling {
max_instance_count = 1
}
containers {
image = "your-container-path"
env {
name = "APP_ENV"
value = "prod"
}
}
}
lifecycle {
ignore_changes = [
client,
client_version,
template[0].containers[0].image,
]
}
}
resource "google_service_account_iam_member" "cloudbuild" {
service_account_id = "projects/${var.project_id}/serviceAccounts/${var.default_service_account}"
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${var.cloud_build_default_service_account}"
}
resource "google_cloud_run_v2_service_iam_binding" "default" {
location = google_cloud_run_v2_service.your_service.location
name = google_cloud_run_v2_service.your_service.name
role = "roles/run.invoker"
members = [
"allUsers"
]
}
Cloud Buildからデプロイするので、Cloud BuildのサービスアカウントがCloud Runを使えるようにしないといけません
なのでgoogle_service_account_iam_member
リソースを作成してそのことを定義します
ちなみにこのエラーがCloud Buildで出ます
(gcloud.run.deploy) PERMISSION_DENIED: Permission 'run.services.get' denied on resource
また、Cloud Runの「未認証を許可」するためにgoogle_cloud_run_v2_service_iam_binding
でallUsersにしています
Cloud Runのリソースですが、費用を抑えてスケールしないようにmax_instance_countを1にしておきました
また、Terraformで差分が発生しないようにignore_changesを定義しています
これでCloud Runの作成は完了です
まとめ
Cloud BuildとCloud RunをTerraformで作った流れを一通り書きました
iam周りとかサービスアカウントの設定とか忘れそうなのでまたインフラ構築するときは自分で見返そうかと思います
cloudbuild.yamlとかは今回触れていなかったのでそのうち記事にまとめようかと思います
Discussion