メインコンテンツへスキップ
友田 陽大
Google Cloud Run 本番運用
GCP
Cloud Run
CI/CD
DevOps
Workload Identity
セキュリティ
インフラ
Terraform

Cloud Run の CI/CD:Cloud Build / GitHub Actions × Workload Identity で鍵レス・Blue/Green・カナリアを実コードで

Cloud Runへの継続的デプロイを本番品質で組む実装ガイド。Artifact Registry、Cloud BuildとGitHub Actions(Workload Identity Federationで鍵レス)の使い分け、--no-traffic+タグURLで検証してからカナリア→Blue/Green→即時ロールバック、DBマイグレーションのジョブ分離、Terraformとの責務分離までを、cloudbuild.yaml・GitHub Actions・gcloudの実コードで解説します。

公開日
読了時間
8分
著者
友田 陽大
シェア

「デプロイが怖い」——本番のコンテナ基盤で最も避けたい感覚です。怖さの正体は、戻せないことと、何が変わったか分からないこと。Cloud RunのCI/CDは、この2つを構造的に潰せます。リビジョンが不変だから再ビルドなしで即座に戻せるし、責務を分離すれば何が変わったかが常に明確になります。

私は放送事業者向けプラットフォームをGCPで運用する中で、Cloud Buildでstg/prodを出し分け、Terraformは『インフラ構成』・Cloud Buildは『イメージと最新env』と責務を分離し、DBマイグレーションは専用ジョブに切り出し、CI/CDはWorkload Identity Federationで鍵レス——という構成で、止まらない社内プラットフォームを回していました。本記事はその設計を、Google Cloud公式ドキュメントに忠実に、実コードで再現します。

本番運用の全体像は Cloud Run 本番運用ガイド、長時間ジョブそのものの設計は Jobs / Workflows ガイド を参照してください。


設計原則:3つの責務を分ける

Cloud RunのCI/CDで事故が起きるのは、たいてい責務が混ざっているときです。最初に境界を引きます。

責務担うもの真実源
アプリの中身コンテナイメージ(CIでビルド → Artifact Registry)Git(コミットSHA=イメージタグ)
インフラ構成サービス・SA・VPC・スケール設定(Terraform)Terraform state
どのリビジョンに流すかトラフィック配分(不変リビジョン)Cloud Runのトラフィック設定

この分離が効くのは——「イメージタグ=コミットSHA」にすれば、本番で動いているものがどのコミットか一意に追跡でき、インフラ変更(Terraform)とアプリ変更(イメージ)が混ざらないから。latest タグは使いません(何が動いているか分からなくなる)。


Artifact Registry:イメージの置き場所

イメージは Artifact Registry(旧Container Registry)に置きます。まずリポジトリを作ります。

gcloud artifacts repositories create app \
  --repository-format=docker \
  --location=asia-northeast1 \
  --description="app container images"
# イメージURLの形:asia-northeast1-docker.pkg.dev/PROJECT_ID/app/api:GIT_SHA

経路A:Cloud Build(GCPネイティブで完結)

GCPだけで完結させたいなら Cloud Build。cloudbuild.yaml に「ビルド → プッシュ → デプロイ」を宣言します。

# cloudbuild.yaml — push trigger で起動。$SHORT_SHA はCloud Buildが注入する。
steps:
  # 1. ビルド(コミットSHAをタグに)
  - name: "gcr.io/cloud-builders/docker"
    args:
      ["build", "-t",
       "${_REGION}-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA", "."]
  # 2. Artifact Registry へプッシュ
  - name: "gcr.io/cloud-builders/docker"
    args:
      ["push",
       "${_REGION}-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA"]
  # 3. トラフィックを流さずにデプロイ(タグURLで検証してから昇格する)
  - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
    entrypoint: gcloud
    args:
      ["run", "deploy", "api",
       "--image", "${_REGION}-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA",
       "--region", "${_REGION}",
       "--no-traffic", "--tag", "sha-$SHORT_SHA"]
