ゼロダウンタイムデプロイの手法|ブルーグリーン・カナリア・ローリングの違いと実装

kento_morota 11分で読めます
社内のAI・IT活用、技術がわかる相手に無料相談。 相談 →
目次

「リリースは深夜にメンテナンス画面を出して行う」という運用を続けていると、リリース頻度は下がり、担当者の負担は増え、深夜作業ゆえのミスも起きやすくなります。一方で「ダウンタイムなしでデプロイしたい」と調べると、ブルーグリーン・カナリア・ローリングと手法が並び、どれを選べばよいのか、自分たちの規模で現実的なのかがわかりにくいのも事実です。

この記事では、3つの手法をそれぞれ「何が起きているか」「コストはどれだけ増えるか」「失敗したときにどう戻すか」の観点で比較し、多くのチームがつまずくDBマイグレーションとの両立、ロードバランサー切替とヘルスチェックの関係、そして少人数チームでの現実的な選択を解説します。CI/CDパイプライン自体の作り方はCI/CDパイプラインの基礎で扱っていますので、本記事は「パイプラインの最後のステップである本番への反映」をどう設計するかに絞ります。

ゼロダウンタイムデプロイの3つの手法

なぜ単純な「止めて入れ替える」でダウンタイムが出るのか

1台のサーバーで「旧プロセスを止める→新コードを配置→新プロセスを起動」とすると、停止から起動完了までの数秒〜数十秒はリクエストを受けられません。起動に時間のかかるJavaやRailsのアプリではさらに長くなります。ゼロダウンタイムの手法はすべて、「旧と新を同時に存在させ、トラフィックの向き先を切り替える」ことでこの空白をなくします。つまり前提として、ロードバランサーやリバースプロキシのように向き先を切り替えられる層が必要です。

ブルーグリーン・カナリア・ローリングの比較

ブルーグリーンカナリアローリング
仕組み本番と同じ環境をもう1組(グリーン)用意し、検証後にLBの向き先を一括で切り替える新バージョンを一部(1〜10%程度)のトラフィックにだけ流し、指標を見ながら比率を上げる複数台のうち1台ずつ(または一定割合ずつ)新バージョンに入れ替える
追加コスト切替前後の一時期、2倍のリソース少量(新バージョン用の数台)ほぼなし(余剰1台分程度)
ロールバック向き先を戻すだけ。数秒で完了比率を0%に戻す。影響範囲が最小再度ローリングで戻す。時間がかかる
新旧混在期間なし(切替は一瞬)あり(意図的に長く)あり(入れ替え中ずっと)
必要なもの環境を丸ごと2組作れる自動化重み付きルーティングと監視指標ヘルスチェックと複数台構成
向いている場面「戻せること」を最優先したいユーザー影響を最小にしながら本番で検証したいコストを増やさず標準的に運用したい

ローリングはKubernetesのDeploymentやAWS ECS、Cloud Runなど多くのプラットフォームのデフォルトで、何も考えなくてもそれなりに動きます。ブルーグリーンとカナリアは「戻しやすさ」と「安全な検証」という明確なメリットがある代わりに、前者はコスト、後者は監視指標と重み付きルーティングの仕組みを要求します。

DBマイグレーションとの両立|後方互換が全ての前提

「新旧が同時に動く瞬間」にスキーマは1つしかない

3手法のどれを選んでも、旧バージョンと新バージョンのコードが同じデータベースを同時に使う時間帯が必ず存在します(ブルーグリーンでも、ロールバックに備える限りDBは共有です)。したがって、スキーマ変更は新旧どちらのコードでも動く形でなければなりません。「カラム名を変更」「NOT NULLカラムをデフォルトなしで追加」「カラムを削除」といった変更を1回のリリースで行うと、旧バージョンのSQLがエラーになります。

Expand / Contractパターン

