代表的な面接トピック

システムデザイン面接:マルチリージョン API Gateway の設計

システム設計難しい
Offer.cc 編集チーム公開日 更新日

質問

300 のバックエンドサービス向けにマルチリージョン API Gateway を設計してください。ピーク時に秒間 50 万リクエストを処理し、2 万件のルート、認証、クォータ、カナリアルーティング、トラフィックを中断しない設定更新をサポートしながら、Gateway による追加 p99 レイテンシを 10 ミリ秒未満に抑え、リージョン障害にも耐えられる構成にする必要があります。

問題とユースケース

300 のバックエンドサービスに対するパブリックエントリーポイントを設計します。3 つのアクティブリージョン全体でピーク時に秒間 500,000 リクエストを処理し、約 20,000 件のルート定義を保持します。Gateway は TLS 終端、呼び出し元の認証、粗粒度の認可とクォータの適用、リクエストの正規化、アップストリームの選択、重み付けカナリアのサポート、メトリクスとトレースの出力を担います。可用性目標は 99.99%、Gateway が追加する p99 レイテンシは 10 ミリ秒未満と仮定します。これらは面接用の前提条件であり、特定製品の仕様ではありません。

ルートやポリシーの変更は、30 秒以内に正常な Gateway に反映される必要があります。緊急の認証情報失効には、より高速な経路が求められます。設定の誤りによって全リージョンがダウンしてはならず、コントロールプレーンが利用できなくなっても、Gateway は最後に確認された正常な設定でトラフィックの処理を継続できなければなりません。バックエンドのビジネスロジック、レスポンスの組み立て、長時間実行されるワークフローのオーケストレーション、アプリケーション固有の認可は Gateway の対象外とします。

これはシニアレベルのシステムデザイン問題です。重要な課題は各コンポーネントの間に存在するためです。すなわち、クリティカルなリクエストパス(ホットパス)の定義、設定とトラフィックの分離、障害時の振る舞いの限定、そしてグローバルに共有されたエントランスが単一障害点にならないことの証明が求められます。

面接官が評価しているポイント

優れた回答では、まずデータプレーンコントロールプレーンが明確に分離されています。各リージョンの Gateway プロセスは、イミュータブルなローカルスナップショットからリクエストを処理します。コントロールプレーンは、ルートやポリシーの変更を検証、バージョン管理、保存、配布します。もし候補者がリクエストごとにデータベースやリモート設定の参照を行う設計にした場合、レイテンシ目標もコントロールプレーン障害の隔離も達成できません。

面接官は、Gateway の責務の境界も確認します。TLS 終端、ID 検証、ルートマッチング、粗粒度ポリシー、レート制限、ヘッダー処理、タイムアウト、テレメトリは横断的関心事です。在庫確認、決済判断、オブジェクトレベルの権限管理は各サービスに属します。任意のビジネスプラグインを実行する汎用 Gateway は、テストが困難になり、リリース時のリスクも高まります。

キャパシティは再計算可能であるべきです。秒間 500,000 リクエストの場合、平均 2 KiB のインバウンドリクエストは、プロトコルのオーバーヘッドを除いて約 500,000 × 2 KiB ≈ 0.95 GiB/s になります。ベンチマーク済みの Gateway インスタンス 1 台が、要求された p99 で秒間 Q リクエストを安全に維持できる場合、フリート全体で少なくとも ceil(500,000 / Q) 台のインスタンスが必要となり、さらにリージョン障害に備えた追加キャパシティが必要です。同等規模の 3 リージョンのうち 1 つが失われると、残りの各リージョンの負荷は約 167,000 から 250,000 リクエスト/秒へと 50% 増加します。キャパシティ計画にはこの状態を含める必要があります。

最後に、完全な回答には、設定の安全性、リージョン間ルーティング、過負荷の伝播、リトライ、可観測性、明示的なデグラデーションが含まれます。「複数リージョンにデプロイする」とだけ答えても、トラフィックがどのように移動するのか、状態がどのように変化するのか、パーティション発生時に何が利用可能であり続けるのかを説明したことにはなりません。

