↓メインコンテンツへスキップ
  1. Blogs/

TerraformでCloud RunからCloud SQL for PostgreSQLへ接続してみた

2 分
Terraform GoogleCloud CloudRun CloudSQL SecretManager
0222-nnn
著者
0222-nnn
猫が好き
目次
Terraform-GoogleCloud - この記事は連載の一部です
パート 22: この記事

概要
#

Cloud RunからCloud SQLへ接続する際、Database Passwordをどこへ渡すかで迷います。環境変数へ直接書けば手軽ですが、Terraform StateやCI Logへ残るとSecretが漏れる経路になります。

そこでDatabase PasswordはSecret Managerへ保存します。Cloud RunのRuntime Service Accountには必要な権限だけを付与する設計です。接続自体もIPアドレスではなくUnix Socketを使い、Cloud SQL Auth Proxyの経路に任せます。

Terraformの基本操作とDockerコマンドは前提としています。Cloud Run、Cloud SQL、Secret Managerを個別には触ったことがある方向けです。

Private IP、Connection Pooling、HA構成は扱いません。Password Rotationの自動化も扱いません。

検証すること
#

  • Cloud Run、Cloud SQL、Secret Manager、Artifact Registryを連携できること
  • Write-only ArgumentでDatabase PasswordをStateへ保存せず登録できること
  • Runtime Service Accountへ必要最小限の権限を付与できること
  • Cloud RunからUnix Socket経由でPostgreSQLへ接続できること

前提環境
#

  • Google Cloud CLI、Terraform 1.11以降、Docker
  • Billingが有効な検証用Google Cloud Project
  • Cloud Run、Cloud SQL、Secret Manager、Artifact Registry、IAMを操作できるGoogleアカウント
  • 検証時のバージョン:Terraform 1.14.3、google 7.43.0

今回の構成
#

Cloud RunとCloud SQLはどちらもVPCの外にあります。接続にはUnix Socketを使います。