images:
  - "${_REGION}-docker.pkg.dev/$PROJECT_ID/app/api:$SHORT_SHA"
substitutions:
  _REGION: asia-northeast1
options:
  logging: CLOUD_LOGGING_ONLY

GitHubリポジトリに push トリガーを接続すれば、コミットごとに自動でビルド・デプロイ(トラフィックは流さない)まで走ります。

gcloud builds triggers create github \
  --repo-name=app --repo-owner=YOUR_ORG \
  --branch-pattern="^main$" \
  --build-config=cloudbuild.yaml

経路B:GitHub Actions × Workload Identity(鍵レス)

既存CIがGitHub Actionsなら、こちらが自然です。サービスアカウント鍵を発行せず、Workload Identity Federation(WIF)でGCPに認証します。

# .github/workflows/deploy.yml
name: deploy
on:
  push:
    branches: [main]

permissions:
  contents: read
  id-token: write   # これが無いとGitHubはOIDCトークンを注入せず、認証が失敗する

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # 鍵レス認証:プールとプロバイダはWIFで事前設定(下記リンク参照)
      - id: auth
        uses: google-github-actions/auth@v3
        with:
          # ★プロジェクト「番号」を含むフルパス。プロジェクトIDではない。
          workload_identity_provider: "projects/123456789/locations/global/workloadIdentityPools/github/providers/app-repo"
          service_account: "deployer@PROJECT_ID.iam.gserviceaccount.com"

      - uses: google-github-actions/deploy-cloudrun@v3
        with:
          service: api
          region: asia-northeast1
          image: asia-northeast1-docker.pkg.dev/PROJECT_ID/app/api:${{ github.sha }}
          flags: "--no-traffic --tag=sha-${{ github.sha }}"

WIFのプール/プロバイダ設定(Attribute Conditionで自分のリポジトリだけ許可する等)は本記事では繰り返しません。 設定の勘所——assertion.repository の一致を必ず付ける・sub をワイルドカードにしない——は専用記事 GitHub Actionsを鍵レスにする にまとめています(DRY)。デプロイ用SAには最小権限(roles/run.developer+Artifact Registry読み取り+ランタイムSAへの roles/iam.serviceAccountUser)だけを与えます。


安全な出荷:検証 → カナリア → Blue/Green → 即時ロールバック

CIは「トラフィックを流さずにデプロイ」までで止めるのが肝です。人間(または自動チェック)が検証してから昇格します。リビジョンが不変だからこそ、この段階制御が安全に効きます。

# 1. タグURLで隔離検証(本番トラフィックに影響しない)
#    → https://sha-abc123---api-xxxxx.a.run.app をスモークテスト
curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
  https://sha-abc123---api-xxxxx.a.run.app/healthz

# 2. 健全なら5%だけカナリア
gcloud run services update-traffic api --region asia-northeast1 \
  --to-tags sha-abc123=5

# 3. エラー率・レイテンシを監視しつつ段階引き上げ(5 → 25 → 50%)
gcloud run services update-traffic api --region asia-northeast1 \
  --to-tags sha-abc123=50

# 4. 問題なければ100%へ(Blue/Green切替)
gcloud run services update-traffic api --region asia-northeast1 --to-latest

# ── 異常を検知したら、旧リビジョンへ即時ロールバック(再ビルド不要)──
gcloud run services update-traffic api --region asia-northeast1 \
  --to-revisions api-00021-prev=100

ロールバックが「旧リビジョンに100%戻すだけ」で完結するのがCloud Run最大の安全装置です。イメージの作り直しもデプロイのやり直しも要りません。これを CI/CD の標準手順に組み込んでおけば、夜間の障害でも数十秒で平常へ戻せます。


DBマイグレーションはデプロイから分離する

