TerraformとGithub ActionsでCloud RunからPostgreSQLへアクセスする例
terraformとgithub actionsでCloud Runを管理する例の紹介です。今回はPostgreSQLを使いますが、workload identity poolは次のリンク先の例ですでに作成済みとします。
Terraform
サービスアカウント、Cloud Run、Cloud SQLの設定は次のterraformで行います。まず、ディレクトリ構造は次のようになっています。
.
├── backend.conf
├── cloudrun.tf
├── main.tf
├── registry.tf
├── service_account.tf
├── sql.tf
├── terraform.tfvars
└── variables.tf
main.tf
はterraformのバージョンの指定と、cloudrun
サービスのアクセスポリシーを定義しています。この例では誰でもアクセス可能になっています。
terraform {
required_version = "~> 1.1.9"
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
}
service_account.tf
はサービスアカウントの作成と権限について定義しています。cloudrun_roles
に主なrole(権限のセットのようなもの)を定義しています。
locals {
cloudrun_roles = [
"roles/run.developer",
"roles/iam.serviceAccountUser",
"roles/cloudsql.client"
]
}
resource "google_project_service" "default" {
project = var.project
service = "iamcredentials.googleapis.com"
}
resource "google_service_account" "github_actions" {
project = var.project
account_id = "hello-crud"
display_name = "hell-crud service account for GitHub Actions"
description = "link to Workload Identity Pool used by github actions"
}
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/${var.workload_identity_pool_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
}
sql.tf
ではCloud SQL関連の定義になります。ユーザーpostgres
はデフォルトのユーザーで、このユーザーのパスワードでgcloudから接続できます。deletion_protection
がfalse
なのは、削除しやすいようにするためです。
resource "google_sql_database_instance" "postgres" {
name = "cloudrun-sql"
region = var.location
database_version = "POSTGRES_13"
settings {
tier = "db-f1-micro"
database_flags {
name = "cloudsql.iam_authentication"
value = "on"
}
}
deletion_protection = "false"
}
resource "google_sql_database" "database" {
name = "quickstart_db"
instance = google_sql_database_instance.postgres.name
}
resource "google_sql_user" "users" {
name = "quickstart-user"
instance = google_sql_database_instance.postgres.name
type = "BUILT_IN"
password = var.db_user_password
}
resource "google_sql_user" "root_user" {
name = "postgres"
instance = google_sql_database_instance.postgres.name
type = "BUILT_IN"
password = var.db_root_user_password
}
cloudrun.tf
はCloud Runサービスの定義になります。env
の部分は環境変数を定義しているだけなので、コード依存のものでありCloud SQL特有のものではありません。autogenerate_revision_name
をtrue
にしているのは、github actionsで更新した後でもterraformから状態を変更するときにエラーにならないようにするためです。
resource "google_cloud_run_service" "default" {
name = "run-sql"
location = var.location
autogenerate_revision_name = true
template {
spec {
containers {
image = var.container_image
env {
name = "INSTANCE_UNIX_SOCKET"
value = "/cloudsql/${var.project}:${var.location}:${google_sql_database_instance.postgres.name}"
}
env {
name = "DB_NAME"
value = google_sql_database.database.name
}
env {
name = "DB_USER"
value = google_sql_user.users.name
}
env {
name = "DB_PASS"
value = google_sql_user.users.password
}
env {
name = "DB_IAM_USER"
value = "${google_service_account.github_actions.name}@${var.project}.iam"
}
}
service_account_name = google_service_account.github_actions.email
}
metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "1000"
"run.googleapis.com/cloudsql-instances" = google_sql_database_instance.postgres.connection_name
"run.googleapis.com/client-name" = "terraform"
}
}
}
}
output "url" {
value = google_cloud_run_service.default.status[0].url
}
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}"
}
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 "repo_name" {
description = "github repository name"
default = "user/repository"
}
variable "container_image" {
description = "docker container image"
type = string
default = ""
}
variable "db_user_password" {
description = "password for the builtin postgresql user"
default = ""
}
variable "db_root_user_password" {
description = "password for the root postgresql user"
default = ""
}
variable "workload_identity_pool_name" {
description = "name of workload identity pool"
default = ""
}
このterraformを実行するには連携するgithubリポジトリをrepo_name
で指定する必要があります。またterraformの実行はdocker imageがないので適当に別のもの(例えばgcr.io/cloudrun/hello
とか)を用意するか、手動で上げます。
github
githubに用意するアプリは次のリンクのcloudsql/postgres/database-sql
のものを使います。
*.yaml
は不要なので、削除しても問題ありません。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 }}/run_sql:${{ 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: run-sql
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 }}/run_sql
以前の記事に書いてある通りにリポジトリにSecretsした後、git push -u origin main
したらCloud Runサービスが更新されます(もちろんその前にterraform apply
してること)
github actionsのビルドが成功した後、Cloud Runサービスにアクセスしたとき、次のように表示され、ボタンをクリックすると投票行えることが確認できれば成功です。
Discussion