🗂

TerraformでCloud Build v2とCloud Run v2を作成する

2024/02/13に公開

Terraformを使ってCloud BuildとCloud Runを作って、GitHubにコードをpushするとCloud BuildでbuildしてCloud Runにデプロイするところまでやったのでそのメモです

こちらの続きでもあります
https://zenn.dev/wataru777/articles/6dd240e664fddf

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を開きます
https://github.com/settings/profile

Personal Access Tokensを選択し、Tokens(classic)を選択します
Fine-grained tokensも試してみたのですが、Cloud Buildで権限が無いってエラーが出たのでclassicの方で作成しました

Generate New Tokenをクリックすると以下のような画面になります

Tokenの設定は以下の通りにします

発行されたトークンは後ほど使うのでメモしておくか、Terraform CloudのVariablesに突っ込んでおきましょう

GitHubにCloud Build Appをインストール

GitHubにCloud Build Appをインストールします
Repository AccessでCloud BuildでBuildしたいRepositoryを選択します
https://github.com/apps/google-cloud-build

インストール後に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