プロンプトとコンテキスト
あるチームが、トラフィックを徐々に増やす前に、本番リクエストのわずかな割合に新しいバージョンを公開したいと考えています。このサービスは、重み(weight)を設定し、カナリアと安定版(stable)を比較し、シグナルが悪化した際に自動的に停止またはロールバックを行う必要があります。SLO、メトリクスウィンドウ、永続状態、権限、冪等性、およびコントローラー切断時の挙動を含め、コントロールプレーンとデータプレーンを設計してください。
これはシステム設計、プラットフォームエンジニアリング、SREの役割に適したトピックです。コアとなるスキルは、単にKubernetes、サービスメッシュ、監視製品を列挙することではなく、「安全なリリース」を回復可能なステートマシンに落とし込むことです。回答では、ルーティング、分析ジョブ、判定のしきい値、人的介入、および旧バージョンとの後方互換性をカバーする必要があります。
面接官がテストしていること
優れた回答では、ロールアウトコントローラー、ルーター、メトリクスクエリ、決定ロジックを分離する前に、公開範囲、期間、ロールバック対象を定義します。メトリクスの欠落が誤って「成功」と判定されないよう、成功、失敗、証拠不十分を明確に区別します。また、コントローラーの再起動に耐えられるよう永続状態と冪等なアクションを使用し、分析ウィンドウをロールアウトのステップに一致させ、小サンプルノイズ、メトリクスの遅延、ロールバックストームに対処します。
最初に明確にすべき質問
- 対象は単一のサービスか、マルチサービスのオーケストレーションか、それともKubernetesのワークロードのみか?
- トラフィックの分割はランダムリクエスト、安定したユーザーハッシュ、リージョン、テナントのいずれに基づくか、またユーザーのスティッキネス(固定化)は必要か?
- どのSLOが重要か:エラー率、レイテンシ、ビジネスコンバージョン、コスト、そして安定版ベースラインはどのように選択されるか?
- 各ステップの実行可能時間はどのくらいか、何回の失敗でロールバックをトリガーするか、また不確実な分析結果の場合は人間の判断のために一時停止すべきか?
- データベーススキーマ、メッセージ形式、外部APIは後方互換性があるか、旧バージョンへのロールバックは安全か?
30秒で答えるフレームワーク
「私はサービスを、ロールアウトコントローラー、トラフィックルーティングアダプター、メトリクスアナライザー、永続状態ストアに分割します。すべてのロールアウトには安定版と候補版のバージョン、段階的な重み、分析ウィンドウ、そして明示的な成功・失敗・判定不能の条件を持たせます。コントローラーは成功後にのみトラフィックを拡大し、失敗時には安定版に戻し、証拠が不十分な場合はアラートを発報して一時停止します。コマンドにはバージョンと冪等性キーが付与され、状態は次のステップに進む前に永続化されます。ルーターやメトリクスの短時間の停止中は、最後の安全な重みを維持し、復旧後に永続状態から再開します。」
ステップごとのソリューション
ステップ1:目標と規模の定義
1つのリージョンで1日あたり200件のリリースを処理し、各リリースは最大40分間続き、コントローラーは毎秒数十件の状態およびメトリクスイベントを処理すると仮定します。ビジネスリクエストは既存のゲートウェイとサービスに残ります。このオーダーオブマグニチュード(概算見積もり)から、リクエストごとのワークフローインスタンスではなく、高可用性のコントローラーとキューを使用する設計が導かれます。
安全性の目標は、影響範囲(ブラストレイディウス)を制限することです。1% → 5% → 25% → 50% → 100%のような設定可能なシーケンスは例示であり、普遍的なしきい値ではありません。欠落したシグナルがリリースの枠を永遠に占有しないよう、各ステップには最小観察時間と最大待機時間が必要です。
ステップ2:コントロールプレーンとデータプレーンの分離
コントロールプレーンは、リリース仕様、バージョンのダイジェスト、現在のステップ、目標の重み、分析結果、実行者を保存します。データプレーンは、ゲートウェイまたはサービスメッシュを使用して、リクエストを安定版またはカナリアのReplicaSetにルーティングします。Argo Rolloutsのアーキテクチャは、Rollout、2つのバージョン管理されたReplicaSet、Services/Ingress、AnalysisTemplate/AnalysisRunを分離しており、独立して進化できる境界を示しています。
メトリクスアダプターはPrometheusなどのプロバイダーに対してクエリを実行し、時間ウィンドウとともに観測結果を返すだけです。決定エンジンはポリシーを適用し、ルートを直接編集しないため、メトリクスバックエンドを置き換えてもロールアウトステートマシンは変更されません。
ステップ3:回復可能なステートマシンの構築
Draft → Running → Paused → Promoting → Succeededのような状態を使用し、RunningまたはPausedからAborting → RolledBackに移行できるようにします。すべての遷移にはrollout_id、希望するバージョン、ステップ番号、冪等性キーが付随します。ユニーク制約またはcompare-and-setによって、2つのコントローラーが同じロールアウトを同時に進行させるのを防ぎます。
Running + analysis=success -> Promoting(next_weight)
Running + analysis=failure -> Aborting(weight=0)
Running + analysis=inconclusive -> Paused(reason=insufficient_signal)
Paused + operator=resume -> Running
Aborting + route=stable -> RolledBack再起動後、コントローラーは最後にコミットされた状態から未完了のアクションを再生します。ルートの更新と状態の書き込みはシステムをまたぐ単一のアトミックトランザクションにはできないため、アクションは繰り返し実行可能である必要があります。同じ重みを2回設定しても余分な影響はなく、コントローラーは次のステップを選択する前に実際のルートを読み取ります。
ステップ4:メトリクスとウィンドウの選択
各ステップには、カナリア対安定版のエラー率デルタ、P95レイテンシデルタ、主要リクエストの成功率など、少なくとも1つの信頼性シグナルと1つのビジネスシグナルを持たせる必要があります。カナリアのリトライが安定版の最初のリクエストと比較されないよう、クエリウィンドウ、分母、フィルターを固定します。
ウィンドウは収集の遅延をカバーし、ステップのタイムアウト内に収まる必要があります。GoogleのSREカナリアガイダンスでは、カナリアを対照群と比較することを推奨し、メトリクスの期間が短いカナリアステージより長いとシグナルが曖昧になると警告しています。サンプル数が少ない場合は判定不能(inconclusive)とマークします。不十分な証拠に基づいて拡大するよりも、一時停止する方が安全です。
ステップ5:トラフィック分割とスティッキネスの処理
ランダムなリクエスト分割はステートレスAPIに適しています。一貫したエクスペリエンスを提供するには、ユーザーまたはテナントの安定したハッシュを使用し、ルーティングされたバージョンを記録します。パーセンテージルーティングでは、候補版の異常、適用されなかった重み、リージョン間の差異、キャッシュキーの衝突を処理する必要があります。
ルーティングアダプターは、有効な重みとバージョンダイジェストを返します。希望する値と実際の値が異なる場合、コントローラーは一時停止してアラートを出します。ルーティングAPIの成功レスポンスは、トラフィックが切り替わった証拠にはなりません。
ステップ6:ロールバック、一時停止、人的介入の設計
失敗時には、カナリアトラフィックの増加を停止し、ゼロまたは安全な重みまで減らします。旧バージョンを実行可能な状態に維持し、ロールバック後もスキーマを読み取れるよう、データベースのマイグレーションにはexpand/contract(拡張/縮小)順序を採用します。ルーターが利用できない際の無限ループを避けるため、ロールバック自体にもタイムアウトとリトライ制限が必要です。
分析結果がInconclusiveの場合、証拠とともに一時停止します。クエリ、サンプル数、バージョン、ウィンドウ、しきい値は監査可能でなければなりません。Argo Rolloutsでは、人間の判断を仰ぐための一時停止結果としてInconclusiveが文書化されており、欠落データを成功として扱うよりも安全です。
ステップ7:信頼性、権限、監査
コントローラーにはリーダー選出またはリースを使用します。キューの配信は少なくとも1回(at-least-once)である可能性があるため、コンシューマーは冪等性キーによって重複排除を行います。リリース仕様、ポリシーの変更、承認は不変の監査ログに書き込みます。リリースのオーナーのみが重みを変更でき、メトリクスの認証情報はシークレットマネージャーから取得し、ロールバック権限は通常のプロモーション権限から分離します。
コントロールプレーンのSLO(遷移レイテンシ、スタックしたロールアウト、ロールバック時間、実際の重み対希望の重み、メトリクスクエリの失敗)を追跡します。コントロールプレーンで障害が発生した場合は、カナリアを自動的に100%にするのではなく、最後の安全なルートを維持し、オペレーターを呼び出します。
ステップ8:検証と負荷テスト
エラー率の上昇、データなし、データの遅延、ルーターのタイムアウト、コントローラーの再起動、重複メッセージ、互換性のないデータベーススキーマなどの障害を注入します。正常系だけでなく、各障害に対する最終状態とアラートを確認します。
過去のリリースを再生して分析クエリのコストとキューのバックログを測定し、N+1のコントローラー障害テストを実行します。100件の同時ロールアウトを負荷上限の例として使用し、状態ストアのロック競合、メトリクスプロバイダーのQPS、ルート更新頻度を観察した上で、同時実行制限を設定します。
トレードオフと境界線
自動化によって人間の作業による遅延は排除されますが、しきい値の設定が悪いとノイズがロールバックにつながったり、本物のリグレッションが見逃されたりする可能性があります。絶対的なエラー率のしきい値は説明が容易で低トラフィックのサービスに適しています。安定版との比較や層別化されたベースラインは日々のトラフィック変動への耐性が高いものの、より慎重な統計処理とサンプル調整が必要です。高リスクのサービスでは、自動分析後に人間の承認を必須にすることもできます。
ブルーグリーンは迅速な切り替えとシンプルなロールバックを提供しますが、通常は2倍のキャパシティを必要とします。カナリアは影響範囲を抑えますが、トラフィック分割と分析ウィンドウが必要です。フィーチャーフラグは機能の公開をバイナリのリリースから切り離すことができますが、バイナリ、依存関係、スキーマの互換性チェックに代わることはできません。ロールバックコスト、トラフィック特性、SLOリスクに応じて選択してください。
ロールアウト計画とエビデンス
まずは1つのステートレスサービスに対して、読み取り専用の分析と手動の一時停止から始めます。stable/canaryラベル、メトリクスのグループ化、監査フィールドを検証し、次に自動ロールバックを有効化し、最後にマルチリージョンのシグナル、ビジネスメトリクス、同時実行数の上限を追加します。すべての段階で人間のキルスイッチと明確なオーナーを維持してください。
Google SREはカナリアを「時間制限のある部分的なデプロイメントと評価」と定義し、その評価をリリースプロセスにフィードバックすることを求めています。Argo RolloutsはAnalysisTemplate/AnalysisRun、メトリクスしきい値、および成功・失敗・判定不能の結果を提供します。公開されているシステム設計面接の教材でも、トラフィック分割、ガードレール評価、自動ロールバックがカナリアの設計ポイントとして扱われています。したがって、Offer.ccのこの記事では、デプロイ戦略の用語集ではなく、回復可能なコントロールプレーンのステートマシンに焦点を当てています。
よくある間違いとフォローアップ
「トラフィックの10%を監視する」とだけ答える
母集団、期間、分母、障害時のアクションがなければ、安全性は証明されません。安定版ベースライン、ウィンドウ、しきい値、一時停止、ロールバックパスを追加してください。
メトリクスの欠落を成功として扱う
コレクターの停止によって、見かけ上健全なシグナルが作られることがあります。データなし、NaN、遅延、サンプル不足は判定不能(inconclusive)とマークし、一時停止してアラートを発報してください。
Deploymentはロールバックしたがルートを戻していない
古いポッドが健全であることは、リクエストがカナリアから離れたことの証明にはなりません。有効な重み、サービスセレクター、キャッシュまたはセッションのスティッキネスを検証してください。
データベースのマイグレーションを不可逆にする
旧バージョンが新しいスキーマを読み取れない場合、ルートを戻しても安全性を回復できません。後方互換性のあるexpand/contract変更を使用し、マイグレーション状態をロールアウトのゲート(通過条件)にしてください。
メトリクスプロバイダーがダウンしている場合はどうするか?
最後の安全な重みを維持し、自動拡大を停止し、未完了の分析を記録してオーナーに通知します。復旧後は永続状態から再開し、デフォルトの「成功」で隙間を埋めてはいけません。