B システム設計/インフラ — Cloud Run v2 × Terraform 1.8+ × Secret Manager Volume Mount × 最小権限 IAM × VPC Connector × カナリアデプロイ(MOps Bad→Good Terraform)

2026-06-09 (Day 64) 火曜 B: システム設計/インフラ ★★★★☆ Cloud Run v2 / Terraform 1.8+ Secret Manager / VPC Connector / IAM Conditions

概要

🔒

INGRESS_TRAFFIC_INTERNAL_ONLY で内部 API を保護

INGRESS_TRAFFIC_ALL は Cloud Run をインターネットに公開する。内部バッチ API には INGRESS_TRAFFIC_INTERNAL_ONLY を設定し、VPC 内(+ Cloud LB)からのみアクセスを受け付けることで不正アクセスのリスクを排除する。

🔑

allUsers 禁止 — invoker は特定 SA のみ

allUsers への roles/run.invoker は認証なし公開 API になる。Argo Workflows GSA のサービスアカウントのみに Invoker 権限を付与し、Pod は OIDC トークンで認証する。

📂

Secret Manager Volume Mount でシークレット漏洩防止

env.value の平文は Terraform state / Cloud Console で露出する。volumes + volume_mounts でファイルとしてマウントすると環境変数に展開されず、default_mode = 292(0444)で読み取り専用を強制できる。

min_instance_count = 1 でコールドスタート排除

min_instance_count = 0 はアイドル後にインスタンスがゼロになり、Argo Workflows が呼び出した際に 2〜10 秒のコールドスタートが発生する。min_instance_count = 1 で常時1台をウォーム状態に保つ。

問題

ECサイトの MOps チームでは、Argo Workflows バッチ処理から呼び出す内部 API を Cloud Run v2 でホストしている。現在の Terraform / Cloud Run 設定には 6つの設計上の問題 が潜んでいる。問題点を全て洗い出し、Cloud Run v2 × Terraform 1.8+ × Workload Identity Federation × VPC Connector × Secret Manager × 最小権限 IAM の最新プラクティスに従って修正せよ。

制約・前提条件

  • Cloud Run v2(google_cloud_run_v2_service)を Terraform 1.8+ で管理
  • バッチワーカー(GKE Autopilot Pod)→ Cloud Run API の通信は VPC 内折返し(Private Ingress)
  • シークレット(DB接続文字列・API キー)は Secret Manager で管理し、Cloud Run にマウント
  • Workload Identity Federation(WIF)でサービスアカウントキー不要
  • Cloud Run のサービスアカウントには最小権限のみ付与
  • リビジョントラフィック制御(ブルーグリーン / カナリアデプロイ対応)
期待する回答形式: 問題点の列挙(番号付き)+ 改善後 Terraform コード(全ブロック)+ 設計意図の説明

悪い Terraform (Before)

この Terraform コードには 6つの設計上の問題 が隠れています。
bad_cloud_run.tf — 問題だらけの Cloud Run v2 定義
resource "google_cloud_run_v2_service" "mops_api" {
  name     = "mops-internal-api"
  location = "asia-northeast1"
  project  = var.project_id

  # 問題①: INGRESS_TRAFFIC_ALL はインターネット公開
  ingress = "INGRESS_TRAFFIC_ALL"

  template {
    service_account = google_service_account.cloud_run_sa.email

    containers {
      image = "asia-northeast1-docker.pkg.dev/my-project/mops/api:latest"

      # 問題③: 平文シークレット(Terraform state に露出)
      env {
        name  = "DB_CONNECTION_STRING"
        value = "postgresql://user:password@host:5432/db"
      }
      env {
        name  = "EXTERNAL_API_KEY"
        value = "sk-supersecret-api-key-12345"
      }
    }

    # 問題⑤: min_instance_count=0 でコールドスタート発生
    scaling {
      min_instance_count = 0
      max_instance_count = 10
    }
  }
  # 問題⑥: traffic ブロックなし(カナリアデプロイ不可)
}

# 問題②: allUsers に invoker 権限(認証なし公開)
resource "google_cloud_run_v2_service_iam_member" "invoker_public" {
  project  = var.project_id
  location = google_cloud_run_v2_service.mops_api.location
  name     = google_cloud_run_v2_service.mops_api.name
  role     = "roles/run.invoker"
  member   = "allUsers"
}

