Cloud RunをGithub ActionsとTerraformで管理する
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リポジトリなので、この段階で連携するリポジトリを決めておく必要があります。
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
には変数の定義があります。
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の作成とサービスアカウントに権限を与えています。
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
します。
bucket = "tf-state-xxxxxxxxx"
terraform init -backend-config=./backend.conf
問題なく実行できたら、次はplan
で問題ないか見ます。
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.default
はcloudrun.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サービスの定義になります。外部からコンテナイメージとサービスアカウントを指定する作りになっています。
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
は先ほどと同様で、変数の定義が書かれています。
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が用意したサンプルプロジェクトを指定します。
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
を使います。
github actionsだけ次のように変更します。
name: deploy
on:
push:
branches:
- "main"
pull_request:
branches:
- "main"
env:
GCP_REGION: ${{ secrets.GCP_REGION_PRD }}
IMAGE: asia.gcr.io/${{ secrets.GCP_PROJECT_ID_PRD }}/hello_cloudrun:${{ github.sha }}
GOOGLE_IAM_WORKLOAD_IDENTITY_POOL_PROVIDER: ${{ secrets.GOOGLE_IAM_WORKLOAD_IDENTITY_POOL_PROVIDER }}
SERVICE_ACCOUNT_EMAIL: ${{ secrets.SERVICE_ACCOUNT_EMAIL }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: 'read'
id-token: 'write'
steps:
- name: Checkout the repository
uses: actions/checkout@v3
- id: "auth"
uses: "google-github-actions/auth@v0"
with:
workload_identity_provider: "${{ env.GOOGLE_IAM_WORKLOAD_IDENTITY_POOL_PROVIDER }}"
service_account: "${{ env.SERVICE_ACCOUNT_EMAIL }}"
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v0
- name: Authorize Docker push
run: gcloud auth configure-docker
- name: Build a docker image
run: docker build -t ${{ env.IMAGE }} -f Dockerfile .
- name: Push the docker image
run: docker push ${{ env.IMAGE }}
- name: Deploy to Cloud Run
id: deploy
uses: google-github-actions/deploy-cloudrun@v0
with:
service: cloudrun-hello
image: ${{ env.IMAGE }}
region: ${{ env.GCP_REGION }}
- name: Clean up Container images
run: |
gcloud container images list-tags "${BASE_IMAGE}" \
--filter="NOT tags:${GITHUB_SHA}" --format="get(digest)" | \
while read digest
do
gcloud container images delete -q --force-delete-tags "${BASE_IMAGE}@$digest"
done
env:
GITHUB_SHA: ${{ github.sha }}
BASE_IMAGE: asia.gcr.io/${{ secrets.GCP_PROJECT_ID_PRD }}/hello_cloudrun
GOOGLE_IAM_WORKLOAD_IDENTITY_POOL_PROVIDER
とSERVICE_ACCOUNT_EMAIL
は、サービスアカウントを作成したときにOutput
で表示されたものをgithubリポジトリのシークレットに設定します。GCP_PROJECT_ID_PRD
はGCPのプロジェクトIDで、GCP_REGION_PRD
はリージョンです(us-central1
, asia-northeast1
など)。最後のClean up Container images
は、今回pushしたイメージ以外はContainer Registryから削除する処理です。最後にgithubリポジトリのmainブランチにpushするとCloud Runへデプロイされ、先ほどのURLを開くとHello World!
とだけ表示されるはずです。ここまで、GCPのリソースについてはterraformのステート保存用にGCSのバケットを作った以外はterraformで管理しているのでterraform destroy
で削除できます。
次は
PostgreSQLを使った例を書きました。良かったら、読んだください。
Discussion