回答前の明確化のための質問

  • クライアントは誰ですか? HTTP API を使用するパブリック Web、モバイル、およびパートナーのクライアントを想定します。内部のサービス間トラフィックは、別個の Ingress やサービスメッシュポリシーを使用できます。
  • 可用性の単位は何ですか? 1 つのリージョンには、少なくとも 3 つの障害ゾーンにまたがる Gateway インスタンスが含まれます。3 つのリージョンはアクティブ・アクティブ構成であり、グローバルルーティングによって異常なリージョンが切り離されます。
  • ルートはホスト名、パス、HTTP メソッドによって独立していますか? はい。マッチング順序は決定論的である必要があり、曖昧なルート定義は公開前に拒否されます。
  • どのポリシーが同期実行されますか? 署名検証、ルートマッチング、粗粒度の認可チェック、クォータ、リクエスト制限、ヘッダー変換です。判断のために Gateway がビジネスデータを取得することはありません。
  • 設定の鮮度はどの程度必要ですか? 通常の変更は 30 秒以内に収束します。Gateway は適用中のバージョンを報告します。セキュリティ上の失効は、スナップショット全体の再構築を強制するのではなく、短いトークン有効期間や個別に配布される小さな拒否リスト(deny list)を使用します。
  • リージョンをまたぐクォータはどの程度厳密ですか? デフォルト設計では、グローバル予算から割り当てられたリージョンごとのクォータを使用します。厳密なグローバルカウンターはリクエストごとにリージョン間依存関係を追加するため、個別かつ十分な正当化が必要です。
  • Gateway はリトライを行ってもよいですか? 最初の試行が受理されていないことが確認された場合のみ、少量の再試行予算(retry budget)の範囲内で、冪等なリクエストに限り許可されます。非冪等な書き込みには、アプリケーション側の冪等性キーが必要であり、自動リトライは行いません。
  • 対象外となるものは何ですか? Gateway 前段の DDoS 緩和、サービス実装、データベースレプリケーション、アプリケーションレベルのトランザクションは別個のシステムです。

30秒の回答フレームワーク

「ヘルス状態を認識するグローバルルーティングの背後にある 3 つのアクティブリージョンのそれぞれに、ステートレスな Gateway フリートを配置します。各リクエストは単一リージョン内で完結します。Gateway は TLS を終端し、ローカルの鍵キャッシュから ID を検証し、イミュータブルなルートスナップショットにマッチさせ、粗粒度ポリシーとリージョンクォータを適用した後、同一リージョン内の正常なアップストリームを選択します。独立したコントロールプレーンが各設定を検証し、バージョンを割り当て、少数の Gateway コホートにカナリア配信してアトミックに有効化します。このプレーンに障害が発生しても、Gateway は最後に確認された正常なスナップショットで処理を継続します。同等な 3 リージョンのうち 1 つが停止した後の 50% の負荷増加に耐えられるよう各リージョンをサイジングし、過負荷の増幅を防ぐためにリトライとキューを制限し、追加レイテンシ、ルート結果、アップストリームのヘルス状態、設定バージョン、リージョンフェイルオーバーを監視します。」

ステップごとの詳細解説

このシステムには 4 つのレイヤーがあります。グローバルトラフィック管理は、クライアントを正常で近隣のリージョンにルーティングします。リージョンロードバランサーは、複数ゾーンにまたがるステートレスな Gateway インスタンスにトラフィックを分散します。データプレーンは、リクエストを同一リージョンのサービスエンドポイントにプロキシします。コントロールプレーンは、目的の設定を保存し、バージョン管理されたスナップショットへとコンパイルし、リクエストパスから完全に独立して配布します。Microsoft は Gateway をルーティングおよび横断的関心事のための一元化されたエントリーポイントとしてドキュメント化しており、そのマネージドおよびセルフホストの Gateway モデルでも同様に、中央管理された設定と分散されたランタイムトラフィックを分離しています。

