Skip to content

Terraform 設計と運用

infra/terraform/ で GCP リソースを HCL で管理する。gcloud の手打ちで作らず、 全部 IaC 化することで「いつ・誰が・何を・なぜ作ったか」が PR レビュー可能になる。

採用したパターン

判断理由
Terraform (Pulumi / gcloud script を捨て)GCP の公式 provider が成熟、コミュニティ大
同 GCP project に staging + prod 同居IaM 設定の手間半分、課金も同じ、規模的に許容
state は GCS backend、env 別 prefix1 バケットで両 env、reconfigure で切替
Secret slot だけ TF、値は外部投入state に機密値を乗せない
Cloud Run image は CI が更新TF と CI が image を奪い合わない (lifecycle.ignore_changes)
per-resource IAM同 project 同居でも env 越境を遮断

ファイル構成

ファイル内容
main.tfprovider / GCS backend / API 有効化 / 共通 labels
variables.tfproject_id, region, env (staging/prod), github_repo, auth_base_url, db_tier 等
cloud_sql.tfCloud SQL (PostgreSQL 17, edition=ENTERPRISE) + DB + user + 乱数 password
cloud_run.tfCloud Run v2 (Hono API) + Unix socket mount + secret env
artifact_registry.tfDocker image 置き場 (共有)
secrets.tfSecret Manager slot + placeholder version + DATABASE_URL 組み立て
iam.tfSA × 2 + WIF Pool/Provider + per-resource IAM bindings
outputs.tfCI / Netlify が使う値の出力
envs/staging.tfvarsstaging 用の値
envs/prod.tfvarsprod 用の値 (project_id は staging と同じ)

初期セットアップ

既に staging で実行済み

このセクションは新規 env を立てるとき or 別 project に移すときの手順。

1. State 用 GCS バケットを手動作成

sh
PROJECT_ID=<shared-project-id>
gcloud config set project $PROJECT_ID

gcloud storage buckets create gs://${PROJECT_ID}-tf-state \
  --location=asia-northeast1 \
  --uniform-bucket-level-access
gcloud storage buckets update gs://${PROJECT_ID}-tf-state \
  --update-labels=app=liga,managed-by=terraform,component=tfstate
gsutil versioning set on gs://${PROJECT_ID}-tf-state

2. terraform init

sh
cd infra/terraform
terraform init \
  -backend-config="bucket=${PROJECT_ID}-tf-state" \
  -backend-config="prefix=staging"

3. apply

sh
terraform plan  -var-file=envs/staging.tfvars
terraform apply -var-file=envs/staging.tfvars

Cloud SQL の作成に 8〜12 分かかる。

4. Secret 値投入

sh
ENV=staging
printf "%s" "$(openssl rand -base64 32)" | gcloud secrets versions add BETTER_AUTH_SECRET_${ENV} --data-file=-
printf "%s" "$(openssl rand -hex 16)"    | gcloud secrets versions add CRON_SECRET_${ENV}        --data-file=-
printf "%s" "sk_test_..." | gcloud secrets versions add STRIPE_SECRET_KEY_${ENV}      --data-file=-
# ... 他の secret も同様

DATABASE_URL は TF が自動投入済み。

5. terraform output で GHA / Netlify 設定

sh
terraform output -json

を見て GitHub Secrets / Netlify env vars に値を投入。

6. DB マイグレ + seed

Cloud SQL Auth Proxy 経由で:

sh
INSTANCE=$(terraform output -raw cloud_sql_connection_name)
cloud-sql-proxy $INSTANCE &
DB_URL_RAW=$(gcloud secrets versions access latest --secret=DATABASE_URL_${ENV})
export DATABASE_URL=$(echo "$DB_URL_RAW" | sed -E 's|@/([^?]+)\?host=/cloudsql/[^"]+|@127.0.0.1:5432/\1|')
pnpm -F @liga/db migrate
pnpm -F @liga/db seed

prod を増やすとき

1. backend を prod prefix に切替

sh
cd infra/terraform
terraform init -reconfigure \
  -backend-config="bucket=${PROJECT_ID}-tf-state" \
  -backend-config="prefix=prod"

2. envs/prod.tfvars の project_id を staging と同じ値に置換

hcl
project_id    = "project-0fccf552-fc68-4756-9c1"
env           = "prod"
auth_base_url = "https://liga.netlify.app"
db_tier       = "db-custom-1-3840"
cloud_run_min_instances = 1

3. apply

sh
terraform plan  -var-file=envs/prod.tfvars
terraform apply -var-file=envs/prod.tfvars

同 GCP project に prod 用リソースが立ち上がる (env=prod の suffix 付き、HA + warm)。

日常運用 cheatsheet

やりたいことコマンド
staging に切替terraform init -reconfigure -backend-config="bucket=${PROJECT_ID}-tf-state" -backend-config="prefix=staging"
差分確認terraform plan -var-file=envs/staging.tfvars
反映terraform apply -var-file=envs/staging.tfvars
Output 取得terraform output -json
Cloud Run 状態gcloud run services describe liga-api-staging --region=asia-northeast1
Cloud SQL 接続gcloud sql connect liga-staging --user=liga
Secret 値更新gcloud secrets versions add <NAME>_staging --data-file=-
Cloud Run ログgcloud run services logs read liga-api-staging --region=asia-northeast1 --limit=50

トラブルシュート

Q. backend prefix を切り替え忘れて staging に prod apply しそうになった

  • A. terraform planliga-api-stagingliga-api-prod に rename しようとしてる、など怪しい差分が出る。 terraform init -reconfigure -backend-config="prefix=<正しい>" で戻す

Q. apply 中に Ctrl-C してしまった

  • A. lock が残ってる場合: terraform force-unlock <LOCK_ID> → 再 apply

Q. db-f1-micro が rejected される

  • A. 新規 project のデフォルト edition が ENTERPRISE_PLUS で、これだと shared-core tier が使えない。cloud_sql.tfsettings.edition = "ENTERPRISE" を明示 (実装済み)

Q. Cloud Run deploy が reserved env names: PORT で失敗

  • A. PORT は Cloud Run が ports.container_port から自動セットする予約名。TF の env block から削除 (実装済み)

Q. google_project_service.enabled が staging / prod 両方で repeat?

  • A. API は project スコープなので冪等。disable_on_destroy = false で destroy 時にも消さない

Q. WIF が staging / prod で 2 つ Pool を作る

  • A. 意図的。env ごとに独立した OIDC 信頼境界を持たせて、staging から prod の SA を借りられないように