最も事故りやすいのがスキーマ変更です。アプリのロールアウトとマイグレーションを同じステップに混ぜると、ロールバックしたときに「コードは古いがスキーマは新しい」不整合に陥ります。正解は専用のCloud Run Jobに切り出し、前方/後方互換のある段階適用にすること。

# マイグレーション専用ジョブを用意し、デプロイとは独立に実行する
gcloud run jobs deploy db-migrate \
  --image asia-northeast1-docker.pkg.dev/PROJECT_ID/app/migrate:${GIT_SHA} \
  --region asia-northeast1 \
  --service-account migrator@PROJECT_ID.iam.gserviceaccount.com \
  --max-retries 0           # マイグレーションは安易にリトライさせない
gcloud run jobs execute db-migrate --region asia-northeast1 --wait

ゼロダウンタイムのスキーマ変更は「①互換性のある列追加 → ②新旧両対応のコードをデプロイ → ③バックフィル → ④古い参照を消すコード → ⑤旧列削除」という多段リリースにします。設計の詳細は ゼロダウンタイムのスキーマ移行 を参照してください(DBはCloud SQL/PostgreSQLでも原則は同じ)。ジョブの作り込み自体は Jobs / Workflows ガイド へ。


Cloud Build と GitHub Actions、どちらを選ぶか

Cloud BuildGitHub Actions
認証GCP内なのでネイティブに簡単WIFで鍵レス(設定は要る)
エコシステムGCPに最適化広い(Lint/テスト/他クラウドと統合しやすい)
向くチームGCP中心・インフラもCloud Buildに寄せたい既にGitHub Actionsが標準
ビルド環境マネージド・並列・キャッシュランナー(self-hosted可)

正解は「チームの既存CIに寄せる」こと。どちらも --no-traffic+タグ検証→カナリア→Blue/Greenという出荷フローは同じに作れます。私のプロジェクトでは、ビルド/デプロイのコアは Cloud Build に集約しつつ、GitHub 側で CodeQL・依存更新・テストを回す併用構成にしていました。


本番投入チェックリスト

  • イメージタグは コミットSHAlatest を使わない)
  • Terraform=インフラ / イメージ=アプリ で責務分離
  • CI/CDは WIFで鍵レスid-token: write を付ける)
  • デプロイ用SAは 最小権限run.developer+AR読み取り+serviceAccountUser
  • CIは --no-traffic--tag まで。昇格は検証後
  • カナリア → Blue/Green の段階出荷をスクリプト化
  • 即時ロールバック手順(旧リビジョンへ100%)を runbook に
  • DBマイグレーションは専用ジョブに分離し、段階適用にする
  • stg環境で本番同等の検証(WAF含む)を先に通す

まとめ:デプロイを「怖くない」作業にする

Cloud RunのCI/CDは、責務分離(イメージ/インフラ/トラフィック)不変リビジョンによる段階出荷で、「戻せない・何が変わったか分からない」という恐怖を構造的に消せます。鍵レス(WIF)で認証情報の漏洩リスクも断ち、マイグレーションを分離して不整合も防ぐ。これで少人数でも、本番デプロイを淡々とこなせるようになります。

全体設計は Cloud Run 本番運用ガイド、コストは 並行性・課金ガイド、長時間処理は Jobs / Workflows ガイド へ。GCPのCI/CD整備や鍵レス化の伴走が必要なら、実運用の知見を踏まえてお手伝いします。

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・ 実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この記事の実装を、案件として承ります

GCP / Cloud Run のコンテナ基盤を、設計から本番運用・コスト最適化まで

Cloud Run(サービス+ジョブ)でのコンテナ基盤構築、AWS/オンプレからの移行、CI/CD(Workload Identityで鍵レス)、Cloud Armor・最小権限の多層防御、並行性と課金モデルのコスト最適化まで。放送事業者向けプラットフォームをGCPにIaCで構築・運用した知見で、速く・安く・安全に伴走します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

あわせて読みたい