Cloud RunをGithub ActionsとTerraformで管理する

33945 ワード

terraformでgcpの状態を管理し、github actionsでコードが更新されるたびにcloud runへdeployする方法を紹介します。順番としては、まずgithubとworkload identity連携するためのサービスアカウントを作成し、そのサービスアカウントを実行者としてCloud Runサービスを作成します。それからCloud Runで実行するアプリを用意し、github actionsでデプロイする設定を行います。

github連携用のサービスアカウントの作成

サービスアカウント用の設定は次のようにします。

ディレクトリ構造
.
├── backend.conf
├── main.tf
├── registry.tf
├── terraform.tfvars
└── variables.tf

main.tfでは、terraformのバージョン指定とサービスアカウントのroleの指定が定義されています。repo_nameは権限を与えるgithubリポジトリなので、この段階で連携するリポジトリを決めておく必要があります。

main.tf
terraform {
  required_version = "~> 1.1.9"
  backend "gcs" {
    prefix = "terraform/state"
  }
}

locals {
  cloudrun_roles = [
    "roles/run.developer",
    "roles/iam.serviceAccountUser"
  ]
}

resource "google_project_service" "default" {
  project = var.project
  service = "iamcredentials.googleapis.com"
}

resource "google_service_account" "github_actions" {
  project      = var.project
  account_id   = "github-actions"
  display_name = "A service account for GitHub Actions"
  description  = "link to Workload Identity Pool used by github actions"
}

resource "google_iam_workload_identity_pool" "github" {
  provider                  = google-beta
  project                   = var.project
  workload_identity_pool_id = "github"
  display_name              = "github"
  description               = "Workload Identity Pool for GitHub Actions"
}

resource "google_iam_workload_identity_pool_provider" "github" {
  provider                           = google-beta
  project                            = var.project
  workload_identity_pool_id          = google_iam_workload_identity_pool.github.workload_identity_pool_id
  workload_identity_pool_provider_id = "github-provider"
  display_name                       = "github actions provider"
  description                        = "OIDC identity pool provider for execute github actions"

  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.repository" = "assertion.repository"
    "attribute.owner"      = "assertion.repository_owner"
    "attribute.refs"       = "assertion.ref"
  }

  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
}

resource "google_service_account_iam_member" "github-account-iam" {
  service_account_id = google_service_account.github_actions.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/${var.repo_name}"
}

resource "google_project_iam_member" "service_account" {
  count   = length(local.cloudrun_roles)
  project = var.project
  role    = element(local.cloudrun_roles, count.index)
  member  = "serviceAccount:${google_service_account.github_actions.email}"
}

output "service_account_github_actions_email" {
  description = "github account for github actions"
  value       = google_service_account.github_actions.email
}

output "google_iam_workload_identity_pool_provider_github_name" {
  description = "Workload Identity Pood Provider ID"
  value       = google_iam_workload_identity_pool_provider.github.name
}

variables.tfには変数の定義があります。

variables.tf
variable "project" {
  description = "A name of a GCP project"
  type        = string
  default     = null
}
variable "repo_name" {
  description = "github repository name"
  default     = "user/repository"
}

registry.tfにはcloud registryの作成とサービスアカウントに権限を与えています。

registry.tf
resource "google_container_registry" "registry" {
  project  = var.project
  location = "ASIA"
}

resource "google_storage_bucket_iam_member" "registry_create" {
  bucket = google_container_registry.registry.id
  role   = "roles/storage.admin"
  member = "serviceAccount:${google_service_account.github_actions.email}"
}

backend.confにバケット名を書いてまずinitします。

backend.conf
bucket = "tf-state-xxxxxxxxx"
terraform init -backend-config=./backend.conf

問題なく実行できたら、次はplanで問題ないか見ます。

terraform.tfvars
project   = "*******"
repo_name = "user/repository"
terraform  plan  -var-file=./terraform.tfvars

問題なければapplyしましょう(好みで-auto-approve付けてください)。

$ terraform  apply  -var-file=./terraform.tfvars
Outputs:åß

service_account_github_actions_email = github-actions@${gcp_project}.iam.gserviceaccount.com
google_iam_workload_identity_pool_provider_github_name = projects/**********/locations/global/workloadIdentityPools/${pool_id}/providers/${provider_id}

アウトプットは後で使うので、消さないようにしましょう。とりあえずこれでworkload identity連携できるサービスアカウントができました。

Cloud Runサービスの作成

Cloud Runサービスの設定は次のようにします。

ディレクトリ構造
.
├── backend.conf
├── cloudrun.tf
├── main.tf
├── out.txt
├── terraform.tfvars
└── variables.tf

main.tfはterraformのバージョン指定と公開範囲について書いています。全体公開になっているので、目的に合わなければ設定を変える必要があります。なおgoogle_cloud_run_service.defaultcloudrun.tfで定義しています。

main.tf
terraform {
  required_version = "~> 1.1.9"
  required_providers {
    google = ">= 4.20.0"
  }
  backend "gcs" {
    prefix = "terraform/state"
  }
}

data "google_iam_policy" "cloud_run_public" {
  binding {
    role = "roles/run.invoker"
    members = [
      "allUsers",
    ]
  }
}

resource "google_cloud_run_service_iam_policy" "policy" {
  location = google_cloud_run_service.default.location
  project  = google_cloud_run_service.default.project
  service  = google_cloud_run_service.default.name

  policy_data = data.google_iam_policy.cloud_run_public.policy_data
}

cloudrun.tfはCloud Runサービスの定義になります。外部からコンテナイメージとサービスアカウントを指定する作りになっています。

cloudrun.tf
resource "google_cloud_run_service" "default" {
  name     = "cloudrun-hello"
  location = var.location  
  autogenerate_revision_name = true
  template {
    spec {
      containers {
        image = var.container_image
      }
      service_account_name = var.service_account_name
    }
  }
}

output "url" {
  value = google_cloud_run_service.default.status[0].url
}

variables.tfは先ほどと同様で、変数の定義が書かれています。

variables.tf
variable "project" {
  description = "A name of a GCP project"
  type        = string
  default     = null
}

variable "location" {
  description = "A location of a cloud run instance"
  type        = string
  default     = "asia-northeast1"
}

variable "container_image" {
  description = "docker container image"
  type        = string
  default     = ""
}

variable "service_account_name" {
  description = "Email address of the IAM service account"
  type        = string
  default     = ""
}

さきほどと同様にして実行しますが、terraform.tfvarsは次のようにします。まだアプリを作成していないのでcontainer_imageはgoogleが用意したサンプルプロジェクトを指定します。

terraform.tfvars
project              = "*********"
location             = "asia-northeast1"
container_image      = "gcr.io/cloudrun/hello"
service_account_name = "github-actions@${project_id}.iam.gserviceaccount.com"

実行後に表示されるurlにアクセスして、次のような画面が表示されれば成功です。

github actions

本当はアプリを作る必要がありますが、次のリポジトリのrun/helloworldを使います。