通常のリクエストフローは以下のとおりです。

  1. グローバル DNS または Anycast が正常なリージョンを選択します。リージョンロードバランサーが Gateway インスタンスを選択します。
  2. Gateway は TLS を終端し、リクエストサイズおよび接続制限を適用し、リクエスト ID を付与します。
  3. キャッシュされた発行者メタデータと公開鍵を使用して、署名付きトークンを検証します。リクエストごとに ID プロバイダーを呼び出すことはありません。
  4. コンパイル済みのマッチャーが、ホスト、メソッド、正規化されたパスを使用して、アクティブなスナップショットからルートを選択します。
  5. ポリシーチェーンが、粗粒度のスコープ、リージョンクォータ、ヘッダー、タイムアウト、オプションのカナリア選択を適用します。
  6. Gateway は、ローカルキャッシュされたサービスディスカバリから正常なエンドポイントを選択し、制限時間を設定してリクエストを転送します。
  7. 結果、レイテンシ、ルート ID、アップストリームクラスタ、設定バージョン、トレースコンテキストを記録し、ホットパス外でテレメトリをストリーミングします。

ルート定義は宣言的な形式を維持できます。

text
Route {
  id: string
  host: string
  methods: string[]
  path_template: string
  upstream_cluster: string
  auth_policy_id: string
  quota_policy_id: string
  timeout_ms: uint32
  retry_policy: { max_attempts, retryable_statuses }
  traffic_split: [{ revision, weight }]
}

コントロール API は、冪等性キーとともに目的のリビジョンを受け入れ、リビジョン ID を返します。検証プロセスでは、スキーマ、競合するマッチ条件、参照されるクラスタ、証明書の所有権、ポリシー制限、安全でないリトライの組み合わせをチェックします。コンパイラは、事前に構築されたマッチ構造を持つイミュータブルなスナップショットを生成します。公開は、検証、シャドウ比較、小規模なカナリアコホート、1 ゾーン、1 リージョン、そして全リージョンの順に進みます。各 Gateway はスナップショットをダウンロードし、チェックサムと署名を検証し、リクエストスレッド外でビルドして、単一のアトミックポインタを切り替えます。処理中のリクエストは古いスナップショットで完了します。ヘルスチェックに失敗した場合、コホートは前のバージョンにロールバックされます。

最大のボトルネックは、フリート規模での設定の安全性です。変更を個別のミュータブルな更新として送信すると、ルート、ポリシー、証明書のバージョン間で不整合が発生する可能性があります。バージョン管理されたスナップショットにより、アクティベーションの単位が明示的になります。Gateway は、検証済みの最新スナップショットをローカルディスクまたは耐久性のあるノードストレージに永続化し、少なくとも 1 つ前のバージョンを保持して、desired_versiondownloaded_versionactive_version を公開します。コントロールプレーンが停止しても変更が凍結されるだけで、トラフィックは停止しません。破損または不完全なスナップショットは拒否されます。緊急の失効処理において、20,000 件のルートすべての再コンパイルを待つべきではありません。短命な認証情報でリスクを抑えつつ、署名付きの小さな拒否リストに独自の高速配布チャネルと有効期限を持たせます。

キャパシティに関しては、空のリバースプロキシではなく、完全なポリシーチェーンを用いてベンチマークを行います。テストされたインスタンスが目標の p99 で秒間 8,000 リクエストを処理できる場合、ピークトラフィックにはヘッドルームを除いて ceil(500,000 / 8,000) = 63 台のインスタンスが必要です。同等の 3 リージョンのうち 1 リージョンが停止した場合、生存リージョンあたり秒間 250,000 リクエスト、つまりそのベンチマークでは 32 台のインスタンスが必要になります。各リージョンに 40〜45 台をデプロイすることで運用上のヘッドルームを確保できます。この数値は例示であり、実際の TLS、トークン検証、ペイロードサイズ、ロギング、アップストリームのレイテンシを用いた測定値に置き換える必要があります。

Gateway はトラフィックを蓄積するのではなく、負荷を削減(ロードシェディング)しなければなりません。同時実行リクエスト数、接続数、リクエストボディサイズ、ルートごとのキュー、テレメトリバッファに制限を設定します。デッドラインをアップストリームに伝播させます。サーキットブレーカーにより、障害が発生しているサービスが Gateway の全接続を使い果たすのを防ぎます。リトライは限定された一連の冪等な失敗のみを対象とし、ジッターを使用し、すべての試行をリトライ予算に計上します。アップストリームが飽和している場合、無制限にキューを溜めて共有 Gateway を枯渇させるよりも、速やかに 503 を返す方が安全です。