互換性を保つには、変更を「広げる(Expand)」段階と「縮める(Contract)」段階に分け、複数回のリリースに分割します。たとえば users.namefull_name に改名したい場合は次のようになります。

  1. リリース1(Expand): full_name カラムをNULL許容で追加。コードは namefull_name の両方に書き込み、読み取りは name のまま。
  2. バックフィル: 既存行の namefull_name にコピーするバッチを、負荷を見ながら実行。
  3. リリース2: 読み取りを full_name に切り替え。書き込みは引き続き両方。
  4. リリース3(Contract): name への書き込みをやめ、十分な期間が過ぎてから name カラムを削除。

手間は増えますが、どの段階でも直前のバージョンにロールバックできます。「マイグレーションを先に流してからコードをデプロイする」のか「後に流す」のかも重要で、Expand系(追加)はコードより先、Contract系(削除)はコードより後、と覚えておくと判断を誤りません。マイグレーションの運用全般はデータベースマイグレーション戦略で詳しく解説しています。

ロードバランサー切替とヘルスチェック

切替は「新が健全になってから、旧を静かに外す」

どの手法でも、向き先の切替は次の順序で行われる必要があります。

  1. 新バージョンを起動し、ロードバランサーのターゲットに登録する。
  2. ヘルスチェックが連続して成功するまで待つ(起動直後は失敗するので、成功回数のしきい値が意味を持つ)。
  3. 旧バージョンをターゲットから登録解除する。このとき既存の接続は完了まで待つ(AWS ALBのDeregistration delay、Nginxの down 指定など)。
  4. 旧バージョンのプロセスにSIGTERMを送り、処理中リクエストを終えてから停止させる。

4はアプリ側の実装が必要で、グレースフルシャットダウンの記事で詳しく説明しています。ヘルスチェックのエンドポイントが「プロセスが生きている」だけを返していると、DB接続が確立する前に振り分けが始まりエラーになるため、依存先への接続完了を含めたReadiness判定を返すようにしてください。

Nginx 1台でできる最小ブルーグリーン

サーバー1台でも、アプリを2つのポートで起動し、Nginxのupstreamを書き換えて reload すればブルーグリーンが成立します。Nginxの reload は既存接続を切らずに新設定のワーカーへ移行するため、この切替自体がゼロダウンタイムです。

# /etc/nginx/conf.d/upstream.conf(デプロイスクリプトが書き換える)
upstream app {
    server 127.0.0.1:3001;          # blue(現行)
    # server 127.0.0.1:3002;        # green(次期)
}

# deploy.sh の切替部分
NEW_PORT=3002
# 1. 新バージョンを別ポートで起動し、ヘルスチェックが通るまで待つ
for i in $(seq 1 30); do
  curl -fs "http://127.0.0.1:${NEW_PORT}/healthz" && break
  sleep 1
done
# 2. upstreamを書き換えて構文チェック後にreload
sed -i "s/127.0.0.1:[0-9]*;/127.0.0.1:${NEW_PORT};/" /etc/nginx/conf.d/upstream.conf
nginx -t && nginx -s reload
# 3. 旧プロセスにSIGTERMを送り、ドレインを待つ
systemctl stop app@3001

Kubernetesのローリングアップデート設定

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # 一時的に何台増やしてよいか(デフォルト25%)
      maxUnavailable: 0    # 稼働台数を減らさない(デフォルト25%)
  template:
    spec:
      containers:
        - name: app
          readinessProbe:
            httpGet: { path: /healthz, port: 8080 }
            periodSeconds: 5
            successThreshold: 1
            failureThreshold: 3

maxUnavailable: 0 にすると、必ず新Podが健全になってから旧Podを落とすので、台数不足による性能低下を避けられます。ロールバックは kubectl rollout undo deployment/app で前のReplicaSetに戻せます。Kubernetesの基本はKubernetes入門を参照してください。

トラブル事例:ローリング中に半分のリクエストが500になった

症状

3台構成でローリングアップデートを行った直後、ユーザー登録機能で断続的に500エラーが発生しました。全台の入れ替えが終わると収まりましたが、約3分間、リクエストの3分の1〜3分の2が失敗していました。

