NGINX Open SourceとNGINX PlusをAPIゲートウェイとして導入する方法を紹介するブログシリーズの第1回です。
- この記事では、複数のユースケースについて詳細な設定手順を説明します。2018年に初めて公開され、現在のAPI設定のベストプラクティスを反映するよう更新されています。リライトルールの代わりに、ネストされた
locationブロックを使用してリクエストをルーティングします。 - パート2では、これらのユースケースを拡張し、本番環境のバックエンドAPIサービスを保護し、セキュリティを確保するために適用できるさまざまな安全対策について説明します。
- パート3では、NGINX Open SourceとNGINX PlusをgRPCサービスのAPIゲートウェイとしてデプロイする方法について説明します。
注記:特に記載がない限り、この記事のすべての情報はNGINX Open SourceとNGINX Plusの両方に適用されます。読みやすさを考慮し、以降は単に「NGINX」と表記します。
最新のアプリケーションアーキテクチャの中核を成すのがHTTP APIです。HTTPを使用することで、アプリケーションを迅速に構築し、容易に保守できます。HTTP APIは、単一目的のマイクロサービスからあらゆる機能を包含するモノリスまで、アプリケーションの規模を問わず共通のインターフェースを提供します。また、HTTPを使用すれば、ハイパースケールのインターネットサービスを支えるWebアプリケーションデリバリーの進歩を活用し、信頼性とパフォーマンスに優れたAPIデリバリーを実現できます。
マイクロサービスアプリケーションにおけるAPIゲートウェイの重要性について詳しく知るには、F5のブログ記事「Building Microservices: APIゲートウェイの使用」をご覧ください。
高性能かつ軽量なリバースプロキシおよびロードバランサーとして業界をリードするNGINXは、APIトラフィックの処理に必要な高度なHTTP処理機能を備えています。そのため、NGINXはAPIゲートウェイの構築に最適なプラットフォームです。このブログ記事では、一般的なAPIゲートウェイのユースケースをいくつか取り上げ、効率性、拡張性、保守性に優れた方法で処理するためのNGINXの設定方法を紹介します。また、本番環境へのデプロイの基盤として使用できる完全な設定についても説明します。
Warehouse APIのご紹介
APIゲートウェイの主な機能は、バックエンドでの実装方法やデプロイ方法にかかわらず、複数のAPIに対して単一かつ一貫したエントリーポイントを提供することです。すべてのAPIがマイクロサービスアプリケーションであるとは限りません。このAPIゲートウェイでは、既存のAPI、モノリシックアプリケーション、マイクロサービスへの移行を部分的に進めているアプリケーションを管理する必要があります。
このブログ記事では、在庫管理用の架空のAPIを「Warehouse API」と呼び、さまざまなユースケースをサンプル構成コードで説明します。Warehouse APIは、JSONリクエストを受け取り、JSONレスポンスを返すRESTful APIです。ただし、APIゲートウェイとしてNGINXを導入する際に、JSONの使用が制約または要件となるわけではありません。NGINXは、API自体で使用されるアーキテクチャスタイルやデータ形式に依存しません。
Warehouse APIは、個別のマイクロサービスの集合として実装され、単一のAPIとして公開されています。在庫リソースと価格リソースはそれぞれ別のサービスとして実装され、異なるバックエンドにデプロイされています。そのため、APIのパス構造は次のようになります。
api
└── warehouse
├── inventory
└── pricingたとえば、現在の倉庫在庫を照会する場合、クライアントアプリケーションはHTTP GETリクエストを/api/warehouse/inventoryに送信します。

