Blue-Green デプロイは、本番と同等の環境 (blue と green) を 2 つ用意し、一度に一方だけにトラフィックをルーティングします。新バージョンをデプロイするときは、待機側 (アイドル側) の環境に投入し、ロードバランサーを切り替えます。ロールバックは切り替えを戻すだけです。
このモデルはステートレスなアプリケーションサーバーには綺麗に当てはまります。データベースはステートフルです。両環境が同じデータを共有するか、最終的に同じデータに収束する必要があります。green に当てたスキーママイグレーションは、同じテーブルを読む blue 側アプリを壊します。本ガイドでは、データベース Blue-Green デプロイ特有の難しさと、それぞれの扱い方を整理します。
Blue-Green デプロイとは
Blue-Green デプロイは、本番同等の環境を 2 つ維持するリリース戦略です。任意の瞬間に、一方の環境 (たとえば blue) が実トラフィックを処理します。もう一方 (green) はアイドルで、次リリースのステージングに使われます。
Blue-Green デプロイのアーキテクチャ: ロードバランサーがアクティブな blue へトラフィックをルーティング、green は新リリースを受ける
リリースの流れ:
- 新バージョンをアイドル環境 (green) にデプロイ。
- blue が実トラフィックを処理している間、green に対してスモークテストを走らせる。
- ロードバランサーを blue から green に切り替える。
- green を監視する。問題があれば blue に戻す。
- green が安定したら、次回のリリース用に blue が新たなアイドル環境になる。
主な利点は、ほぼ瞬時で低リスクな切り替えです。ロールバック経路 (ロードバランサーを戻すだけ) は昇格と同じ操作なので、悪いデプロイの影響範囲が限定されます。
データベース Blue-Green デプロイ特有の難しさ
アプリケーションサーバーはステートレスです。v1 と v2 を並走させ、どちらかにトラフィックを流し、もう一方を破棄しても、調整は要りません。データベースはそうではありません。
両環境が同じデータを指す。 別々のデータベースを 2 つ用意 (高コスト、同期も複雑) しない限り、blue と green は同じテーブルを読み書きします。orders に NOT NULL カラムを足すスキーマ変更は、その瞬間からそのカラム無しで挿入しようとする blue アプリを壊します。
スキーマ変更は切り替えで戻せない。 green に blue が知らないカラムがあると、blue の書き込みは失敗します。green が使っていたカラムを削除すれば、green が壊れます。ロードバランサーの切り替えは可逆ですが、スキーマ移行はたいてい不可逆です。
両バージョンが同時に動く窓がある。 カットオーバー中、blue の進行中リクエストはまだ処理されています。旧コードと新コードの綺麗な切れ目は保証できません。旧コードと互換でないスキーマ変更は、この窓でエラーを生みます。
カットオーバー後に書かれたデータは戻せない。 green に切り替え、green が新フォーマットでデータを書き、その後 blue に戻すと、blue はそのデータを理解できないかもしれません。データセットが壊れます。
これらはインフラ層では解けません。スキーマ変更に対する規律あるアプローチが必要です。
スキーマを後方互換に保つ: Expand/Contract パターン
Expand/Contract パターン (Parallel Change とも呼ばれる) は、Blue-Green やローリングデプロイで安全にスキーマ移行を行う標準的な方法です。原則は、スキーマ変更を複数フェーズに分け、各フェーズが旧コードと新コードの両方と互換になるようにします。
フェーズ 1: Expand
何も削らずに新しい構造を追加する。データベースは両バージョンのアプリをサポートする状態になります。
例: カラム user_name を username にリネームする
-- フェーズ 1: 新カラムを追加。旧 (user_name) と新 (username) の両方が存在する。
ALTER TABLE users ADD COLUMN username VARCHAR(255);
-- 旧データから新カラムへバックフィル
UPDATE users SET username = user_name WHERE username IS NULL;この時点で: 旧アプリは user_name を読み書きし、新アプリは username を読み書きする。どちらも動く。
フェーズ 2: 新アプリケーションコードをデプロイ
username を使う新バージョンをアイドル環境にデプロイし、トラフィックを切り替えます。Expand フェーズのスキーマと新コードが本番で動いている状態です。
監視する。問題があれば戻す。旧カラムはまだ残っており、旧コードもまだ動く。
フェーズ 3: Contract
新コードが許容できる監視期間 (リスク許容度に応じて数時間〜数日) 安定して動いた後、旧構造を削除します。
-- フェーズ 3: 走っているコードがどれも参照していないので、旧カラムを削除
ALTER TABLE users DROP COLUMN user_name;このクリーンアップはデプロイ済みコードが誰も user_name を参照していないため安全です。
Expand/Contract のルール
このパターンが機能するのは、すべてのスキーマ変更が次のルールに従うときだけです。
| 変更 | 直接デプロイしてよいか? | Expand/Contract が必要か? |
|---|---|---|
| NULL 許容カラムの追加 | Yes | No |
| デフォルト値付き NOT NULL カラムの追加 | Yes (慎重に) | 大きなテーブルでは推奨 |
| カラムリネーム | No | Yes |
| カラム削除 | No | Yes (Contract フェーズのみ) |
| 索引追加 (CONCURRENTLY) | Yes | No |
| カラムの型変更 | No | Yes |
| 外部キー制約の追加 | 要注意 | 旧コードが違反し得るなら Yes |
判定の要点: 現行版と新版のアプリケーションコードが、このスキーマに対して同時に正しく動けるか?
ステップごとの実例: Bytebase を使ったデータベース Blue-Green デプロイ
Bytebase はマルチ環境のデプロイパイプラインを通じて Blue-Green パターンと直接対応します。各環境 (Dev、Staging、Production-Blue、Production-Green) は Bytebase の独立したターゲットで、それぞれの承認・デプロイ規則を持ちます。
環境のセットアップ
Bytebase で順に 4 環境を作ります: Dev、Staging、Production-Blue、Production-Green。各環境はデータベースインスタンスにマップされます。
マイグレーションの提出
開発者は Bytebase 経由でスキーマ変更を提出します。変更はパイプラインを進みます — まず Dev、次に Staging、そして本番環境へ順に。
Bytebase の Issue 詳細: SQL diff、環境ごとのデプロイステージ、DBA 承認制御を含むスキーママイグレーション
どの環境に到達する前にも、Bytebase の SQL レビュールールが自動で走ります。次のような点を確認します。
UPDATE/DELETEのWHERE句欠落- デフォルト無しの
NOT NULLカラム追加 - テーブルをロックする文
- 命名規約違反
違反があれば Issue は解消まで進めません。
アイドル環境へデプロイ
変更が本番ステージに来たら、まずアイドル環境 (たとえば Production-Green) にデプロイします。Production-Blue は引き続き稼働し、トラフィックを受けています。
Bytebase は適用された正確な SQL、タイムスタンプ、承認者を記録します。変更は実トラフィックに触れる前に変更履歴で確認できます。
切り替えと監視
Production-Green にデプロイして検証したらロードバランサーを切り替えます。Production-Green が本番、Production-Blue がアイドルです。
新リリースに問題があれば戻します。スキーマは Expand フェーズなので、旧コードも新コードもまだ動きます。
Contract フェーズを実行
新コードが監視期間中安定して動いたら、Bytebase を通じて Contract マイグレーションを提出します。旧アプリケーションコードが使っていた旧カラムや制約を削除します。同じパイプライン、同じレビュープロセスを通します。
Bytebase のデータベース変更管理ワークフローでは、Contract フェーズを含むすべてのマイグレーションに承認の証跡が残ります。何週間後に問題が起きても、何が、いつ、誰の承認で変わったかを正確に追えます。
Blue-Green vs Canary vs Rolling: スキーマ変更の観点
3 戦略はスキーマ互換性の含意が異なります。
| 戦略 | トラフィックパターン | スキーマ互換性の要件 |
|---|---|---|
| Blue-Green | 一括切り替え | 切り替え時間帯 (数分〜数時間) の間、両バージョンを同時に支える必要 |
| Canary | 少量から段階的に新バージョンへ | カナリア期間全体 (数時間〜数日) の間、両バージョンを支える必要 |
| Rolling | インスタンスを 1 つずつ更新、旧新コードが同居 | ロールアウト期間全体 (インスタンスごとに数分〜数時間) の間、両バージョンを支える必要 |
Blue-Green は重なりの窓が最短で、推論しやすいモデルです。Canary は最長で、旧新コードが数日同居します — Canary でのスキーマ変更は、観測期間全体にわたって後方互換でなければなりません。
3 戦略とも Expand/Contract の恩恵を受けます。Blue-Green は Contract フェーズが最もシンプルです — blue のコードが走っていない明確な時点が存在するからです。
ハマりがちな落とし穴と回避策
Expand フェーズを飛ばしてスキーマ変更を直接デプロイする
最もよくある間違いは、データベース移行をアプリケーションデプロイのように扱うことです: 新スキーマを green に投入してトラフィックを切り替え、終わり。旧コードが必要としているものをスキーマから外していたら、切り替え窓で blue のリクエストが落ちます。必ず先に Expand しましょう。
長時間マイグレーションがロックを保持する
大きなテーブルへの ALTER TABLE は数分にわたって排他ロックを保持し、すべての読み書きをブロックし得ます。PostgreSQL では、ADD COLUMN のデフォルト設定を ALTER インラインではなく分離して指定し、索引は CREATE INDEX CONCURRENTLY で構築しましょう。本番サイズのデータでステージングのマイグレーション所要時間を測ってから昇格します。
Contract フェーズを忘れる
Expand はカラムを足し、旧カラムを残します。Contract フェーズが無いと、データベースは死んだカラム、古いテーブル、孤児になった索引を抱え込みます。Contract マイグレーションは任意のクリーンアップではなく、必須の後続作業として扱いましょう。Bytebase の Issue やチケットシステムに明示的に記録し、こぼれ落ちないようにします。
ロールバックをテストしない
Blue-Green の価値はロールバック経路です。実際に戻したことが無いなら、動くかは分かりません。デプロイのランブックにロールバックテストを含めましょう。green に切り替えてスモークテスト、blue に戻してまたスモークテスト。インシデント前に経路を検証しておきます。
環境間のスキーマドリフト
Dev、Staging、Production-Blue が互いにドリフトしていると、Dev で通ったマイグレーションが本番で失敗します。データベーススキーマドリフトはデプロイパイプラインの静かな殺し屋です。Bytebase のドリフト検知は、各環境を期待スキーマ状態と照合し、マイグレーション実行前に差分をフラグします。
デプロイインフラを過剰設計する
データベース層の Blue-Green に物理データベースクラスタを 2 つ用意する必要はありません。多くのチームはデプロイパイプライン内で 2 つの論理的な環境スロットとして実装し、同じ DB インスタンスへスキーマ変更を慎重に管理しながらルーティングします。物理クラスタの二重化は、環境間でデータの隔離が必要なときだけ — それは稀です。
Blue-Green デプロイは、アプリケーション層のリリースリスクをロードバランサーの切り替えに帰着させます。データベース層はもっと作業が必要です: Expand/Contract のマイグレーション、後方互換なスキーマ変更、規律ある Contract フェーズ。ツール (マイグレーション管理の Bytebase、トラフィックルーティングのロードバランサー設定) は仕組みを担います。規律はスキーマ設計の側にあります。
Bytebase でのマルチ環境デプロイワークフローの詳細はデータベースのマルチ環境デプロイを参照。スキーマ変更をこのパイプラインに流し込む CI/CD 統合はデータベーススキーママイグレーションのための CI/CD パイプライン構築を参照してください。