原因

新バージョンのマイグレーションで users テーブルに NOT NULL かつデフォルト値なしの plan_id カラムを追加していました。マイグレーションはデプロイ開始時に一括で実行されたため、まだ旧バージョンで動いている2台の INSERT INTO users (email, name) が「plan_idにNULLは許可されない」というエラーで失敗したのです。ローリング中に新旧が混在することを想定しないスキーマ変更が原因でした。

対処

  1. 緊急対応として ALTER TABLE users ALTER COLUMN plan_id SET DEFAULT 1 を実行し、旧バージョンのINSERTを通るようにした。
  2. 再発防止として、マイグレーションのレビュー観点に「直前のバージョンのコードでもエラーにならないか」を追加。
  3. NOT NULL追加は「NULL許容で追加→バックフィル→NOT NULL制約を付ける」の3段階に分ける運用に変更。

ローリングやカナリアは「混在期間が長い」からこそ、後方互換の徹底が不可欠です。ブルーグリーンなら混在は一瞬ですが、ロールバックした瞬間に同じ問題が起きるので、結局は同じルールに従うことになります。

小規模チームでの現実的な選択

構成別の推奨

手法の優劣より、「自分たちの構成で追加の仕組みなしにできるもの」から始めるのが現実的です。

  • VPS 1台+Nginx: 上記のポート切替式ブルーグリーン。デプロイスクリプトに30行足すだけで実現でき、ロールバックはポートを戻してreloadするだけです。
  • AWS ECS / Cloud Run / App Service: プラットフォームのローリング更新(ECSは最小健全率、Cloud Runはリビジョンのトラフィック分割)をそのまま使う。Cloud Runはリビジョン単位でトラフィック比率を指定できるので、そのままカナリアにもなります。
  • Kubernetes: Deploymentのローリングを基本にし、カナリアが必要になった段階でArgo RolloutsやIngressの重み付けを検討する。

カナリアは「指標を見て判断する人か自動化」が必要なので、監視ダッシュボードとエラー率アラートが整っていないチームが導入すると、単に「一部ユーザーだけ壊れている時間が長い」状態になります。まずはローリングかブルーグリーンで「戻せる」状態を作り、監視が育ってからカナリアへ進むのが順当です。GitHub Actionsからのデプロイ自動化はGitHub Actions自動デプロイ入門を参照してください。

まとめ

ゼロダウンタイムデプロイは、手法の選択そのものより「新旧が同時に存在する前提で設計されているか」で成否が決まります。

  1. ブルーグリーンは戻しやすさ、カナリアは影響の最小化、ローリングは低コスト。まずは構成上すぐできるものから。
  2. スキーマ変更はExpand/Contractで分割し、直前のバージョンでも動く状態を常に保つ。
  3. 切替は「新が健全→旧を外す→旧をドレイン」の順。ヘルスチェックは依存先の準備完了まで含める。
  4. カナリアは監視が育ってから。

次のリリースで「このマイグレーションは旧コードでも動くか」を1つ確認するだけでも、事故の確率は大きく下がります。デプロイ基盤の整備やリリース運用の改善は、Harmonic Societyのシステム開発・インフラ支援でもお手伝いしています。

#ブルーグリーンデプロイ#カナリアリリース#ローリングアップデート#デプロイ

Harmonic Society

この記事の内容、自社の業務でも活かせそうですか?

ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。

共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

関連記事

Related / 9 articles

  1. プログラミング

    DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用

    DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。

  2. プログラミング

    WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド

    WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。

  3. プログラミング

    セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説

    CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。

  4. プログラミング

    クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計

    クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。

  5. プログラミング

    秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除

    APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。

  6. プログラミング

    開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御

    開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。

  7. プログラミング

    ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方

    NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。

  8. プログラミング

    ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方

    ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。

  9. プログラミング

    localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決

    localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。

Harmonic Society

「読んで終わり」にせず、自社の業務で試してみませんか?

AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。

無料・30分・オンラインOK|1営業日以内に返信します