# 問題④: roles/editor は過大権限
resource "google_project_iam_member" "cloud_run_editor" {
  project = var.project_id
  role    = "roles/editor"
  member  = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}
問題点サマリー(6点)
1ingress = "INGRESS_TRAFFIC_ALL" — 内部 API をインターネットに公開。INGRESS_TRAFFIC_INTERNAL_ONLY に変更し VPC 内に閉じる
2allUsers への roles/run.invoker — 認証なし公開 API になる。Argo Workflows GSA のみに IAM Binding を設定する
3平文シークレット(env.value — Terraform state / Cloud Console / gcloud run services describe で露出。Secret Manager Volume Mount に変更する
4roles/editor は過大権限 — GCP プロジェクト全体への書き込み権限。必要最小権限(BQ dataEditor + pubsub.publisher + secretmanager.secretAccessor)に絞る
5min_instance_count = 0(コールドスタート) — アイドル後に 2〜10秒の起動待ちが発生。min_instance_count = 1 + cpu_idle = false でウォーム状態を維持する
6traffic ブロックなし — カナリアデプロイ / ブルーグリーン切り替えが不可。traffic ブロックを追加してリビジョン管理を可能にする

ヒント(段階的開示)

ヒント1 — 方向性
Cloud Run v2 では ingress = "INGRESS_TRAFFIC_ALL" はインターネットからもアクセスを許可する。VPC 内専用 API には INGRESS_TRAFFIC_INTERNAL_ONLY が適切。allUsers への roles/run.invoker 付与は認証なし公開になる。Terraform で Cloud Run サービスアカウントに roles/editor を付与するのは過大権限。Secret の env.value 平文は Terraform state ファイルに記録されるため漏洩リスクが高い。
ヒント2 — アプローチ
  • 問題①: ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY" に変更し、GKE Pod → Cloud Run の通信を VPC 内に閉じる
  • 問題②: allUsers を削除し、Argo Workflows GSA の SA メールのみに roles/run.invoker を付与
  • 問題③: volumes + volume_mounts で Secret Manager からファイルとしてマウント。default_mode = 292(0444)で読み取り専用を強制
  • 問題④: roles/editor を削除し roles/bigquery.dataEditor + roles/pubsub.publisher + roles/secretmanager.secretAccessor の3権限を個別付与
  • 問題⑤: min_instance_count = 1 + cpu_idle = false でコールドスタートを排除
  • 問題⑥: traffic ブロックで TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST を指定し、カナリアデプロイ時はリビジョン名で分割する
ヒント3 — コードの骨格
# Cloud Run v2 の正しい ingress 制御と Secret Volume Mount
resource "google_cloud_run_v2_service" "mops_api" {
  name    = "mops-internal-api"
  ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY"  # 修正①

  template {
    # Secret Manager ボリュームマウント 修正③
    volumes {
      name = "db-secret"
      secret {
        secret       = google_secret_manager_secret.db_conn.secret_id
        default_mode = 292  # 0444 読み取り専用
        items {
          version = "latest"
          path    = "db_connection_string"
          mode    = 292
        }
      }
    }
    containers {
      image = "...@sha256:${var.api_image_digest}"
      volume_mounts {
        name       = "db-secret"
        mount_path = "/secrets/db"
      }
      resources {
        limits   = { cpu = "1", memory = "512Mi" }
        cpu_idle = false  # 修正⑤
      }
    }
    scaling {
      min_instance_count = 1   # 修正⑤
      max_instance_count = 10
    }
  }

  # 修正⑥: traffic ブロック
  traffic {
    type    = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
    percent = 100
  }
}

# 修正②: Argo Workflows SA のみ invoker
resource "google_cloud_run_v2_service_iam_member" "invoker_argo" {
  name   = google_cloud_run_v2_service.mops_api.name
  role   = "roles/run.invoker"
  member = "serviceAccount:${google_service_account.argo_workflows_sa.email}"
}

問題点分析(6点)

#問題点分類改善方法
1ingress = "INGRESS_TRAFFIC_ALL"(インターネット公開)セキュリティINGRESS_TRAFFIC_INTERNAL_ONLY に変更
2allUsers への roles/run.invoker(認証なし)セキュリティArgo Workflows GSA のみに IAM Binding
3平文シークレット env.value(Terraform state 露出)セキュリティSecret Manager Volume Mount + default_mode = 292
4roles/editor は過大権限(プロジェクト全体書き込み)最小権限BQ dataEditor + pubsub.publisher + secretAccessor の3権限
5min_instance_count = 0(コールドスタート 2〜10秒)可用性/性能min_instance_count = 1 + cpu_idle = false
6traffic ブロックなし(カナリアデプロイ不可)信頼性traffic ブロックでリビジョン管理を追加

アーキテクチャ図 — Bad vs Good の変換フロー

Bad Terraform(変更前) ① ingress = "INGRESS_TRAFFIC_ALL" インターネット全体からアクセス可能 → 内部 API が公開状態 ⚠️ MOps 内部 API が外部から丸見え ② member = "allUsers" → roles/run.invoker URL を知っていれば誰でも呼び出せる(認証なし) ⚠️ DDoS・不正呼び出し・コスト爆発リスク ③ env.value = "postgresql://user:password@host..." Terraform state / Cloud Console / gcloud describe で平文露出 ⚠️ DB 認証情報が複数箇所に平文記録 ④ roles/editor(プロジェクト全体書き込み権限) Cloud Run SA がプロジェクト内の全リソースを変更できる ⚠️ SA 侵害時の影響範囲がプロジェクト全体に及ぶ ⑤ min_instance_count = 0(コールドスタート) アイドル後に起動待ち 2〜10秒 → Argo Workflows タイムアウト ⚠️ バッチ実行が不定期に失敗 ⑥ traffic ブロックなし 新リビジョンに自動 100% ルーティング(カナリア不可) ⚠️ 不具合が即座に全トラフィックに影響 セキュリティ・可用性・コスト管理 全ての観点で問題あり(インターネット公開 API / 平文シークレット / 過大権限) Good Terraform(変更後) ① ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY" VPC 内(GKE Pod)からのみアクセス可能 / インターネット完全遮断 vpc_access.connector = mops-vpc-connector で内部通信を確立 ② member = "serviceAccount:argo-workflows-sa@..." Argo Workflows GSA のみが OIDC トークンで呼び出し可能 Pod は WIF 経由で identity token を取得して Authorization ヘッダーに付与 ③ volumes.secret + volume_mounts でファイルマウント secret_id = db_conn → /secrets/db/db_connection_string にマウント default_mode = 292(0444)読み取り専用 / state に平文なし ④ 最小権限 IAM(3権限 + IAM Conditions) bigquery.dataEditor(mops_*データセットのみ) pubsub.publisher + secretmanager.secretAccessor(対象シークレットのみ) ⑤ min_instance_count = 1 + cpu_idle = false 常時1台ウォーム状態 / コールドスタートなし / レイテンシ安定 cpu_idle = false でリクエスト待機中も CPU を割り当て(応答時間安定) ⑥ traffic ブロック(カナリアデプロイ対応) type = TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST / percent = 100 カナリア時: REVISION 旧90% + LATEST 10% → CI/CD で段階切り替え セキュリティ・可用性・コスト全てを改善 ① VPC 内閉塞 ② 認証必須 ③ シークレット保護 ④ 最小権限 ⑤ ウォーム ⑥ カナリア 修正

模範解答

resource "google_cloud_run_v2_service" "mops_api" {
  name     = "mops-internal-api"
  location = "asia-northeast1"
  project  = var.project_id

  # 修正①: VPC 内専用 Ingress(インターネットからアクセス不可)
  ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY"

  template {
    service_account = google_service_account.cloud_run_sa.email

    # GKE Pod → Cloud Run を VPC 内で通信(VPC アクセスコネクタ経由)
    vpc_access {
      connector = google_vpc_access_connector.mops_connector.id
      egress    = "PRIVATE_RANGES_ONLY"
    }

    # 修正③: DB 接続文字列を Secret Manager からボリュームマウント(平文排除)
    volumes {
      name = "db-secret"
      secret {
        secret       = google_secret_manager_secret.db_conn.secret_id
        default_mode = 292  # 0444: 読み取り専用(8進数 444 = 10進数 292)
        items {
          version = "latest"
          path    = "db_connection_string"
          mode    = 292
        }
      }
    }

    volumes {
      name = "api-key-secret"
      secret {
        secret       = google_secret_manager_secret.external_api_key.secret_id
        default_mode = 292
        items {
          version = "latest"
          path    = "api_key"
          mode    = 292
        }
      }
    }

    containers {
      # digest ピン留め(:latest タグ禁止)
      image = "asia-northeast1-docker.pkg.dev/${var.project_id}/mops/api@sha256:${var.api_image_digest}"

      # 修正③: Secret をファイルとしてマウント(環境変数への平文展開を避ける)
      volume_mounts {
        name       = "db-secret"
        mount_path = "/secrets/db"
      }
      volume_mounts {
        name       = "api-key-secret"
        mount_path = "/secrets/api"
      }

      # 非機密の設定のみ環境変数として渡す
      env {
        name  = "GCP_PROJECT_ID"
        value = var.project_id
      }
      env {
        name  = "ENV"
        value = var.environment
      }

      resources {
        limits = {
          cpu    = "1"
          memory = "512Mi"
        }
        # 修正⑤: cpu_idle = false でリクエスト処理中も CPU を確保(レイテンシ改善)
        cpu_idle = false
      }

      startup_probe {
        http_get {
          path = "/healthz"
          port = 8080
        }
        initial_delay_seconds = 10
        period_seconds        = 5
        failure_threshold     = 6   # 起動最大 40 秒待機
      }

      liveness_probe {
        http_get {
          path = "/healthz"
          port = 8080
        }
        period_seconds    = 15
        failure_threshold = 3
      }
    }

    # 修正⑤: min_instance_count = 1 でコールドスタートを排除
    scaling {
      min_instance_count = 1
      max_instance_count = 10
    }

    session_affinity = false
  }

  # 修正⑥: traffic ブロックでカナリアデプロイ対応
  traffic {
    type    = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
    percent = 100
  }

  depends_on = [
    google_secret_manager_secret_iam_member.cloud_run_sa_db,
    google_secret_manager_secret_iam_member.cloud_run_sa_api_key,
  ]
}
# Cloud Run サービスアカウント
resource "google_service_account" "cloud_run_sa" {
  account_id   = "mops-cloud-run-sa"
  display_name = "MOps Cloud Run Service Account"
  project      = var.project_id
}

# 修正④: roles/editor を削除し、必要最小権限のみ付与
# BigQuery: mops_* データセットのみ dataEditor(IAM Conditions で制限)
resource "google_project_iam_member" "cloud_run_bq_editor" {
  project = var.project_id
  role    = "roles/bigquery.dataEditor"
  member  = "serviceAccount:${google_service_account.cloud_run_sa.email}"

  condition {
    title       = "mops_dataset_only"
    description = "MOps データセットへのアクセスのみ許可"
    expression  = "resource.name.startsWith(\"projects/${var.project_id}/datasets/mops_\")"
  }
}

# Pub/Sub: メッセージ publish のみ(topic レベルで制限)
resource "google_project_iam_member" "cloud_run_pubsub_publisher" {
  project = var.project_id
  role    = "roles/pubsub.publisher"
  member  = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}

# Secret Manager: 対象シークレットのみ読み取り(プロジェクトレベルは不要)
resource "google_secret_manager_secret_iam_member" "cloud_run_sa_db" {
  project   = var.project_id
  secret_id = google_secret_manager_secret.db_conn.secret_id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}

resource "google_secret_manager_secret_iam_member" "cloud_run_sa_api_key" {
  project   = var.project_id
  secret_id = google_secret_manager_secret.external_api_key.secret_id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}

# 修正②: allUsers 削除 → Argo Workflows SA のみ invoker
resource "google_cloud_run_v2_service_iam_member" "invoker_argo" {
  project  = var.project_id
  location = google_cloud_run_v2_service.mops_api.location
  name     = google_cloud_run_v2_service.mops_api.name
  role     = "roles/run.invoker"
  member   = "serviceAccount:${google_service_account.argo_workflows_sa.email}"
}

# Cloud Load Balancer のヘルスチェック用(内部ロードバランサー経由の場合)
resource "google_cloud_run_v2_service_iam_member" "invoker_lb" {
  project  = var.project_id
  location = google_cloud_run_v2_service.mops_api.location
  name     = google_cloud_run_v2_service.mops_api.name
  role     = "roles/run.invoker"
  member   = "serviceAccount:service-${var.project_number}@serverless-robot-prod.iam.gserviceaccount.com"
}
# VPC アクセスコネクタ(GKE Pod → Cloud Run VPC 内通信)
resource "google_vpc_access_connector" "mops_connector" {
  name          = "mops-vpc-connector"
  region        = "asia-northeast1"
  project       = var.project_id
  network       = google_compute_network.mops_vpc.name
  ip_cidr_range = "10.8.0.0/28"  # /28 = 14 IP(コネクタ専用サブネット)

  # スループット設定(Cloud Run の最大インスタンス数に合わせて調整)
  min_throughput = 200   # Mbps
  max_throughput = 1000  # Mbps(max_instance_count = 10 に対応)
}

# ---- Direct VPC Egress の代替構成(コネクタ不要・推奨: 2024年以降)----
# VPC アクセスコネクタの代わりに Direct VPC Egress を使う場合:
#
# vpc_access {
#   network_interfaces {
#     network    = google_compute_network.mops_vpc.name
#     subnetwork = google_compute_subnetwork.cloud_run_subnet.name
#   }
#   egress = "PRIVATE_RANGES_ONLY"
# }
#
# Direct VPC Egress は Cloud Run v2 専用で、コネクタより低レイテンシ・
# 高スループット(最大 100 Gbps)を実現。コネクタ管理の手間も不要。
# 2024年以降の新規構成では Direct VPC Egress を推奨。

カナリアデプロイ手順(Terraform + CI/CD)

# ── 通常デプロイ(LATEST に 100%)──────────────────────────────────────────
traffic {
  type    = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
  percent = 100
}

# ── カナリアデプロイ(新リビジョン 10%、旧リビジョン 90%)──────────────────
# CI/CD パイプラインから terraform apply -var canary_percent=10 で適用
traffic {
  type     = "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
  revision = var.stable_revision_name  # 例: "mops-internal-api-00010-abc"
  percent  = 90                        # 旧リビジョンに 90%
}
traffic {
  type    = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
  percent = 10                         # 新リビジョンに 10%(カナリア)
}

# ── 完全切り替え(新リビジョンに 100%)──────────────────────────────────────
# エラーレートが閾値以下なら terraform apply -var canary_percent=100 で完全移行
traffic {
  type    = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
  percent = 100
}

GitHub Actions カナリアデプロイワークフロー

# .github/workflows/deploy-canary.yml(概略)
jobs:
  canary:
    steps:
      - name: Deploy canary (10%)
        run: terraform apply -var canary_percent=10 -var api_image_digest=${{ env.IMAGE_DIGEST }}

      - name: Monitor error rate (5 min)
        run: |
          ERROR_RATE=$(gcloud monitoring read --filter='...')
          if [ "$ERROR_RATE" -gt "1" ]; then exit 1; fi

      - name: Promote to 100%
        run: terraform apply -var canary_percent=100
問題修正内容効果
① INGRESS_TRAFFIC_ALLINGRESS_TRAFFIC_INTERNAL_ONLYインターネットからのアクセスを完全遮断
② allUsers invokerArgo Workflows GSA のみ invokerOIDC 認証必須・不正呼び出し防止
③ 平文シークレットSecret Manager Volume Mount(0444)Terraform state / Console に平文なし・ローテーション対応
④ roles/editor3権限 + IAM ConditionsSA 侵害時の影響範囲を最小化
⑤ min=0 コールドスタートmin=1 + cpu_idle=falseArgo Workflows からの呼び出し遅延をゼロに
⑥ traffic ブロックなしtraffic ブロック + カナリア戦略段階的ロールアウトで障害の影響範囲を限定

ポイント解説

1 INGRESS_TRAFFIC_INTERNAL_ONLY(問題①)
Cloud Run v2 の ingress には INGRESS_TRAFFIC_ALL(全トラフィック)/ INGRESS_TRAFFIC_INTERNAL_ONLY(VPC 内 + Cloud LB のみ)/ INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER(Cloud LB 経由のみ)の3種類がある。内部バッチ API には INTERNAL_ONLY を設定し、VPC 内の GKE Pod や Cloud Run 同士の呼び出しのみを許可する。インターネットから直接アクセスする必要がある場合は INTERNAL_LOAD_BALANCER でロードバランサー経由に限定する。
2 allUsers invoker 禁止(問題②)
allUsers は未認証アクセスを含む全ての主体を指す。roles/run.invoker を付与すると URL を知っていれば誰でも HTTP リクエストを送れる。Argo Workflows Pod は Workload Identity Federation v2 で GSA になりすまし、OIDC トークン(Authorization: Bearer <token>)を HTTP ヘッダーに付与して Cloud Run を呼び出す。Token は gcloud auth print-identity-token または GKE 内では /var/run/secrets/token から取得する。
3 Secret Manager Volume Mount(問題③)
env.value に平文を書くと Terraform state ファイル(tfstate)に平文が記録される。tfstate を GCS に保存していても、IAM で tfstate バケットにアクセスできる全ての人員に漏洩する。volumes.secret + volume_mounts でファイルとしてマウントすることで、シークレット値が Terraform state に記録されない。default_mode = 292 は8進数の 0444(所有者・グループ・その他全員に読み取り専用)を10進数で表した値。Cloud Run コンテナ内でのパーミッション制御に使う。
4 最小権限 IAM + IAM Conditions(問題④)
roles/editor は GCP プロジェクト内の殆どのリソースへの書き込み権限を含む。Cloud Run SA が侵害された場合、攻撃者はプロジェクト内のデータベース・ストレージ・Pub/Sub トピックを自由に操作できる。IAM Conditions の resource.name.startsWith(...) でリソースレベルまで絞り込むことで、仮に SA が侵害されても mops_* データセット以外の BigQuery テーブルには触れない。Secret Manager のアクセス権はシークレットレベルで個別に付与する(プロジェクトレベルは不要)。
5 コールドスタート対策(問題⑤)
Cloud Run は最後のリクエストから数分後にインスタンスをゼロにスケールダウンする。次のリクエスト時にコンテナ起動(コールドスタート)が発生し、Python コンテナでは 2〜10 秒の遅延が生じる。Argo Workflows のステップタイムアウト(デフォルト 10〜30 秒)と重なるとタイムアウトエラーになる。min_instance_count = 1 で常時1台を維持し、cpu_idle = false(CPU Always-on)でコンテナが待機中も CPU を確保することでコールドスタートを排除する(月 ~$6 のコスト増)。
6 traffic ブロックとカナリアデプロイ(問題⑥)
traffic ブロックがない場合、Terraform apply のたびに最新リビジョンに 100% のトラフィックがルーティングされる。新リビジョンにバグがあると即座に全バッチ処理に影響する。TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION で特定リビジョン名を指定し、percent を分割することでカナリアデプロイを実現できる。CI/CD パイプラインで terraform apply -var canary_percent=10 → Cloud Monitoring でエラーレート監視 → percent=100 に移行という段階的ロールアウトを自動化する。

実務への応用

  • Argo Workflows → Cloud Run 呼び出し: Argo の http テンプレートステップで Authorization: Bearer $(gcloud auth print-identity-token --audiences=https://mops-internal-api-xxxx-an.a.run.app) ヘッダーを付与。Workload Identity v2 を使うと GKE Pod から gcloud なしに直接 OIDC トークンを取得できる(/var/run/secrets/token をマウント)。
  • VPC Connector vs Direct VPC Egress: Cloud Run v2 の新規構成では Direct VPC Egress(network_interfaces ブロック)を推奨。コネクタ管理が不要で最大 100 Gbps のスループットを実現。既存の VPC Connector ベースの構成は Direct VPC Egress への移行を検討する。
  • Secret のローテーション: Secret Manager の新バージョンが追加されると、Cloud Run は version = "latest" の設定により次のデプロイ時に自動で最新バージョンを参照する。即時反映が必要な場合は Secret Manager の Pub/Sub 通知 → Cloud Run の revision_name を強制更新するパイプラインを構築する。
  • DataDog APM との連携: Cloud Run v2 でも Unified Service Tagging(DD_SERVICE / DD_ENV / DD_VERSION)を環境変数で設定し、DataDog Agent へ OTLP トレースを送信できる。cpu_idle = false はリクエスト外のバックグラウンド処理(DataDog Agent の定期送信)も正常に動作させるために有効。

今日のまとめ

Cloud Run v2 の本番設計チェックリストは「① INGRESS_TRAFFIC_INTERNAL_ONLY ② invoker は特定 SA のみ ③ Secret は Volume Mount(default_mode = 292)④ SA には最小権限 + IAM Conditions ⑤ min_instance_count = 1 でコールドスタート排除 ⑥ traffic ブロックでカナリアデプロイ対応」の6点であり、roles/editor + allUsers invoker + 平文シークレットの組み合わせは即座にセキュリティインシデントに直結する。

Terraform で IAM Conditions(resource.name.startsWith(...))を活用することで SA レベルの粗い権限制御をリソースレベルまで細粒度化でき、侵害時の爆発半径を最小化できる。

自己評価

自分の回答

気づき・メモ