APIゲートウェイ
API Gateway ・ エーピーアイゲートウェイ
多数のAPIの手前に立つ単一の入口。認証・流量制御・振り分け・集約を一手に引き受ける。
✦ 30秒で試す — 航海サービスを細かく分けたら、スマホの画面が遅くなった中継役に3つをまとめて取らせると、画面が出るまでは?予想してみる概要
APIゲートウェイは、多数の API を提供するシステムの手前に立つ「単一の入口」です。クライアントは個々のバックエンドサービスの場所や構成を知らなくてよく、いつもゲートウェイという一つの窓口にリクエストを送ります。ゲートウェイはそれを適切なサービスへ振り分けると同時に、認証、レート制限、TLS 終端といった「どのAPIにも共通して必要な処理」を一手に引き受けます。
役所の総合受付をイメージすると分かりやすいでしょう。来訪者は各課の部屋番号を覚える必要がなく、受付で身分確認を済ませれば案内してもらえます。各課は本来の業務に集中でき、受付のルールを変えれば全課に一度に適用できます。マイクロサービスアーキテクチャの実運用では、ほぼ必ずこの層が存在します。代表的な実装・サービスには Kong、Apigee、Amazon API Gateway、Envoy ベースのゲートウェイ群などがあります。
なぜ生まれたか
モノリスの時代、API の入口は自然と一つでした。ところがマイクロサービスに分割すると、クライアントは苦しみを背負わされます。注文サービス・在庫サービス・ユーザーサービスがそれぞれ別のホストとポートで動いているとき、モバイルアプリは全サービスのアドレスを知り、それぞれと TLS を張り、画面を一つ描くために何往復も通信しなければなりません。モバイル回線では往復のたびに遅延が積み上がります。さらにサービス側の事情 — 分割・統合・移設 — が起きるたびに、配布済みのクライアントが壊れる。内部構造がそのまま外部に露出してしまうのです。
もう一つの苦しみはサービス側にありました。認証、レート制限、CORS 対応、アクセスログといった横断的関心事を、全サービスがそれぞれの言語・フレームワークで重複実装することになり、実装漏れは即セキュリティホールになります。「入口を一つに集約し、共通処理はそこでまとめて済ませ、内部構成はその後ろに隠す」— APIゲートウェイは、この二つの苦しみへの答えとして定着しました。往復の多さについても、ゲートウェイは答えを持っています。画面に必要な複数の問い合わせをゲートウェイがまとめて引き受け、遅い回線の往復を1回に束ねる「集約」です(詳細で扱います)。Netflix が2013年にオープンソースとして公開した Zuul のように、大規模サービスが自前のゲートウェイを作って公開したことも、この形が広まるきっかけになりました。
詳細
前段に立つ構成
ゲートウェイはすべての外部リクエストの通り道になり、パスやホスト名に基づいて後段のサービスへルーティングします。共通処理を通過済みのリクエストだけが内部に届くため、各サービスは「認証済み・流量制御済み」を前提にビジネスロジックへ集中できます。
引き受ける横断的関心事
ゲートウェイの仕事は大きく分けて4種類あります。第一にルーティング。/orders は注文サービスへ、/users はユーザーサービスへ、といった振り分けに加え、バージョンごとの出し分けやカナリアリリースのような比率制御も担えます。第二にセキュリティ。TLS 終端、JWT などのトークンの検証、APIキーの管理を入口で済ませ、WAF と組み合わせて防御の第一線を張ります。第三に流量の制御。クライアントごとのレート制限やクォータで、特定の利用者の暴走や DDoS 的な負荷から後段を守ります。第四に変換と集約。複数サービスへの問い合わせを束ねて1レスポンスにまとめたり、内部の gRPC を外部向けの REST API に変換したり、キャッシュで後段の負荷を減らしたりします。すべてのトラフィックが通る場所なので、メトリクス収集や分散トレーシングの起点としても最適です。
リバースプロキシとの違い
「それはリバースプロキシでは?」という疑問はもっともで、実際、APIゲートウェイはリバースプロキシの一種です。違いは意識するレイヤにあります。汎用のリバースプロキシがホスト名やパスといった HTTP の器を見て中継するのに対し、APIゲートウェイは「これはどのAPIの、どの操作か」を理解した上で動きます。APIごとの認可ポリシー、利用者ごとのクォータ、APIキーの発行・失効、開発者ポータルでの仕様公開 — つまり「APIを製品として管理する」機能まで含むのがゲートウェイです。リバースプロキシが土台で、その上にAPI管理の層を載せたもの、と捉えると位置づけが明確になります。また、外から内への「南北」トラフィックを扱うのがゲートウェイ、サービス間の「東西」トラフィックを扱うのがサービスメッシュ、という役割分担もよく整理に使われます。
集約 — 遅い往復を1回に束ねる
「なぜ生まれたか」で見た往復の多さに、ゲートウェイがどう効くかを追ってみます。画面を1枚出すのに会員情報・注文履歴・おすすめの3つが要るとき、クライアントが1つずつ取りに行けば、遅いモバイル回線(往復で100ミリ秒を超えることも珍しくありません)を3往復します。ゲートウェイが集約を引き受けると、クライアントは「この画面のデータをください」と1回頼むだけになり、ゲートウェイがデータセンター内の速い回線(往復数ミリ秒)で各サービスを回って、結果を1つの応答にまとめて返します。遅い往復の回数そのものを減らし、減らせない往復を速い場所へ移す — これが集約の効き目です。
後ろの問い合わせが前の結果を必要とする(注文履歴には会員IDが要る)とき、クライアント側で順に呼ぶと遅い往復が直列に積み上がります。ゲートウェイの中なら、その直列の待ちも速い回線の上で済みます。下の部品で、呼び出しのチップをゲートウェイの箱へ運び、モバイル回線の往復の回数と、画面が出るまでの時間の内訳がどう変わるかを確かめてください。
画面を1枚出すのに3つのデータが要ります。まず「画面を開く」を押すか、呼び出しのチップをつかんでゲートウェイの箱へ運んでください。
集約には代償もあります。集約の中の1つのサービスが遅いと応答全体が遅れるため、ゲートウェイ側でタイムアウトを切り、取れなかった部分は空で返すといったグレースフルデグラデーションの設計が要ります。また、画面の都合で集約の形がどんどん増えていくと、次の節の問題に行き着きます。
BFF — クライアントの種類ごとに入口を分ける
単一のゲートウェイに全クライアントの要望を詰め込むと、モバイル向けの軽量レスポンスとWeb向けの詳細レスポンスが同居して肥大化していきます。そこで、クライアントの種類ごとに専用のゲートウェイ層を立てるのが BFF(Backend for Frontend)パターンです。モバイル用BFFは画面に必要な情報だけを集約して返し、Web用BFFは SPA に合わせた形に整える。各BFFはそれを使うフロントエンドチームが所有するため、「どのクライアントの都合を優先するか」という綱引きも解消されます。近年は BFF に GraphQL を採用し、クライアント自身が必要なデータの形を宣言できるようにする構成も人気です。
落とし穴 — 単一障害点と太りすぎ
すべてのトラフィックが一点を通る構成は、そのまま弱点にもなります。ゲートウェイが落ちれば全APIが落ちるため、冗長化とスケーリング、そして自身のヘルスチェックは必須です。また全リクエストに1ホップ分の遅延が加わることも忘れてはいけません。もう一つの古典的な失敗が「太りすぎゲートウェイ」です。便利な場所ゆえにビジネスロジックが少しずつ染み出し、気づけばゲートウェイ自体が巨大なモノリスと化して、全チームの変更がそこで渋滞する — これでは何のためにサービスを分割したのか分かりません。ゲートウェイに置いてよいのは横断的関心事まで、ドメインのロジックは後段のサービスに、という線引きを守り続けることが、この構成を健全に保つ鍵になります。
試問
スマホの画面1枚を出すのに、会員情報・注文履歴・おすすめの3つのデータが要ります。スマホとゲートウェイ(入口の中継役)のあいだは遅いモバイル回線(往復120 ms)、ゲートウェイと3つのサービスのあいだはデータセンター内の速い回線(往復4 ms)です。3枚の呼び出しのチップは、「スマホが1つずつ呼ぶ」と「ゲートウェイがまとめる」の2つの箱のあいだで運べ、運ぶたびに画面を開き直して時間を測ります。
画面を1枚出すのに3つのデータが要ります。「画面を開く」で時間を測れます。呼び出しのチップは、つかんで2つの箱のあいだを運べます(押しても入れ替わります)。
