Blogへ戻る

Language: 日本語

「デプロイは Green なのにコードが変わっていない?」 Cloud Run CD で最新リビジョンが反映されない罠

GitHub Actions の CD ワークフローがすべて完了し、綺麗な Green ランプが灯っているのを見て安心した直後……。

GitHub Actions の CD ワークフローがすべて完了し、綺麗な Green ランプが灯っているのを見て安心した直後……。

「あれ? 本番の画面を開いても、マージしたはずの変更がまったく反映されていない……?」

と青ざめた経験はありませんか?

develop や main ブランチへのマージを契機に、Google Cloud Run へ自動デプロイする CD パイプラインを組んでいた際、まさにこの 「デプロイ成功という名のサイレント見逃し」 に遭遇しました。

GitHub Actions 上はエラーが 1 件も出ず、CI/CD は完全に成功(exit code 0)として終了しているのに、Cloud Run の本番トラフィックは古いリビジョンを向き続けていたのです。

この記事では、Cloud Run のトラフィック固定(Pinned Traffic)仕様が引き起こすこの罠のメカニズムと、「デプロイコマンドの終了コード」ではなく「実際のインフラ状態(新リビジョンへのトラフィック 100%)」を検証して確実に失敗を検知する 4 ステップの CD ガードレール を解説します。

得られること

  • Cloud Run のトラフィック固定(Pinned Traffic)が引き起こす「デプロイ成功(exit 0)なのにトラフィック 0%」の仕組みがわかる
  • デプロイ後に期待リビジョンへトラフィック 100% が遷移したかを明示的にアサートする CD 検証手順が学べる
  • 「コマンドの終了コード」と「インフラの実状態(State)」の乖離を防ぐ自動化設計の原則が身につく!

なぜ「デプロイ成功なのに反映されない」が起きるのか?

通常の gcloud run deploy は、新しいリビジョンを作成し、自動的に 100% のトラフィックをその新リビジョンへ向けます。

しかし、Cloud Run には 「過去のトラフィック分割やタグ設定を永続化(Pin)する」 という強力な仕様があります。

以下のような運用を一度でも行うと、Cloud Run のトラフィックルーティングは「最新リビジョンへ自動追従するモード(--to-latest)」から 「特定のリビジョンに固定(Pin)されたモード」 へ切り替わります。

  1. カナリアリリースやパーセント分割: update-traffic で 90% / 10% などの分割設定を行った
  2. タグ付きリビジョンや Preview QA: --no-traffic やタグ(--tag)を指定してデプロイした
  3. Terraform による構成管理: Terraform 側が特定リビジョン名を指定してトラフィックを管理していた
【トラフィック固定下でのデプロイの挙動】
GitHub Actions で gcloud run deploy を実行

Cloud Run は新リビジョンを正常にビルド・起動(healthy)

しかし、トラフィック設定は「旧リビジョン固定(100%)」のまま維持される 😱

新リビジョンは「トラフィック 0%」のまま放置される

gcloud コマンドは「リビジョン作成完了」として正常終了 (exit code 0)

GitHub Actions は誇らしげに Green(成功)を表示!

gcloud run deploy からすれば、「指示通りに新しいリビジョンを作成した」という仕事は 100% 完走しているため、エラー(非 0)を出す理由がありません。

結果として、CI/CD パイプラインは Green になり、開発者は「デプロイが成功した」と信じ込みますが、実際のユーザーには旧バージョンのコードが配信され続けるという最悪のサイレント不整合が発生します。

解決策: 期待リビジョンとトラフィック 100% の明示的検証

このサイレント失敗を撲滅するために、CD パイプラインに 「リビジョンとトラフィックの明示的な 4 ステップ検証」 を組み込みました。

【追加した 4 ステップのガードレール】
1. revision suffix をコミットハッシュで固定

2. 作成された revision 名が期待値と一致するか照合

3. gcloud run services update-traffic で期待 revision = 100% を明示指定

4. トラフィックが 100% 振れなければ workflow を強制 fail ⚡️
# GitHub Actions での検証ステップのイメージ
- name: Deploy to Cloud Run
  id: deploy
  run: |
    # コミットハッシュ等で revision suffix を一意に固定
    REV_SUFFIX="${{ github.sha }}"
    gcloud run deploy backend-service \
      --image="${{ env.IMAGE_URI }}" \
      --revision-suffix="${REV_SUFFIX}" \
      --region=asia-northeast1

- name: Verify and force traffic migration (100%)
  run: |
    SERVICE_NAME="backend-service"
    REGION="asia-northeast1"
    EXPECTED_REV="${SERVICE_NAME}-${{ github.sha }}"

    # 1. 作成されたリビジョンの存在確認
    gcloud run revisions describe "${EXPECTED_REV}" \
      --region="${REGION}" > /dev/null

    # 2. 明示的に新リビジョンへ 100% トラフィックを割り当て(Pin 解除と強制移行)
    gcloud run services update-traffic "${SERVICE_NAME}" \
      --to-revisions="${EXPECTED_REV}=100" \
      --region="${REGION}"

    # 3. 実際のトラフィックルーティングが期待した新リビジョン 100% になったかを直接照合
    CURRENT_TRAFFIC_REV=$(gcloud run services describe "${SERVICE_NAME}" \
      --region="${REGION}" \
      --format="value(status.traffic[0].revisionName)")

    CURRENT_PERCENT=$(gcloud run services describe "${SERVICE_NAME}" \
      --region="${REGION}" \
      --format="value(status.traffic[0].percent)")

    if [ "${CURRENT_TRAFFIC_REV}" != "${EXPECTED_REV}" ] || [ "${CURRENT_PERCENT}" != "100" ]; then
      echo "❌ エラー: 新リビジョンへのトラフィック切り替えに失敗しました!"
      echo "期待: ${EXPECTED_REV} (100%), 実際: ${CURRENT_TRAFFIC_REV} (${CURRENT_PERCENT}%)"
      exit 1
    fi
    echo "✅ 新リビジョン (${EXPECTED_REV}) に 100% トラフィックが正常に切り替わりました。"

ガードレールの効果

  • リビジョン作成とトラフィック割り当ての分離・強制: update-traffic --to-revisions=...=100 を明示的に呼ぶことで、過去のトラフィック固定状態を上書きし、新リビジョンへ確実に 100% を向けます。
  • 実状態(State)の照合によるフェイルセーフ: 最終的に status.traffic[0] を直接クエリして照合するため、何らかの理由でトラフィックが切り替わらなかった場合は即座に GitHub Actions を Red で落とせます。

終了コードと実際の状態(State)を混同しない

このトラブルから得られた最大の教訓は、「コマンドの終了コード(exit code)と、インフラの実際の状態(State)を混同してはならない」 ということです。

観点コマンドの終了コードインフラの実際の状態(State)
意味CLI ツールがクラッシュせずに処理を終えたか意図したリビジョンが 100% トラフィックを受けているか
信頼性ツール側の仕様(トラフィック固定等)によって見かけ上成功しやすい実環境の真実(Single Source of Truth)
自動化での扱い単なるプロセスの完了通知にすぎないCD 成功の絶対条件としてアサートすべき対象

この「コマンドは通ったのに、実態が追従していない」という現象は、Cloud Run だけでなく様々な場所で発生します。

  • DB マイグレーションの CLI を実行したが、ロック競合でスキップされていた
  • Terraform の apply は通ったが、クラウド側の非同期プロビジョニングが裏でコケていた

「デプロイコマンドが 0 で終わったから成功」と盲信するのではなく、「期待した状態に変化したこと(State)を外側から直接クエリして確かめる」

この一手間を CD パイプラインに挟むだけで、デプロイの信頼性は劇的に向上します。

参考リンク