NGINX設定の整理
NGINXをAPIゲートウェイとして使用する利点の1つは、既存のHTTPトラフィックに対するリバースプロキシ、ロードバランサー、Webサーバーとして機能しながら、APIゲートウェイの役割も同時に果たせることです。NGINXがすでにアプリケーションデリバリースタックに組み込まれている場合、通常は別のAPIゲートウェイを導入する必要はありません。ただし、APIゲートウェイに求められるデフォルトの動作には、ブラウザベースのトラフィックに求められる動作とは異なる点があります。そのため、APIゲートウェイの設定は、ブラウザベースのトラフィック向けの既存(または将来)の設定から分離します。
この分離を実現するため、多目的なNGINXインスタンスに対応し、CI/CDパイプラインを通じた構成のデプロイを自動化しやすい構成レイアウトを作成します。その結果、/etc/nginx配下のディレクトリ構造は次のようになります。
etc/
└── nginx/
├── api_conf.d/ ………………………………… Subdirectory for per-API configuration
│ └── warehouse_api.conf …… Definition and policy of the Warehouse API
├── api_backends.conf ………………… The backend services (upstreams)
├── api_gateway.conf …………………… Top-level configuration for the API gateway server
├── api_json_errors.conf ………… HTTP error responses in JSON format
├── conf.d/
│ ├── ...
│ └── existing_apps.conf
└── nginx.confすべてのAPIゲートウェイ設定のディレクトリ名とファイル名には、接頭辞として「api_」が付いています。以下で詳しく説明するように、これらのファイルとディレクトリは、それぞれAPIゲートウェイの異なる機能を有効にします。「warehouse_api.conf」ファイルは、Warehouse APIをさまざまな方法で定義する、以下で説明する設定ファイルを一般化して示すための代替名です。
最上位APIゲートウェイの定義
すべてのNGINX設定は、メイン設定ファイルである「nginx.conf」から始まります。APIゲートウェイの設定を読み込むため、includeディレクティブを「http」ブロック内の「nginx.conf」に追加し、ゲートウェイ設定を含むファイル「api_gateway.conf」を参照します(直下の28行目)。デフォルトの「nginx.conf」ファイルでは、includeディレクティブを使用して、「conf.d」サブディレクトリからブラウザーベースのHTTP設定を読み込んでいます(29行目)。このブログ記事では、可読性を高め、設定の一部を自動化できるようにするため、includeディレクティブを多用しています。
コード例を読み込み中…
The api_gateway.confファイルでは、NGINXをAPIゲートウェイとしてクライアントに公開する仮想サーバーを定義します。この構成では、APIゲートウェイが公開するすべてのAPIを単一のエントリポイントであるhttps://api.example.com/(9行目)に公開し、12~17行目の設定に従ってTLSで保護します。この構成はHTTPS専用であり、平文HTTPのリスナーはありません。APIクライアントは正しいエントリポイントを把握し、デフォルトでHTTPS接続を行うことを前提としています。
この設定は静的に維持することを想定しています。個々のAPIとそのバックエンドサービスの詳細は、20行目のincludeディレクティブで参照されるファイルに指定します。23~26行目ではエラー処理を扱っており、詳細については後述の「エラーへの対応」を参照してください。
コード例を読み込み中…
単一サービスとマイクロサービスのAPIバックエンドの比較
一部のAPIは単一のバックエンドに実装される場合もありますが、通常はレジリエンスや負荷分散のために複数のバックエンドを使用します。マイクロサービスAPIでは、サービスごとに個別のバックエンドを定義し、それらが一体となって完全なAPIとして機能します。ここでは、Warehouse APIを2つの独立したサービスとしてデプロイし、それぞれに複数のバックエンドを配置しています。
コード例を読み込み中…
APIゲートウェイで公開されるすべてのAPIについて、バックエンドAPIサービスはすべてapi_backends.confで定義されます。ここでは、APIコードのデプロイ先を示すために、各upstreamブロックで複数のIPアドレスとポートのペアを使用していますが、ホスト名も使用できます。NGINX Plusをご利用の場合は、動的なDNSロードバランシングを利用して、新しいバックエンドを実行時設定に自動的に追加することもできます。
Warehouse APIの定義
Warehouse APIは、次の例に示すように、ネストされた構成内の複数のlocationブロックによって定義されます。外側のlocationブロック(/api/warehouse)はベースパスを識別し、その配下のネストされたlocationで、バックエンドAPIサービスにルーティングされる有効なURIを指定します。外側のブロックを使用すると、API全体に適用される共通ポリシーを定義できます(この例では、6行目のログ構成)。
コード例を読み込み中…
NGINXには、リクエストURIを構成のセクションと照合するための、非常に効率的で柔軟な仕組みがあります。locationディレクティブの順序は重要ではなく、最も具体的に一致するものが選択されます。ここでは、10行目と14行目のネストされたlocationが、外側のlocationブロックよりも具体的な2つのURIを定義しています。各ネストブロック内のproxy_passディレクティブは、リクエストを適切なアップストリームグループにルーティングします。特定のURIに対してより具体的なポリシーを指定する必要がない限り、ポリシー構成は外側のlocationから継承されます。
ネストされたlocationのいずれにも一致しないURIは、外側のlocationで処理されます。外側のlocationには、無効なすべてのURIに対して404 (Not Found)レスポンスを返す包括的なディレクティブ(18行目)が含まれています。
APIの定義範囲:広範か厳密かの選択
APIの定義には、広範な定義と厳密な定義という2つのアプローチがあります。各APIに最適なアプローチは、そのAPIのセキュリティ要件と、無効なURIをバックエンドサービスで処理することが望ましいかどうかによって異なります。
上記の「warehouse_api_simple.conf」では、Warehouse APIに広範なアプローチを採用しています。10行目と14行目でURIプレフィックスを定義し、いずれかのプレフィックスで始まるURIが適切なバックエンドサービスにプロキシされるようにしています。このようにプレフィックスに基づいて広範にロケーションを照合する場合、以下のURIへのAPIリクエストはすべて有効です。
/api/warehouse/inventory
/api/warehouse/inventory/
/api/warehouse/inventory/foo
/api/warehouse/inventoryfoo
/api/warehouse/inventoryfoo/bar/
各リクエストを適切なバックエンドサービスにプロキシすることだけを考慮する場合、広範なアプローチにより、処理を最速化し、構成を最もコンパクトにできます。一方、より厳密なアプローチでは、利用可能な各APIリソースのURIパスを明示的に定義することで、APIゲートウェイがAPIのURI空間全体を把握できます。この厳密なアプローチを採用したWarehouse APIの次のURIルーティング構成では、完全一致(=)と正規表現(~)を組み合わせて、有効なURIを1つずつ定義します。
コード例を読み込み中…
この設定は記述量が多くなりますが、バックエンドサービスに実装されているリソースをより正確に記述できます。正規表現の照合による若干の追加オーバーヘッドが発生する一方で、不正な形式のクライアントリクエストからバックエンドサービスを保護できるという利点があります。この設定を適用すると、NGINXは一部のURIを受け入れ、それ以外を無効として拒否します。
有効なURI 無効なURI/api/warehouse/inventory /api/warehouse/inventory//api/warehouse/inventory/shelf/foo /api/warehouse/inventoryfoo/api/warehouse/inventory/shelf/foo/box/bar /api/warehouse/inventory/shelf/api/warehouse/inventory/shelf/-/box/- /api/warehouse/inventory/shelf/foo/bar/api/warehouse/pricing/baz /api/warehouse/pricing /api/warehouse/pricing/baz/pub
正確なAPI定義を使用すると、既存のAPIドキュメント形式に基づいてAPIゲートウェイを構成できます。OpenAPI Specification(旧称Swagger)からNGINX API定義を自動生成することも可能です。このためのサンプルスクリプトは、このブログ記事のGistで提供しています。
破壊的変更に対応するためのクライアントリクエストの書き換え
APIの進化に伴い、厳密な後方互換性を損なう変更を行い、クライアントの更新が必要になる場合があります。たとえば、APIリソースの名前や場所を変更する場合です。Webブラウザとは異なり、APIゲートウェイは、新しい場所を示すリダイレクト(コード301 (Moved Permanently))をクライアントに送信できません。ただし、APIクライアントの変更が現実的でない場合は、クライアントリクエストをその場で書き換えることができます。
次の例では、上記のwarehouse_api_simple.confとほぼ同じアプローチを使用します。ただし、この構成は、価格設定サービスが在庫サービスの一部として実装されていた旧バージョンのWarehouse APIを置き換えるものです。3行目のrewriteディレクティブは、旧価格設定リソースへのリクエストを新しい価格設定サービスへのリクエストに変換します。
コード例を読み込み中…
エラーへの対応
HTTP APIとブラウザベースのトラフィックの主な違いの1つは、クライアントへのエラーの通知方法です。NGINXをAPIゲートウェイとして導入する場合は、APIクライアントに最適な方法でエラーを返すように設定します。
最上位のAPIゲートウェイ構成には、エラーレスポンスの処理方法を定義するセクションがあります。
コード例を読み込み中…
23行目の「error_page」ディレクティブでは、リクエストがいずれのAPI定義にも一致しない場合、NGINXがデフォルトの404(Not Found)エラーではなく、400(Bad Request)エラーを返すよう指定しています。この動作は任意であり、APIクライアントに対して、APIドキュメントに記載された有効なURIのみへのリクエストを要求します。また、権限のないクライアントが、APIゲートウェイを通じて公開されているAPIのURI構造を特定することを防ぎます。
24行目は、バックエンドサービス自体が生成するエラーを指しています。未処理の例外には、クライアントに送信すべきでないスタックトレースやその他の機密データが含まれる可能性があります。この設定では、標準化されたエラーレスポンスをクライアントに送信することで、保護をさらに強化します。
標準化されたエラーレスポンスの完全なリストは、25行目のincludeディレクティブが参照する別の設定ファイルに定義されています。以下に、そのファイルの冒頭部分を示します。JSON以外のエラー形式を使用する場合は、このファイルを変更し、default_typeの値に合わせてapi_gateway.confの26行目を変更できます。また、各APIのポリシーセクションに個別のincludeディレクティブを指定して、グローバルなレスポンスを上書きする別のエラーレスポンスファイルを参照することもできます。
コード例を読み込み中…
この設定を適用すると、無効なURIに対するクライアントリクエストには、次のレスポンスが返されます。[terminal]$ curl -i https://api.example.com/foo HTTP/1.1 400 Bad Request Server: nginx/1.19.5 Content-Type: application/json Content-Length: 39 Connection: keep-alive {"status":400,"message":"Bad request"}[/terminal]認証の実装
APIを保護するための何らかの認証を設けずに公開することは、一般的ではありません。NGINXでは、APIの保護とAPIクライアントの認証に複数の方法を利用できます。通常のHTTPリクエストにも適用できる方法については、IPアドレスベースのアクセス制御リスト(ACL)、デジタル証明書認証、およびHTTP Basic認証のドキュメントを参照してください。ここでは、API固有の認証方法について説明します。
APIキー認証
APIキーは、クライアントとAPIゲートウェイの間で共有される秘密情報です。APIキーは基本的に、長期的な認証情報としてAPIクライアントに発行される、長く複雑なパスワードです。APIキーは簡単に作成できます。この例のように、乱数をエンコードするだけです。
$ openssl rand -base64 187B5zIqmRGXmrJTFmKa99vcit最上位のAPIゲートウェイ設定ファイルの2行目であるapi_gateway.confでは、api_keys.confというファイルをインクルードします。このファイルには、クライアント名またはその他の説明で識別される各APIクライアントのAPIキーが含まれています。ファイルの内容は次のとおりです。
コード例を読み込み中…
APIキーはmapブロック内で定義されます。mapディレクティブは2つのパラメーターを取ります。第1パラメーターはAPIキーの取得元を指定します。この例では、クライアントリクエストのapikey HTTPヘッダーであり、その値は$http_apikey変数に格納されます。第2パラメーターは新しい変数($api_client_name)を作成し、第1パラメーターがキーと一致する行の第2パラメーターの値をこの変数に設定します。
たとえば、クライアントがAPIキー「7B5zIqmRGXmrJTFmKa99vcit」を提示すると、変数「$api_client_name」は「client_one」に設定されます。この変数は、認証済みクライアントの確認に使用できるほか、より詳細な監査のためにログエントリに含めることもできます。「map」ブロックはシンプルな形式で、既存の認証情報ストアから「api_keys.conf」ファイルを生成する自動化ワークフローに容易に組み込めます。
ここでは、「broad」構成(warehouse_api_simple.conf)を変更し、認証の判定を指定したロケーションに委任するauth_requestディレクティブをポリシーセクションに追加することで、APIキー認証を有効にします。
コード例を読み込み中…
With the auth_requestディレクティブ(7行目)を使用すると、たとえばOAuth 2.0トークンイントロスペクションなどの外部認証サーバーで認証を処理できます。この例では代わりに、APIキーを検証するロジックを最上位のAPIゲートウェイ設定ファイルに追加します。追加するのは、次に示すlocationブロック「/_validate_apikey」です。
コード例を読み込み中…
30行目のinternalディレクティブは、このロケーションに外部クライアントから直接アクセスできず、auth_requestからのみアクセスできることを意味します。クライアントは、apikey HTTPヘッダーでAPIキーを提示する必要があります。このヘッダーが存在しないか空の場合(32行目)、認証が必要であることをクライアントに通知するため、401 (Unauthorized)レスポンスを返します。35行目では、APIキーがmapブロック内のどのキーとも一致しない場合を処理します。この場合、api_keys.confの2行目にあるdefaultパラメーターによって$api_client_nameが空文字列に設定され、認証に失敗したことをクライアントに通知するため、403 (Forbidden)レスポンスを返します。いずれの条件にも該当しない場合、APIキーは有効であり、このロケーションは204 (No Content)レスポンスを返します。
この設定により、Warehouse APIにAPIキー認証が実装されます。
$ curl https://api.example.com/api/warehouse/pricing/item001{"status":401,"message":"Unauthorized"}
$ curl -H "apikey: thisIsInvalid" https://api.example.com/api/warehouse/pricing/item001
{"status":403,"message":"Forbidden"}
$ curl -H "apikey: 7B5zIqmRGXmrJTFmKa99vcit" https://api.example.com/api/warehouse/pricing/item001
{"sku":"item001","price":179.99}JWT認証
API認証でJSON Web Token(JWT)の利用が広がっています。JWTのネイティブサポートはNGINX Plusのみで提供されており、ブログ記事「JWTとNGINX PlusによるAPIクライアントの認証」で説明しているように、JWTを検証できます。実装例については、Part 2の「特定のメソッドへのアクセス制御」をご覧ください。
まとめ
本シリーズの第1回となるこのブログでは、NGINX Open SourceとNGINX PlusをAPIゲートウェイとして導入するための包括的なソリューションについて詳しく説明します。このブログで取り上げるファイル一式は、F5のGitHub Gistリポジトリで確認およびダウンロードできます。
このシリーズの他の記事もご覧ください:
- パート2では、悪意のあるクライアントや不適切に動作するクライアントからバックエンドサービスを保護する、より高度なユースケースについて説明します。
- パート3では、NGINXをgRPCサービスのAPIゲートウェイとしてデプロイする方法を説明します。
NGINX Plusをお試しいただくには、今すぐ30日間の無料トライアルを開始するか、お問い合わせのうえ、ユースケースについてご相談ください。
著者について

関連するブログ記事
チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。
AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。
JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。
F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。
NGINX PlusによるAWS Auto Scalingグループの負荷分散
新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります