ブルーグリーンデプロイ
Blue-Green Deployment ・ ぶるーぐりーんでぷろい
本番環境を2系統用意し、向き先の切り替えで新版を公開するデプロイ手法。切り戻しが一瞬で済む。
✦ 30秒で試す — 航海サーバーを丸ごと2倍にするのは、戻る道を残すため新版に不具合が出た。向き先をブルーの旧版へ戻すと?予想してみる概要
ブルーグリーンデプロイは、本番環境を「ブルー」と「グリーン」の2系統まるごと用意しておき、片方(たとえばブルー)が本番のトラフィックを受けている間に、もう片方(グリーン)へ新しい版を配置して確かめ、準備ができたらトラフィックの向き先を一斉に切り替える手法です。利用者から見ると、ある瞬間に旧版から新版へ移るだけで、サービスは一度も止まりません。
この手法のいちばんの価値は、切り戻し(ロールバック)の速さにあります。切り替えた後も旧版の環境は起動したまま残っているので、問題が見つかったら向き先を元へ戻すだけで、数秒で旧版に復帰できます。「もう一度デプロイし直す」のではなく「スイッチを戻す」だけ。この即時性が、CI/CD でリリースの回数を増やしたいチームにとっての安全網になります。
ただし、この「戻すだけ」が成り立つには条件があります。2系統に分けられるのはアプリケーションの層だけで、データベースはふつう両系統で1つを共有しているからです。新しい版がデータベースの形を変えてしまうと、戻した先の旧版がそれを読めずに壊れます。ブルーグリーンを使いこなす鍵は、切り替えの仕組みそのものより、この共有された部分の扱いにあります。
なぜ生まれたか
伝統的なデプロイは、稼働中のサーバの中身を上書きする方式でした。サービスを止めてファイルを入れ替え、プロセスを再起動する。このやり方には根深い問題が2つありました。1つ目は、入れ替えの間サービスが止まる(あるいは新旧が混ざった不安定な状態になる)ため、深夜のメンテナンス時間を設けるのが当たり前だったことです。2つ目は、失敗したときの復旧が遅いことです。旧版はすでに上書きされて消えているので、ロールバックとは「旧版をもう一度デプロイする」作業になり、その間ずっと障害が続きます。本番のサーバの上で入れ替えるので、途中で失敗すると中途半端に壊れた状態が残ることさえありました。
ブルーグリーンデプロイは、「稼働中の環境には手を触れない」という発想の逆転でこれを解決しました。新しい版は本番のトラフィックが当たっていない別の系統に置き、起動と疎通の確認まで済ませてから、トラフィックの向き先だけを変えます。危険な作業はすべて利用者の見ていない場所で終わり、公開の瞬間は「向き先の変更」という小さな操作だけになりました。この手法は2000年代半ばに ThoughtWorks の Dan North と Jez Humble が使い始め、Martin Fowler が2010年に「BlueGreenDeployment」として紹介したことで名前が広まりました。同じ年に出た Humble と Farley の『継続的デリバリー』でも、デプロイメントパイプラインの最後を安全にする方法として扱われています。
とはいえ、本番をもう1系統まるごと持つのは、物理サーバの時代には高くつく贅沢でした。クラウドコンピューティングで環境を数分で借りて返せるようになり、IaC で同じ構成をコードから何度でも作れるようになったことで、「本番の複製」は日常的に使える道具になりました。
詳細
仕組み — 2つの環境とルータ
登場するのは3つです。いま本番のトラフィックを受けている環境(アクティブ。慣例的にブルー)、待機している環境(アイドル。グリーン)、そして両者の手前でトラフィックの向き先を決めるルータです。ルータの実体はロードバランサやリバースプロキシの転送先の設定、あるいは DNS のレコードです。
デプロイは次の順に進みます。(1)待機中のグリーンに新しい版を配置して起動する。(2)本番のトラフィックが来ない状態のまま、ヘルスチェックと、主要な機能がひととおり動くかを確かめるスモークテスト(テストの一種)を流す。(3)問題がなければルータの向き先をブルーからグリーンへ切り替える。(4)しばらく監視して安定を確かめたら、ブルーは次のデプロイの待機系に回る。次のリリースでは役割が入れ替わり、今度はブルーに新しい版を置いてグリーンから切り替えます。2つの系統が交互にアクティブになるのが、この手法のリズムです。
切り替えの手段は環境によって変わります。DNS のレコードを書き換える方法はいちばん単純ですが、各地のリゾルバやクライアントがキャッシュを TTL(有効期限)の間持ち続けるので、切り替えが一斉には効きません。古い向き先に来続ける人が残るため、確実さが要るならロードバランサの転送先を切り替えるのが定石です。Kubernetes では、Service のラベルセレクタを新しい版の Pod 群へ書き換える方法や、Argo Rollouts の BlueGreen 戦略がこの手順を自動化します。AWS なら CodeDeploy の blue/green デプロイや、ロードバランサの重み付きターゲットグループが同じ役割を担います。
ローリングアップデートとの違い
無停止でデプロイする方法はもう1つあります。ロードバランサの後ろに並んだサーバを1台ずつ(あるいは数台ずつ)新しい版に入れ替えていく、ローリングアップデートです。Kubernetes の Deployment の既定の動きもこれです。追加で必要なのは入れ替え中の1台分程度で、2系統を持つより安く済みます。
違いは「新旧が混ざる時間」と「戻す速さ」です。ローリングアップデートでは入れ替えの途中、同じ利用者のリクエストが v1 に行ったり v2 に行ったりします。画面は v2 なのに API は v1、ということも起こり得るので、新旧の版がお互いに通信できる互換性が要ります。戻すときも1台ずつの入れ替えをやり直すので、数分から数十分かかります。ブルーグリーンは台数を倍にする代わりに、切り替えも切り戻しも一瞬で、利用者が新旧の混ざった状態を見る時間がほとんどありません。
最大の落とし穴 — 1つしかないデータベース
2系統にできるのは状態を持たないアプリケーションの層だけで、データベースはふつう両系統で共有します。ここがこの手法の最大の難所です。新しい版がカラムの改名や削除のような破壊的なスキーマ変更を含んでいると、切り替えた後でブルーへ戻した瞬間、旧版のコードが消えたカラムを読みに行って壊れます。環境は戻せても、データベースは戻りません。「切り替えは一瞬、切り戻しも一瞬」という利点は、データベースが新旧どちらの版とも互換であるときにしか成り立たないのです。
定石は、破壊的な変更を1回のリリースに詰め込まず、何回かに分けることです。(1)新しいカラムを足し、新旧どちらのコードも動ける状態を作る(expand)、(2)既存のデータを新しいカラムへ移し、新しい版は両方に書く、(3)旧カラムを読む版がどこにも残っていないことを確かめてから、後のリリースで旧カラムを消す(contract)。こうすると、どの時点で切り戻しても、戻った先の版が使うカラムはデータベースに残っています。
下の部品で、レバーを倒して切り替え、そして戻してみてください。「一度に変える」計画では、戻した先のブルーが朱く壊れます。「3回に分ける」計画に替えると、何度往復しても壊れません。
本番はいまブルーの v1 です。まず待機系のグリーン(空き)を押して、新しい版 v2 を置いてください。
データベースのほかにも、切り替えの瞬間に引き継ぐものがあります。処理中のリクエストは、ロードバランサのコネクションドレイニング(新しい接続だけを止め、処理中のものは終わるまで待つ)で取りこぼさずに済みます。利用者のセッションをサーバのメモリに持っていると、切り替えた瞬間に全員がログアウトされるので、外部のストアに出しておきます。キューのコンシューマやバッチのような「待機系でも動いてしまう」処理は、アクティブな側だけで動くように止めておかないと、2系統が同じ仕事を二重に実行します。
コストと、カナリアリリースとの使い分け
本番を2系統持つので、素朴にやるとインフラの費用は2倍になります。クラウドなら「デプロイの間だけグリーンを起動し、安定したらブルーを縮める」運用で費用を抑えられますが、そのぶん即時ロールバックが使える時間は短くなります。また、切り替えは全員一斉なので、新しい版にバグがあれば、短い時間とはいえ全員がそれを踏みます。
一部の利用者にだけ新しい版を当て、様子を見ながら広げたいならカナリアリリースが向いています。「戻す速さのブルーグリーン、確かめる丁寧さのカナリア」が基本の使い分けで、グリーンへの切り替えを一斉ではなく数段階の重みで行う折衷もよく使われます。切り替え前のグリーンに本番と同じリクエストを複製して流すシャドーイング(応答は捨てる)や、社内の利用者だけ先にグリーンへ向ける運用を組み合わせると、切り替え前の確認をさらに本番に近づけられます。
試問
利用者のリクエストはルータを通って本番の環境(ブルーかグリーン)へ流れ、どちらの環境も1つの共有 DB を読みます。待機側の箱を押すと次の版が置かれ、ルータのレバーを倒すと本番が入れ替わります。新しい版は、DB の name 列を full_name に改名します。
本番はいまブルーの v1 です。まず待機系のグリーン(空き)を押して、新しい版 v2 を置いてください。