認証は署名付きトークンのローカル検証を使用します。公開鍵は非同期で更新され、ローテーション中は新旧が重複して保持され、最後に確認された有効なセットが一定期間維持されます。トークンが不透明(opaque)でイントロスペクションが必須の場合は、短い肯定結果をキャッシュし、ルートのリスクに応じてフェイルオープンまたはフェイルクローズを定義します。この依存関係は可用性の計算を変化させます。Gateway は信頼できるドメイン状態を持たないため、オブジェクトの所有権やビジネス認可はサービス内に留めます。

クォータはホットパス上ではリージョン単位で処理されます。グローバルアロケータがテナントの予算をリージョンごとの短期リースに分割し、リージョンのデータストアがアトミックに判定します。これにより、パーティション発生時に無制限のグローバルクォータが増殖するのを防ぎますが、隔離されたリージョンは自らのリース分しか消費できません。独立したリージョンがリクエストの受け入れを継続し、後で調整を行うようにした場合、可用性は向上しますが超過受け入れ(over-admission)が発生する可能性があります。プロダクトオーナーはその許容範囲を選択しなければなりません。詳細なトークンバケットの実装は、各 Gateway 内で再構築するのではなく、レートリミッターサブシステムに委譲されます。

AWS は、マルチリージョン Gateway 向けにアクティブ・パッシブのフェイルオーバールーティングとアクティブ・アクティブの重み付けルーティングの両方をドキュメント化しています。ここでは、コールドフェイルオーバーのリスクを低減するためにアクティブ・アクティブを採用します。ヘルス状態は階層的です。インスタンスのヘルスチェックは 1 つのプロセスを切り離し、ゾーンシグナルはゾーンをドレインし、合成エンドツーエンドプローブとリージョンのエラー率によってリージョン全体が切り離されます。トラフィックの移行は可能な限り段階的に行われます。リージョンのバックエンドとそのデータストアも準備ができている必要があります。Gateway だけを移動しても、利用できないサービスを健全にすることはできません。

可観測性には、Gateway による追加の p50/p95/p99 レイテンシ、ルートごとのリクエスト率とエラー率、TLS および認証の失敗、クォータ判定結果、アクティブ接続数、キューの深さ、リトライ、サーキットの状態、アップストリームのレイテンシ、エンドポイントのヘルス状態、設定バージョンの遅延が必要です。ログは成功トラフィックをサンプリングしますが、制限された予算内でセキュリティイベントやエラーイベントを保持します。トレースは受信したコンテキストを保持し、Gateway のスパンを開始します。アラートは Gateway の障害とアップストリームの障害を明確に区別し、サービスの障害に対してオペレータが正常な Gateway をロールバックしてしまうのを防ぎます。

主な代替案は、単一の汎用 Gateway と、クライアントやドメインごとの Gateway または BFF(Backend for Frontend)の比較です。単一のフリートは外部エントリとガバナンスを簡素化しますが、影響範囲が拡大し、ポリシーの肥大化を招きます。複数の Gateway はチームとクライアント固有の動作を分離しますが、運用が重複し、一貫したグローバル制御が必要になります。まずは 1 つの軽量なプラットフォーム Gateway とドメイン所有のサービスから始め、クライアントが真に集約や異なるインターフェース契約を必要とする場合にのみ BFF を追加します。サービスメッシュはこの設計を East-West トラフィック向けに補完するものであり、パブリックな North-South Gateway を置き換えるものではありません。

検証には、決定論的マッチングを確認するルートテーブルプロパティテスト、スナップショット互換性テスト、通常時および 1 リージョン障害時のキャパシティ負荷テスト、新旧リビジョンのシャドウ比較、ならびにコントロールプレーンの接続喪失、古い鍵、遅延アップストリーム、テレメトリのバックプレッシャー、ゾーン喪失、リージョン退避のフォールトインジェクションが含まれます。各障害に観測可能な状態と限定された応答が存在して初めて、設計は完成したと言えます。

高品質な回答サンプル

