デプロイが自ら判断する時代:ロールバックとプロモーションの自動化
APIの新バージョンをデプロイした直後、エラーレートが0.1%から4%に跳ね上がった。あなたは会議中だ。ダッシュボードを確認したときには15分が経過し、ユーザーからはすでに苦情が届いている。
これが週に3回も発生したらどうだろう。毎回、誰かが異常に気づき、監視ツールを開き、数値を解釈し、判断を下し、手動でロールバックまたは停止を実行しなければならない。1日に複数回デプロイするチームにとって、この手動判断ループは疲弊の元だ。さらに悪いことに、判断に一貫性がない。あるエンジニアはエラーレート2%でロールバックするが、別のエンジニアは5%まで待つ。さらに別のエンジニアは誰かにチャットで通知されるまで気づかないかもしれない。
この問題を解決するのがデプロイメントゲーティング(deployment gating)だ。デプロイメントゲートとは、新しいバージョンを次のステージに進めるか停止するかを自動的に判断するチェックポイントである。ゲートは推測で動かない。ポリシーに従う。ポリシーとは、「このシグナルが閾値を超えたら、このアクションを実行する」というルールセットだ。
デプロイメントゲートの仕組み
ゲートはクラブの入り口にいる用心棒のようなものだ。用心棒はあなたのことを知らない。ただチェックするだけだ:リストに載っているか?IDは有効か?イエスなら入場、ノーなら待機か退場だ。
デプロイメントゲートも同じだ。新しいバージョンを一部のユーザーまたはステージング環境にデプロイした後、ゲートはオブザーバビリティシグナルをチェックする。シグナルが健全なら、ゲートはバージョンをより多くのユーザーにプロモートする。シグナルが悪ければ、ゲートはロールバック、ホールド、またはポーズをトリガーする。
以下の図は、ゲートがオブザーバビリティシグナルをチェックした後の3つの可能な結果を示している。
重要なのは、判断がチームが事前に合意したルールに基づいて自動的に行われることだ。誰も午前2時にダッシュボードを見張る必要はない。誰もプレッシャーの中で判断を下す必要はない。システムがポリシーに従う。
ポリシーに含めるべきもの
ポリシーは単一のルールではない。デプロイする対象の種類に応じた条件のセットだ。デプロイ対象によって障害パターンが異なるため、それぞれに異なるポリシーが必要になる。
アプリケーションの場合、ポリシーは以下をチェックする:
- SLOベースラインと比較したエラーレート
- p95またはp99パーセンタイルのレイテンシ
- サービスがリクエストを拒否していることを示すスループットの低下
データベースマイグレーションの場合、ポリシーは以下をチェックする:
- プライマリとレプリカ間のレプリケーションラグ
- マイグレーション後のスロークエリ数
- コネクションプールの枯渇
インフラストラクチャ変更の場合、ポリシーは以下をチェックする:
- クラスタ内のノードヘルス
- CPUおよびメモリ使用パターン
- Pod再起動回数
各対象は独自のポリシーを持つ。それぞれの壊れ方が異なるからだ。APIのレイテンシスパイクとデータベースのレプリケーションラグは同じではない。ポリシーは障害モードに適合する必要がある。
以下は、上記のロジックを実装した最小限のポリシー・アズ・コードの例である:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: api-deployment
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api
service:
port: 8080
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: error-rate
thresholdRange:
max: 0.05
interval: 2m
- name: request-duration
thresholdRange:
max: 0.5
interval: 2m
webhooks:
- name: rollback-on-failure
timeout: 30s
metadata:
action: rollback
このポリシーは2分ごとにエラーレートとレイテンシをチェックする。どちらかが閾値を超えた場合、デプロイは自動的にロールバックされる。すべてのチェックが5分間成功した場合、新しいバージョンは次のウェイトステップにプロモートされる。
エラーバジェットをポリシーのアンカーとして使う
エラーバジェットは、ポリシーに組み込む実用的な数値を提供する。チームが99.9%の可用性SLOを設定した場合、月間の許容ダウンタイムは約43分になる。それがエラーバジェットだ。
ここで、新しいデプロイが最初の1時間でそのバジェットの10分を消費したとする。それは何かがおかしいという強いシグナルだ。ポリシーはこう言える:「新しいバージョンが最初の30分で月間エラーバジェットの5%以上を消費した場合、自動的にロールバックする」。
このアプローチは推測を排除する。チームはSLOに合意している。ポリシーがそれを強制する。10分が多すぎるかどうかを議論する必要はない。数値はすでに決まっている。
すべての判断がロールバックである必要はない
よくある間違いは、すべてのポリシーをロールバックで終わらせることだ。多くの状況ではそれは過剰すぎる。より良いアプローチは階層化ポリシーである。
例:
- エラーレートが0.5%上昇したがSLO閾値未満の場合、ホールドをトリガーする。新しいバージョンは稼働を続けるが、より多くのユーザーにはプロモートされない。チームはプレッシャーなく調査できる。
- エラーレートがSLO閾値を超えた場合、ロールバックをトリガーする。システムは即座に以前のバージョンに戻る。
- レイテンシは増加したがエラーレートは安定している場合、ポーズをトリガーする。これ以上のプロモーションは行われないが、現在のバージョンは稼働を続ける。チームが手動で続行かロールバックかを判断する。
この階層化アプローチにより、チームは過剰反応や反応不足に陥ることなく、さまざまな深刻度に対処できる。
これを機能させるために必要なもの
デプロイメントゲーティングには、オブザーバビリティシステムとデプロイメントプラットフォームの統合が必要だ。監視からのシグナルは、デプロイメントを管理するパイプラインまたはプラットフォームから読み取り可能でなければならない。
Argo Rollouts、Flagger、Spinnakerなどのツールはすでにこのパターンをサポートしている。Prometheus、Datadog、New Relicなどからメトリクスを取得できる。ポリシーを設定すれば、ツールが判断を実行する。
しかし、難しいのはツールではない。難しいのはポリシーの定義だ。以下を知る必要がある:
- 各デプロイタイプにとって重要なシグナルはどれか
- どの閾値が実際の問題を示し、どの閾値がノイズかを判断する基準
- 障害の深刻度ごとにどれだけ迅速に反応する必要があるか
シンプルに始めよう。1つのシグナル、1つの閾値、1つのアクションを選ぶ。1週間実行し、誤検出がどれだけあるか確認する。調整する。徐々にシグナルを追加する。
システムに判断を任せても安全か?
この質問は毎回出る。答えはこうだ:ポリシーをどれだけうまく定義するかに依存する。
良いポリシーは人間の判断を完全に置き換えるものではない。すでに予測可能な判断を引き継ぐのだ。チームが「エラーレートが2%を5分間超えたら必ずロールバックする」とわかっているなら、なぜ人間がそれを実行するのを待つのか?その判断を自動化しよう。
エッジケースはどうするのか?チームは常にオーバーライドメカニズムを持つべきだ。ポリシーが誤って発動した場合、誰かがロールバックを停止したり手動でプロモートしたりできるようにする。自動化はルーチンケースを処理する。人間は例外を処理する。
目標は人間をループから排除することではない。目標は、人間を退屈で反復的で予測可能な判断から解放し、本当にコンテキストと判断力を必要とする作業に集中させることだ。
始めるためのクイックチェックリスト
最初のデプロイメントゲートを構築する前に、以下が整っていることを確認しよう:
- 信頼できる1つのオブザーバビリティシグナル(エラーレートまたはレイテンシから始める)
- SLOまたはエラーバジェットに基づく明確な閾値
- ゲーティングをサポートするデプロイメントプラットフォーム(Argo Rollouts、Flagger、Spinnakerなど)
- 手動介入のためのオーバーライドメカニズム
- 2週間ごとにポリシーの有効性を確認するレビューサイクル
初日から完璧なポリシーを構築しようとしないでほしい。1つのゲート、1つのシグナル、1つのアクションから始めよう。結果から学び、そこから拡張する。
真の価値は一貫性にある
自動化されたデプロイ判断の最大のメリットはスピードではない。確かにスピードも役立つが、真の価値は一貫性だ。すべてのデプロイが同じゲートを通り、同じ基準で判断され、同じ判断ロジックが適用される。オンコールエンジニアと仲が良いからといってパスをもらえることはない。レビュアーの機嫌が悪いからといって不当にロールバックされることもない。
デプロイ判断がポリシーによって自動化されると、チームは burnout することなくより頻繁にデプロイできるようになる。システムがルーチンの判断を処理し、チームは例外と改善に集中する。これこそが、頻繁にデプロイするチームと持続可能にデプロイするチームの違いだ。