概要
Cloud SQL Auth Proxy(サイドカー)
Cloud Run v2のマルチコンテナ機能でAuth Proxyをサイドカーとして動かす。キーファイル不要でIAMベース認証。
Datastream CDC
Cloud SQLのPostgreSQL logical replicationを使ったCDC(Change Data Capture)でBigQueryにリアルタイム同期。
Terraform 環境分離
GCPではプロジェクト = 課金・権限の分離単位。環境ごとにプロジェクトを分け、Terraformで管理する。
Workload Identity Federation
GitHub ActionsからGCPにデプロイする際にサービスアカウントキー(JSONファイル)が不要になる。
問題
ECサイトのプロモーション管理システムをGCP上で運用しています。現在の構成:
[Cloud Run v2] → [Cloud SQL (PostgreSQL)] → [BigQuery(日次バッチ)]
現在の課題
| 課題 | 詳細 |
|---|---|
| 接続数過多 | キャンペーンイベント時に Cloud Run に急激なトラフィック(通常の10〜50倍)。Cloud SQL への接続数が上限(500接続)に達してタイムアウト |
| リアルタイム分析なし | BigQuery へのデータ同期が日次バッチのみでリアルタイム分析ができない |
| コスト管理困難 | すべてのサービスが同一プロジェクトに混在しており、チーム別コスト管理が難しい |
制約
- GCPのみ使用(他クラウドは不可)
- IaC は Terraform 1.8+ を使用
- Cloud Run v2(第2世代)を前提
- Workload Identity Federation を使用してサービスアカウントキーを廃止すること
ヒント(段階的開示)
ヒント1 — 各問題の方向性
- 接続数問題: Cloud Runが直接Cloud SQLに接続していることが問題の根本。間に接続プーリング層を挟む必要がある。
- リアルタイムパイプライン:
Datastream(CDC)→BigQuery Storage Write APIの構成が定番。 - プロジェクト分離:
terraform.tfvarsごとに環境を分ける。共通リソース(VPC, IAM)はsharedプロジェクトで管理。
ヒント2 — 接続プーリング(推奨構成)
Cloud Run → Cloud SQL Auth Proxy (Cloud Run sidecar) → Cloud SQL
↓
min-instances=N で接続を維持
SQLAlchemy: pool_size=5, max_overflow=0
# 接続数計算:
# max_instances=50 × pool_size=5 = 最大250接続
# (500接続上限の50%で余裕あり)
ヒント3 — Terraform骨格とWorkload Identity
# environments/prod/main.tf
module "cloud_run" {
source = "../../modules/cloud-run"
project_id = var.project_id
region = var.region
}
# Workload Identity Federation(GitHub Actions 用)
resource "google_iam_workload_identity_pool" "github" {
workload_identity_pool_id = "github-actions-pool"
project = var.project_id
}
改善後アーキテクチャ全体図
解答① 接続数問題: Cloud Run + Cloud SQL Auth Proxy(サイドカー)
Cloud Run v2 では マルチコンテナ(サイドカー) が利用可能。Cloud SQL Auth Proxy をサイドカーとして動かす。
# modules/cloud-run/main.tf
resource "google_cloud_run_v2_service" "app" {
name = "${var.service_name}-${var.env}"
location = var.region
project = var.project_id
template {
scaling {
min_instance_count = 2 # 最小2インスタンスで接続維持
max_instance_count = 50
}
service_account = google_service_account.app.email
containers {
name = "app"
image = var.app_image
env {
name = "DB_HOST"
value = "127.0.0.1" # Cloud SQL Auth Proxy 経由
}
env {
name = "DB_POOL_SIZE"
value = "5" # インスタンスあたりの接続数を制限
}
}
# Cloud SQL Auth Proxy をサイドカーとして追加
containers {
name = "cloud-sql-proxy"
image = "gcr.io/cloud-sql-connectors/cloud-sql-proxy:2"
args = [
"--structured-logs",
"--port=5432",
var.cloud_sql_connection_name,
]
resources {
limits = {
cpu = "0.5"
memory = "256Mi"
}
}
}
}
}
# アプリのサービスアカウント(Cloud SQL Client ロールのみ)
resource "google_service_account" "app" {
account_id = "${var.service_name}-sa-${var.env}"
display_name = "${var.service_name} Service Account (${var.env})"
project = var.project_id
}
resource "google_project_iam_member" "cloudsql_client" {
project = var.project_id
role = "roles/cloudsql.client"
member = "serviceAccount:${google_service_account.app.email}"
}
# アプリ側の接続プール設定 (SQLAlchemy)
from sqlalchemy import create_engine
engine = create_engine(
f"postgresql+psycopg2://user:password@127.0.0.1:5432/db",
pool_size=5, # Cloud Run 1インスタンスあたり5接続
max_overflow=0, # 上限を厳格に守る
pool_timeout=30,
pool_pre_ping=True, # 接続の死活チェック
)
# 接続数計算:
# max_instances=50 × pool_size=5 = 最大250接続(500接続上限の50%で余裕あり)
解答② リアルタイムパイプライン: Datastream → BigQuery
# modules/datastream/main.tf
resource "google_datastream_connection_profile" "source" {
display_name = "cloudsql-postgres-${var.env}"
location = var.region
connection_profile_id = "cloudsql-postgres-${var.env}"
project = var.project_id
postgresql_profile {
hostname = var.cloud_sql_private_ip
port = 5432
username = var.db_replication_user
password = var.db_replication_password # Secret Manager から注入
database = var.db_name
}
}
resource "google_datastream_stream" "main" {
display_name = "cloudsql-to-bigquery-${var.env}"
stream_id = "cloudsql-to-bigquery-${var.env}"
location = var.region
project = var.project_id
desired_state = "RUNNING"
source_config {
source_connection_profile = google_datastream_connection_profile.source.id
postgresql_source_config {
publication = "datastream_publication"
replication_slot = "datastream_slot"
include_objects {
postgresql_schemas {
schema = "public"
postgresql_tables { table = "orders" }
postgresql_tables { table = "campaigns" }
}
}
}
}
destination_config {
destination_connection_profile = google_datastream_connection_profile.destination.id
bigquery_destination_config {
data_freshness = "900s" # 最大遅延15分
single_target_dataset {
dataset_id = "${var.project_id}:${var.bq_dataset_id}"
}
}
}
backfill_all {}
}
コスト試算: Datastream 約$0.50/GB。月間CDCデータ量100GB想定 → 約$50/月。BigQuery Storage は最初の10GBは無料、以降$0.02/GB。
解答③ プロジェクト分離とコスト管理
# プロジェクト構成
# gcp-org/
# ├── shared/ # VPC, Cloud DNS, Artifact Registry
# ├── dev/ # 開発環境
# ├── staging/ # ステージング環境
# └── prod/ # 本番環境
# environments/prod/terraform.tfvars
project_id = "myapp-prod-123456"
env = "prod"
region = "asia-northeast1"
app_image = "asia-northeast1-docker.pkg.dev/myapp-shared/images/app:v1.2.3"
# environments/dev/terraform.tfvars
project_id = "myapp-dev-654321"
env = "dev"
region = "asia-northeast1"
app_image = "asia-northeast1-docker.pkg.dev/myapp-shared/images/app:latest"
# ラベルでコスト管理(全リソースに付与)
locals {
common_labels = {
env = var.env
team = "mops"
managed = "terraform"
project = "promotion-system"
}
}
# Workload Identity Federation(GitHub Actions 用)
resource "google_iam_workload_identity_pool" "github" {
workload_identity_pool_id = "github-actions-pool"
project = var.project_id
}
resource "google_iam_workload_identity_pool_provider" "github" {
workload_identity_pool_id = google_iam_workload_identity_pool.github.workload_identity_pool_id
workload_identity_pool_provider_id = "github-provider"
project = var.project_id
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.actor" = "assertion.actor"
"attribute.repository" = "assertion.repository"
}
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
}
# terraform.tf
terraform {
required_version = ">= 1.8"
backend "gcs" {
bucket = "myapp-tfstate-prod"
prefix = "terraform/state"
}
required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.0"
}
}
}
ポイント解説
1
Cloud Run v2 マルチコンテナ(サイドカー)
Cloud SQL Auth Proxy をサイドカーにすることで、アプリコンテナはサービスアカウントキー不要でCloud SQLに接続できる。 IAMで最小権限を実現。
Cloud SQL Auth Proxy をサイドカーにすることで、アプリコンテナはサービスアカウントキー不要でCloud SQLに接続できる。 IAMで最小権限を実現。
2
pool_size × max_instances = 総接続数
Cloud Runはオートスケールするため、SQLAlchemy の
Cloud Runはオートスケールするため、SQLAlchemy の
pool_size を小さく保ち、
max_overflow=0 で接続数上限を厳格に管理する。
接続数 = インスタンス数 × pool_size であることを意識する。
(50 × 5 = 250接続、500接続上限の50%で余裕あり)
3
Datastream CDC
Cloud SQL のPostgreSQLの
Cloud SQL のPostgreSQLの
logical replicationを使ったCDC(Change Data Capture)。
日次バッチと違いINSERT/UPDATE/DELETEをリアルタイムに拾える。
data_freshness=900s で最大15分の遅延でBigQueryに反映。
4
環境別プロジェクト分離
GCPでは「プロジェクト = 課金・権限の分離単位」なので、環境ごとにプロジェクトを分けるのが正解。 Terraform は環境ごとに
GCPでは「プロジェクト = 課金・権限の分離単位」なので、環境ごとにプロジェクトを分けるのが正解。 Terraform は環境ごとに
root module を持ち、共通ロジックは modules/ に切り出す。
全リソースに labels = {env, team} を付与 → 請求エクスポートでチーム別コスト集計が可能。
5
Workload Identity Federation
GitHub ActionsからGCPにデプロイする際にサービスアカウントキー(JSONファイル)が不要になる。 OIDCトークンをGCPが直接信頼する仕組み。サービスアカウントキーのローテーション管理が不要になる。
GitHub ActionsからGCPにデプロイする際にサービスアカウントキー(JSONファイル)が不要になる。 OIDCトークンをGCPが直接信頼する仕組み。サービスアカウントキーのローテーション管理が不要になる。
実務への応用
-
タイムセール開始時:
min-instances=2に設定することで、オートスケール前の初回リクエストでも 接続プールが温まっており、Cloud SQLタイムアウトが発生しない。 - Datastreamでキャンペーンデータをリアルタイム集計: BigQueryに流したデータをLooker StudioやDataDogで可視化し、 「キャンペーン開始後のCV数・GMVをリアルタイム監視」できるようになる。
-
Terraformでコスト管理:
labels = {env = "prod", team = "mops"}を全リソースに付与 → GCPの請求エクスポートをBigQueryに送り、ラベルでチーム別コストを集計できる。
次のステップ
発展問題:
Cloud Spanner vs Cloud SQL の選択基準(グローバル展開・トランザクション保証の観点)
- 参考:
google_cloud_run_v2_serviceTerraform公式ドキュメント - 参考: Datastream Terraform provider
- 参考: Cloud SQL Auth Proxy v2 マルチコンテナガイド
今日のまとめ
Cloud Runの接続数問題は「サイドカーAuth Proxy + pool_size制御」で解決し、
リアルタイム分析はDatastreamのCDCで実現。
Terraform環境分離は「プロジェクト単位 + Workload Identity」が2026年のベストプラクティス。 全リソースにラベルを付与することでチーム別コスト管理も自動化できる。
Terraform環境分離は「プロジェクト単位 + Workload Identity」が2026年のベストプラクティス。 全リソースにラベルを付与することでチーム別コスト管理も自動化できる。