「3 つのアクティブリージョンと複数ゾーンにまたがるステートレスな Gateway フリートを実行します。グローバルルーティングがクライアントを正常で近隣のリージョンに送り、リージョンロードバランサーが Gateway インスタンスへトラフィックを分散します。ホットパスは TLS を終端し、ローカルの鍵キャッシュから署名済み ID を検証し、コンパイル済みルートとマッチングし、粗粒度の認可とリージョンクォータを適用し、同一リージョン内の健全なエンドポイントを選択して、制限されたタイムアウトで転送します。ドメイン認可はサービス内に留めます。

リクエストパスが設定データベースを直接読み取ることは決してありません。独立したコントロールプレーンがルートとポリシーの変更を検証し、曖昧なマッチや安全でないリトライを拒否し、イミュータブルで署名されたスナップショットをコンパイルして、シャドウ、コホート、ゾーン、リージョンの各ステージを通じてロールアウトします。各インスタンスは別スレッドで新しいスナップショットをビルドし、アトミックに切り替えます。最後に確認された正常なバージョンを永続化するため、コントロールプレーンが停止してもトラフィックを止めることなく変更のみを停止できます。バージョンの遅延状況と自動ロールバックにより、部分的なロールアウトを可視化し、安全に取り消すことができます。

秒間 500,000 リクエストにおいて、2 KiB のインバウンドデータはオーバーヘッド前で約 0.95 GiB/s になります。ポリシーチェーン全体をベンチマークしてインスタンスあたりの安全なスループット Q を算出し、ceil(500,000 / Q) に障害時のヘッドルームを加えた数をデプロイします。同等な 3 リージョンのうち 1 つを失うと生存リージョンの負荷が 50% 増加するため、リージョンのキャパシティと負荷テストにはその状態を含めます。

接続数、同時実行数、リクエストボディ、キュー、リトライ試行回数を制限します。冪等なリトライは小さな予算を使用し、非冪等な書き込みには冪等性キーを要求するか、自動リトライを行いません。グローバルクォータは短期間のリージョンリースとして割り当てられ、ネットワーク分断時のトレードオフを明示化します。最後に、Gateway による追加レイテンシ、ルート結果、アップストリームのヘルス状態、リトライとサーキットの動作、アクティブな設定バージョン、合成リージョンプローブを監視し、コントロールプレーンの喪失、不正な設定、ゾーン喪失、リージョンフェイルオーバーのテストを本番稼働前に実施します。」

よくある間違い

  • リクエストごとにデータベースからルートやポリシーを読み取る → レイテンシと可用性がコントロールプレーンに依存してしまう → 検証済みのローカルスナップショットから処理し、最後に確認された正常な状態を維持する。
  • Gateway プラグインにビジネスロジックを組み込む → リリースの影響範囲がグローバルに広がり、ドメインの所有権が曖昧になる → Gateway は宣言的な構成を維持し、ビジネス判断はサービスまたは正当な理由のある BFF に移管する。
  • 個別のミュータブルな変更を公開する → ルート、ポリシー、証明書が互換性のない組み合わせで有効化される恐れがある → 単一のバージョン管理されたスナップショットをコンパイルし、アトミックに切り替える。
  • 複数インスタンスの存在だけで十分な耐障害性があるとみなす → 1 つの不正なリビジョンがすべてのインスタンスを同時に破壊する可能性がある → コホート、ゾーン、リージョン単位でカナリアリリースを行い、自動ヘルスゲートとロールバックを備える。
  • 通常のピーク時のみを想定してサイジングする → トラフィック退避時に生存リージョンが過負荷になる → 3 つの同等リージョンのうち 1 つが停止した後の、リージョンあたり 50% の負荷増加を計算しテストする。
  • すべての失敗をリトライする → リトライが過負荷を増幅させ、書き込みを重複させる可能性がある → 制限された冪等なケースのみをリトライし、書き込みにはアプリケーション側の冪等性を要求する。
  • リクエストごとに ID プロバイダーを呼び出す → 認証が同期的なリモート依存関係を引き継いでしまう → 署名付きトークンをローカルで検証し、制限された許容遅延の範囲で公開鍵を非同期更新する。
  • 各リージョンに完全なグローバルクォータを付与する → テナントが許容量をリージョン数分だけ掛け算できてしまう → リージョンリースを割り当てるか、許容される超過受け入れの制限を明確に定める。
  • バックエンドを確認せずに Gateway のトラフィックを移動する → 移動先リージョンに正常なサービスやデータキャパシティが存在しない可能性がある → エンドツーエンドのリージョンプローブとバックエンドの準備状態をフェイルオーバー条件に含める。
  • すべての成功ペイロードをログに記録する → テレメトリがホットパスのリソースを消費し、機密データを漏洩させる可能性がある → 構造化メタデータを非同期で出力し、成功ログはサンプリングし、ポリシーに従ってマスキングする。