flowchart TB
    Client["Invoker"]

    subgraph GCP["Google Cloud"]
        subgraph Project["Project"]
            SA["Runtime Service Account"]
            SM["Secret Manager
DBパスワード"] subgraph Region["asia-northeast1"] AR["Artifact Registry"] Run["Cloud Run v2 Service"] SQL["Cloud SQL
PostgreSQL 15"] end end end Client -->|"HTTPS + ID Token"| Run Run -->|"起動時に pull"| AR Run -->|"Unix Socket
/cloudsql/CONNECTION_NAME"| SQL SM -.->|"環境変数へ注入"| Run SA -.->|"cloudsql.client
secretmanager.secretAccessor"| Run

点線が権限と設定の注入です。Runtime Service Accountにcloudsql.clientがないと接続できません。

リクエストを受けてからDBへ届くまでの流れです。

sequenceDiagram
    actor Client as Invoker
    participant Run as Cloud Run
    participant SM as Secret Manager
    participant SQL as Cloud SQL

    Note over Run,SM: 起動時にパスワードを取得
    SM-->>Run: 環境変数へ注入
    Client->>Run: HTTPS リクエスト
    Run->>SQL: Unix Socket 経由で接続
    SQL-->>Run: クエリ結果
    Run-->>Client: HTTP 200

Unix Socketを使うため、IPアドレスやVPC Connectorの設定は要りません。

使用するTerraformコード
#

PasswordをStateへ残しにくくする
#

Terraform 1.11のWrite-only Argumentを使います。

resource "google_sql_user" "app" {
  name                = var.db_user
  instance            = google_sql_database_instance.postgres.name
  password_wo         = var.db_password
  password_wo_version = var.db_password_version
}

resource "google_secret_manager_secret_version" "db_password" {
  secret                 = google_secret_manager_secret.db_password.id
  secret_data_wo         = var.db_password
  secret_data_wo_version = var.db_password_version
}

password_woとsecret_data_woは値そのものをStateへ保存しません。Password変更時はVersion値も増やして更新をTerraformへ伝えます。ただし入力元のtfvarsやCI Logの保護は別途必要です。

Cloud RunからCloud SQLへ接続する
#

Cloud Run TemplateにCloud SQL Volumeを定義し、/cloudsqlへMountします。Applicationは/cloudsql/<connection-name>のUnix Socketを使う構成です。Runtime Service Accountには次だけを付与します。

  • roles/cloudsql.client
  • 対象Secretのroles/secretmanager.secretAccessor
  • Artifact Registry Repositoryのroles/artifactregistry.reader

実行手順
#

cd Advanced-Examples/06-cloudrun-cloudsql-postgresql
cp terraform.tfvars.example terraform.tfvars
terraform init
terraform fmt -check
terraform validate
terraform apply

最初はHello ImageでInfrastructureを作成します。Artifact Registry、Cloud SQL、Secret Manager、Cloud Runなど合わせて17リソースです。Instance作成には10分ほどかかりました。

Apply complete! Resources: 17 added, 0 changed, 0 destroyed.

Outputs:

app_image_example = "asia-northeast1-docker.pkg.dev/YOUR_PROJECT_ID/tf-adv-run-sql/cloudrun-sql:latest"
database_name = "appdb"
database_user = "appuser"
instance_connection_name = "YOUR_PROJECT_ID:asia-northeast1:tf-adv-run-sql-pg"
service_url = "https://tf-adv-run-sql-ldzccxoqeq-an.a.run.app"
sql_instance_name = "tf-adv-run-sql-pg"

付属Python ApplicationをBuildしてArtifact RegistryへPushします。container_imageを変更して再度Applyします。

gcloud auth configure-docker REGION-docker.pkg.dev
docker build -t REGION-docker.pkg.dev/PROJECT/REPOSITORY/app:latest app
docker push REGION-docker.pkg.dev/PROJECT/REPOSITORY/app:latest
terraform apply

GCP側で確認する
#

Cloud SQL Instanceの状態を確認します。

gcloud sql instances describe "$(terraform output -raw sql_instance_name)"
state: RUNNABLE
databaseInstalledVersion: POSTGRES_15_18

RUNNABLEはInstanceが起動していることを示すだけで、Cloud Runから実際に接続できることは別に確認します。Cloud RunのURLへID Tokenを付けて呼び出します。

curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  "$(terraform output -raw service_url)"
{"connection":"cloud-sql-unix-socket","database":"appdb","status":"ok","user":"appuser"}

status: okとともに、Unix Socket経由でappdbへappuserとして接続できたことが分かります。

ヘルスチェックのパスにhealthzを使わない。 Cloud Runのデフォルトドメイン(*.run.app)では、パス/healthzがGoogle Frontendの段階で予約扱いになり、コンテナへ到達せず404になります(/healthなど他のパスは到達します)。このサンプルのApplicationも当初/healthzを使っていましたが、実際には到達できないEndpointでした。/healthにリネームして解消しています。

curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  "$(terraform output -raw service_url)/health"
{"status":"ok"}

後片付けと注意点
#

terraform destroy
Destroy complete! Resources: 17 destroyed.

Cloud SQL Instanceは停止中でも課金が発生し得るため、検証後は必ず削除します。ProductionではDeletion Protection、Backup、PITR、HA、Private IP、Connection Pool、Migration方法を追加で検討してください。Secretを環境変数として渡す場合、Application Logへ出力しないことも重要です。

まとめ
#

  • Cloud RunとCloud SQLをUnix Socketで接続できる
  • Runtime SAのIAMをResourceごとに最小化できる
  • Secret ManagerからDatabase Passwordを注入できる
  • Write-only ArgumentでPasswordをTerraform Stateへ残しにくくできる

参考資料
#

次回
#

Advanced Examples 01〜06を通して、複数のGoogle Cloud Serviceを安全に連携する構成を確認できました。次はGKEプライベートクラスタと踏み台VMを作成します。

Terraform-GoogleCloud - この記事は連載の一部です
パート 22: この記事

関連記事

TerraformでArtifact RegistryのコンテナをCloud Runへデプロイしてみた
2 分
Terraform GoogleCloud CloudRun ArtifactRegistry
Terraform StateをCloud Storage Remote Backendへ移行してみた
2 分
Terraform GoogleCloud CloudStorage
TerraformからGoogle Cloudへ接続してProject情報を取得してみた
3 分
Terraform GoogleCloud
TerraformでArtifact RegistryのDocker Repositoryを作成してみた
2 分
Terraform GoogleCloud Docker
TerraformでBigQuery DatasetとTableを作成してみた
2 分
Terraform GoogleCloud
TerraformでCloud KMSのKeyRingとCryptoKeyを作成してみた
2 分
Terraform GoogleCloud