障害の多くはデプロイから始まります。コードはレビューもテストも通ったのに、本物のトラフィック、本物のデータ、本物の依存先に出会った途端に問題が起きます。デプロイ戦略は、トラフィックのどれだけが一度に新しいバージョンに触れるか、問題にどれだけ早く気づけるか、どれだけ早く元に戻せるかを決めます。
この記事では、多くのチームが選択肢にする 3 つの戦略、ローリングアップデート、ブルーグリーン、カナリアリリースを比較します。それぞれでトラフィックがどう移り、ロールバックがどうなるかを示し、最後に、続行するかロールバックするかの判断を自動化したカナリアリリースの図を紹介します。
ローリングアップデート
旧バージョンのインスタンスを、少しずつ新バージョンに置き換えます。Kubernetes の Deployment はデフォルトでこれを行います。RollingUpdate 戦略 では、maxSurge と maxUnavailable がロールアウト中に許される追加の Pod 数と不足する Pod 数を決め、どちらもデフォルトは 25% です。
- トラフィック: ロールアウト中は、両方のバージョンがそれぞれの Pod 数に応じた割合で同時にリクエストを処理します。
- ロールバック: 旧バージョンへのもう一度のローリングアップデートです。秒単位ではなく分単位です。
- コスト: ほとんど追加なし。多くてもサージ分の容量です。
- リスク: どのユーザーも早い段階で新バージョンに当たる可能性があり、自動のゲートは readiness probe だけです。起動は問題なくても負荷がかかると失敗するバージョンは、最後まで展開されてしまいます。
ローリングアップデートは、リスクの低い変更や、テストとプローブが整った社内サービスにとって妥当なデフォルトです。readiness probe には、プロセスが起動しているかだけでなく、サービスが動くのに必要なものを確認させましょう。そうしないと、ロールアウトは健全な Pod を壊れた Pod に平然と置き換えていきます。
ブルーグリーン
完全な環境を 2 つ動かします。ブルーが本番を処理し、グリーンには新しいバージョンを入れてその場でテストします。その後、ロードバランサーか DNS がすべてのトラフィックを一度にブルーからグリーンへ切り替えます。Martin Fowler による ブルーグリーンデプロイの説明 が古典的な参考資料です。
- トラフィック: すべてブルー、その後すべてグリーン。両方に流れることはなく、あっても短時間です。
- ロールバック: まだ動いているブルーに戻すだけです。秒単位です。
- コスト: 両方が動いている間は 2 倍の容量が必要です。
- リスク: 切り替えでユーザーの 100% が一度に移るので、本物のトラフィックでしか現れない問題は全員に影響します。データベースは共有なので、スキーマの変更は両方のバージョンで動く必要があります。
カナリア
本物のトラフィックのごく一部、たとえば 5% を新バージョンに送り、安定版と比較し、問題なければ段階的に割合を広げます。名前は炭鉱のカナリアに由来します。Fowler のサイトの カナリアリリースの記事 を参照してください。
- トラフィック: 重み、ヘッダー、またはユーザーセグメントで分割します。
- ロールバック: カナリアの重みをゼロにします。秒単位で済み、影響を受けたユーザーも数パーセントだけです。
- コスト: 追加の Pod が少し。加えて、トラフィック分割と分析のためのツールが必要です。
- リスク: 正しいメトリクスを見ていれば、3 つの中で最も低くなります。誰も見ていないカナリアは、遅いローリングアップデートにすぎません。
並べて比較
| ローリング | ブルーグリーン | カナリア | |
|---|---|---|---|
| 不具合のあるバージョンに触れるユーザー | 徐々に、最大で全員 | 全員、一度に | ごく一部 |
| ロールバック時間 | 数分 | 数秒 | 数秒 |
| 追加の容量 | サージ分のみ | 切り替え中は 100% | Pod 数個 |
| トラフィック分割の必要性 | なし | 切り替えスイッチ | 重み付きルーティング |
| データベースの変更 | 後方互換 | 後方互換 | 後方互換 |
最後の行がどれも同じなのには理由があります。どの戦略でも、ある時点で 2 つのバージョンのコードが同じデータベースに出会うからです。expand-and-contract 方式のマイグレーションを使いましょう。まず新しい列やテーブルを追加し、両方の形で動くコードをデプロイし、古い形は後のリリースで削除します。
カナリアの前のシャドートラフィック
カナリアの前に 1 段階加えるチームもあります。本番リクエストの一部を新バージョンにコピーし、そのレスポンスは捨てるのです。Envoy や Istio はこの方法でトラフィックをミラーリングできます。ユーザーにまったく影響を与えずにクラッシュやレイテンシーの悪化を見つけられますが、使えるのは読み取りと冪等なリクエストだけです。ミラーリングされた書き込みは、メール、決済、在庫の変更といった副作用を繰り返します。書き込みはミラーから外すか、シャドーをサンドボックス化した依存先に向けましょう。
カナリアの判断を自動化する
カナリアの価値は比較にあり、人間は 1 時間もダッシュボードを見続けるのが苦手です。Argo Rollouts や Flagger などのツールは、Kubernetes 上でこれを自動化します。トラフィックを段階的に移し、各段階の間にメトリクスプロバイダーに問い合わせ、あなたが定義したしきい値に基づいて昇格または中止を決めます。
良い分析メトリクスは少数で、ユーザーに近いものです。
- カナリアのエラー率(5xx レスポンス)を安定版と比較したもの。
- p99 のような高いパーセンタイルでのレイテンシー。
- 可能なら業務上のシグナルを 1 つ。たとえば 1 分あたりの決済完了数です。
カナリアは固定の数値ではなく安定版と比較します。そうすれば、依存先の調子が悪い夜でも、すべてのリリースが失敗することはありません。
アーキテクチャ図
ダイアグラムの内容
- CI pipeline
- Rollout controller
- Users
- Ingress
- Stable v1
- Canary v2
- Metrics
- Canary within thresholds?
- Next step: 25, 50, 100 percent
- Abort: all traffic to v1
- image v2
- 95%
- 5%
- error rate, p99 latency
- yes
- no
- set weights
図の読み方は次のとおりです。
- 上、トラフィックの経路: ユーザーは Ingress に到達し、Ingress はリクエストの 95% を安定版の ReplicaSet に、5% をカナリアに送ります。両方がメトリクスに報告します。
- 下、制御ループ: CI が新しいイメージをロールアウトコントローラーに渡します。コントローラーは Ingress の重みを設定し(破線のエッジ)、両バージョンのエラー率と p99 レイテンシーを読み取って判断します。
- 判断: しきい値の範囲内なら次の段階へ進み、範囲外なら中止してすべてのトラフィックを v1 に戻します。
- Markdown ブロック には段階と中止のルールを並べています。これは各サービスのロールアウト設定のレビューに含めるべき内容です。
フィーチャーフラグは 4 つ目の戦略
デプロイ戦略は、どのサーバーが新しいコードを実行するかを制御します。フィーチャーフラグは、デプロイとは独立に、どのユーザーが新しい振る舞いを目にするかを制御します。頻繁にリリースするチームは両方を組み合わせます。ローリングアップデートかカナリアでデプロイし、リスクのある機能はフラグの裏に置いて、フラグを段階的にオンにします。何も再デプロイしなくて済むので、フラグのほうが速いロールバック手段です。
デプロイの図に描くべきこと
- エッジ上のトラフィックの割合。 ロールアウト中に変わるので、最初の段階を示します。
- 誰が重みを変えるか。 コントローラー、パイプライン、または人です。
- 中止の基準。 「調子が悪そうなら」ではなく数値で書きます。
- データベース: スキーマ変更は expand-and-contract で行うことを書き添えます。
これより前のビルドとテストの段階は CI/CD パイプラインの図 で、クラスターについては Kubernetes のアーキテクチャ図 で、メトリクスの出どころは オブザーバビリティのアーキテクチャ図 で扱っています。
Archboard で作る
- Google または GitHub でサインインし、図を作成します。
- ユーザー用に 人物 を追加し、Kubernetes パックから Ingress を 1 つと、安定版とカナリア用に ReplicaSet ノードを 2 つ追加します。
- DevOps パックから メトリクス と パイプライン を、ロールアウトコントローラー用に C4 モデルパックから コンテナ を追加します。
- 昇格と中止のために、フローチャートパックから 判断、処理、開始 / 終了 を追加します。
- Ingress のエッジにトラフィックの重みをラベルとして付け、重みの変更を破線のエッジで描き、段階を書いた基本パックの Markdown ブロックを追加します。
この例で使うダイアグラムノードは 11 個で、Free プランの範囲に収まります。
よくある質問
最適なデプロイ戦略はどれですか? リスクとツール次第です。ローリングアップデートはリスクの低い変更の大半に、カナリアは不具合のあるリリースのコストが高いユーザー向けサービスに、ブルーグリーンは瞬時に全体を切り替える必要があり、2 倍の容量をまかなえるシステムに向いています。
ブルーグリーンとカナリアの違いは何ですか? ブルーグリーンはすべてのトラフィックを一度に切り替え、即座にロールバックできるよう古い環境を残しておきます。カナリアはまずトラフィックのごく一部を移し、新バージョンが問題ないと確認できてから割合を広げます。
デプロイ中のデータベースマイグレーションはどう扱えばいいですか? すべてのスキーマ変更を後方互換にします。まず拡張し、両方の形で動くコードをデプロイし、後のリリースで縮小します。
カナリア分析ではどのメトリクスを使うべきですか? 安定版と比較したエラー率と高パーセンタイルのレイテンシー、それに素早く測れるなら業務メトリクスを 1 つ加えます。
フィーチャーフラグはデプロイ戦略ですか? リリース戦略です。機能をオンにすることをコードのデプロイから切り離すもので、3 つのデプロイ戦略のどれとも組み合わせやすいものです。