フォローアップの質問と回答

フォローアップ 1: リクエストをドロップせずに不正なルート設定をロールバックするにはどうすればよいですか?

イミュータブルなスナップショットと、少なくとも 1 つの以前に検証されたバージョンを保持します。Gateway はリクエストスレッド外で候補スナップショットをビルドし、そのチェックサムと参照を検証して、アクティブなポインタをアトミックに切り替えます。既存のリクエストは完了するまで古いスナップショットを保持します。コホートの健全性が低下した場合、コントロールプレーンはそのリビジョンを失敗としてマークし、Gateway は以前のポインタに戻します。ロールバックは設定状態を変更するだけであり、フリート全体を再起動する必要はありません。

フォローアップ 2: コントロールプレーンが 1 時間利用できなくなった場合はどうなりますか?

トラフィックは永続化されている最後に確認された正常なスナップショット上で継続して処理されます。Gateway はそのスナップショットの経過時間とバージョンを公開し、未検証の変更の受け入れを停止します。証明書と鍵のローテーションには、この状況に対応できる十分な重複有効期間が必要です。緊急の失効には、短いトークン有効期間か、個別に署名されたコンパクトな拒否リストを使用します。停止中はオペレータによる変更作業が不可能になるため、既存の認証情報等の有効期限が切れるよりずっと前に配布遅延のアラートを発報させます。

フォローアップ 3: Gateway のリトライ時に POST の重複実行を防ぐにはどうすればよいですか?

単にアップストリームへの接続が切断されたという理由だけで、Gateway が非冪等なリクエストを自動的にリトライすることはありません。レスポンスが失われる前にバックエンドが処理をコミットしている可能性があるためです。リトライ可能であるべき操作については、クライアントが冪等性キーを提供し、対象サービスが最初の結果を保存して返します。Gateway はリクエストの制限時間内かつ少量の再試行予算の範囲内でのみリトライできます。1 バイトも受信される前の接続エラーについては、トランスポート層がその状態を証明できる場合に限り、個別に処理できます。

フォローアップ 4: リージョンルーティングにはグローバル DNS と Anycast のどちらを選択しますか?

どちらでもアーキテクチャの要件を満たすことができます。ヘルス状態を考慮した DNS は運用が比較的容易ですが、キャッシュの影響でトラフィックの退避は段階的になります。Anycast はより迅速にトラフィックを誘導できますが、より高度なネットワーク運用が必要であり、依然としてアプリケーションのヘルスシグナルを必要とします。プラットフォームがすでに運用しているメカニズムを選択し、フェイルオーバー時間を測定した上で、クライアントがエンドポイントの変更を許容できるように設計します。リージョン Gateway の設計は、DNS の変更が瞬時に反映されるという非現実的な前提に依存するものではありません。

フォローアップ 5: 単一の Gateway を複数の Gateway に分割すべきなのはどのような場合ですか?

規制対象トラフィック、独立して運用されるドメイン、大幅に異なる集約ロジックを必要とするモバイルと Web クライアントなど、分離性やインターフェース契約が明確に異なる場合に分割します。すべてのマイクロサービスをミラーリングするためだけに分割してはなりません。クライアントに内部トポロジーが露出し、運用コストが増大するためです。ランタイムフリートが分離されている場合でも、共通のポリシー定義スキーマ、ID ルール、テレメトリ、リリース安全性はプラットフォーム共通の機能として維持できます。

公開情報ソース

関連する質問

関連面接ツール

システム設計の回答には「回答する」を使用

まず要件を明確にし、スケール、アーキテクチャ、コンポーネント選定、トレードオフの順に進めます。

ツールを見る