これは、NGINX Open Source と NGINX Plus を API ゲートウェイとして導入する方法に関するシリーズの 3 番目のブログ投稿です。
注記: 特に記載がない限り、この記事のすべての情報は、NGINX Plus と NGINX Open Source の両方に適用されます。 読みやすくするために、ブログの残りの部分では、両方のバージョンに適用される説明では単に「NGINX」と表記します。
マイクロサービス アプリケーション アーキテクチャの概念と利点は近年十分に文書化されており、特にNGINX ブログでよく文書化されています。 マイクロサービス アプリケーションの中心となるのは HTTP API であり、このシリーズの最初の 2 つのブログ投稿では、仮想の REST API を使用して、NGINX がこのスタイルのアプリケーションにどのように対応するかを説明します。
最新のアプリケーションでは JSON メッセージ形式を採用した REST API が人気ですが、すべてのシナリオやすべての組織にとって理想的なアプローチというわけではありません。 最も一般的な課題は次のとおりです。
近年、gRPC は分散アプリケーション、特にマイクロサービス アプリケーションを構築するための代替アプローチとして登場しました。 gRPC はもともと Google で開発され、2015 年にオープンソース化され、現在は Cloud Native Computing Foundation のプロジェクトとなっています。 重要なのは、gRPC が HTTP/2 をトランスポート メカニズムとして使用し、そのバイナリ データ形式と多重化されたストリーミング機能を活用していることです。
gRPC の主な利点は次のとおりです。
このシリーズの最初の 2 つの投稿では、単一のエントリ ポイント (例: https://api.example.com ) を通じて複数の API を配信する方法について説明しました。 gRPC トラフィックのデフォルトの動作と特性により、NGINX が gRPC ゲートウェイとして展開される場合も同じアプローチを取ることになります。 同じホスト名とポートで HTTP トラフィックと gRPC トラフィックの両方を共有することは可能ですが、それらを分離することが望ましい理由はいくつかあります。
この分離を実現するために、gRPC ゲートウェイの構成を、 /etc/nginx /conf.d ディレクトリにあるメインの gRPC 構成ファイルgrpc_gateway.conf内の独自のserver{}
ブロックに配置します。
まず、gRPC トラフィックのアクセス ログのエントリの形式を定義します (1 ~ 4 行目)。 この例では、JSON 形式を使用して、各リクエストから最も関連性の高いデータを取得します。 たとえば、すべての gRPC リクエストはPOST を
使用するため、 HTTP メソッドは含まれていないことに注意してください。 HTTP ステータス コードとともに gRPC ステータス コードもログに記録します。 ただし、gRPC ステータス コードはさまざまな方法で生成できます。 通常の状況では、 grpc-status は
バックエンドから HTTP/2 トレーラーとして返されますが、エラー状況によっては、バックエンドまたは NGINX 自体によって HTTP/2 ヘッダーとして返されることがあります。 アクセス ログを簡素化するために、マップ
ブロック (6 ~ 9 行目) を使用して新しい変数$grpc_status
を評価し、発生元から gRPC ステータスを取得します。
この設定には 2 つのlisten
ディレクティブ (行 12 と 13) が含まれているため、プレーンテキスト (ポート 50051) と TLS で保護された (ポート 443) トラフィックの両方をテストできます。 http2
パラメータは、NGINX が HTTP/2 接続を受け入れるように設定します。これはssl
パラメータとは独立していることに注意してください。 また、ポート 50051 は gRPC の従来のプレーンテキスト ポートですが、本番環境での使用には適していないことにも注意してください。
TLS 構成は、TLS 1.2 を最も弱い許容プロトコルとして指定するssl_protocols
ディレクティブ (行 23) を除いて従来どおりです。 HTTP/2 仕様では、TLS 1.2 (またはそれ以降) の使用が義務付けられており、これにより、すべてのクライアントが TLS のServer Name Indication (SNI) 拡張をサポートすることが保証されます。 これは、gRPC ゲートウェイが他のserver{}
ブロックで定義された仮想サーバーとポート 443 を共有できることを意味します。
NGINX の gRPC 機能を調べるために、複数の gRPC サービスがデプロイされた、gRPC ゲートウェイの主要コンポーネントを表すシンプルなテスト環境を使用しています。 公式 gRPC ガイドの 2 つのサンプル アプリケーション、 helloworld (Go で記述) とRouteGuide (Python で記述) を使用します。 RouteGuide アプリケーションは、次の 4 つの gRPC サービス メソッドがそれぞれ含まれているため、特に便利です。
両方の gRPC サービスは、NGINX ホストに Docker コンテナとしてインストールされます。 テスト環境の構築に関する詳細な手順については、付録を参照してください。
RouteGuide および helloworld サービスと、利用可能なコンテナのアドレスを認識するように NGINX を構成します。
各 gRPC サービスにアップストリーム
ブロック (40 ~ 45 行目と 47 ~ 51 行目) を追加し、gRPC サーバー コードを実行している個々のコンテナーのアドレスを入力します。
NGINX は gRPC の従来のプレーンテキスト ポート (50051) をリッスンし、ルーティング情報を構成に追加して、クライアント要求が正しいバックエンド サービスに到達するようにします。 しかし、まず、gRPC メソッド呼び出しが HTTP/2 リクエストとしてどのように表現されるかを理解する必要があります。 次の図は、RouteGuide サービスのroute_guide.protoファイルの短縮バージョンを示しており、NGINX から見たパッケージ、サービス、および RPC メソッドが URI を形成する方法を示しています。
したがって、HTTP/2 リクエストで運ばれる情報は、パッケージ名 (ここではrouteguide
またはhelloworld
) を単純に一致させることでルーティングの目的に使用できます。
最初のロケーション
ブロック (行 26) は、修飾子なしで、 /routeguide. が
そのパッケージの対応する.protoファイルで定義されているすべてのサービスと RPC メソッドと一致するようにプレフィックス マッチを定義します。 したがって、 grpc_pass
ディレクティブ (行 27) は、RouteGuide クライアントからのすべての要求を上流グループrouteguide_serviceに渡します。 この構成 (および 29 行目と 30 行目の helloworld サービス用の並列構成) は、gRPC パッケージとそのバックエンド サービス間のシンプルなマッピングを提供します。
grpc_pass
ディレクティブへの引数はgrpc://
スキームで始まり、プレーンテキストの gRPC 接続を使用してリクエストをプロキシすることに注意してください。 バックエンドが TLS 用に構成されている場合は、 grpcs://
スキームを使用して、エンドツーエンドの暗号化で gRPC 接続を保護できます。
RouteGuide クライアントを実行した後、ログ ファイルのエントリを確認してルーティングの動作を確認できます。 ここでは、RouteChat RPC メソッドがポート 10002 で実行されているコンテナーにルーティングされたことがわかります。
$ python route_guide_client.py ... $ tail -1 /var/log/nginx/grpc_log.json | jq { "timestamp": 「2021-01-20T12:17:56+01:00」、「クライアント」: "127.0.0.1", "uri": "/routeguide.RouteGuide/RouteChat", "http-status": 200、「grpc-ステータス」: 0、「上流」: "127.0.0.1:10002", "rxバイト": 161、「送信バイト」: 212 }
上記のように、複数の gRPC サービスを異なるバックエンドにルーティングするのはシンプルで効率的であり、必要な構成行はごくわずかです。 ただし、実稼働環境でのルーティング要件はより複雑になる可能性があり、URI 内の他の要素 (gRPC サービスまたは個々の RPC メソッド) に基づいたルーティングが必要になる場合があります。
次の構成スニペットは、双方向ストリーミング RPC メソッドRouteChat
が 1 つのバックエンドにルーティングされ、他のすべてのRouteGuide
メソッドが別のバックエンドにルーティングされるように、前の例を拡張します。
2 番目の場所
ディレクティブ (7 行目) では、 =
(等号) 修飾子を使用して、これがRouteChat
RPC メソッドの URI と完全に一致することを示します。 完全一致はプレフィックス一致の前に処理されます。つまり、 RouteChat
URI では他の場所
ブロックは考慮されません。
gRPC エラーは、従来の HTTP トラフィックのエラーとは多少異なります。 クライアントはエラー状態が gRPC 応答として表現されることを期待しているため、NGINX が gRPC ゲートウェイとして構成されている場合、NGINX エラー ページのデフォルト セット (HTML 形式) は不適切になります。 これを解決するには、gRPC クライアントにカスタム エラー応答のセットを指定します。
gRPC エラー応答の完全なセットは比較的長く、大部分が静的な構成であるため、それらを別のファイルerrors.grpc_confに保存し、 include
ディレクティブ (行 34) を使用して参照します。 HTTP/REST クライアントとは異なり、gRPC クライアント アプリケーションは、広範囲の HTTP ステータス コードを処理することは想定されていません。 gRPC ドキュメントでは、クライアントが常に適切な応答を受信できるように、NGINX などの中間プロキシが HTTP エラー コードを gRPC ステータス コードに変換する方法が指定されています。 このマッピングを実行するには、 error_page
ディレクティブを使用します。
各標準 HTTP ステータス コードは@
プレフィックスを使用して名前付きの場所に渡されるため、gRPC 準拠の応答を生成できます。 たとえば、HTTP404
応答は内部的に@grpc_unimplemented の
場所にリダイレクトされます。この場所はファイルの後半で定義されています。
@grpc_unimplemented
という名前の場所は、内部 NGINX 処理でのみ使用できます。ルーティング可能な URI が存在しないため、クライアントは直接要求できません。 この場所では、必須のgRPCヘッダーを設定し、レスポンス本文なしでHTTPステータスコードを使用して送信することでgRPCレスポンスを構築します。204
(コンテンツ
なし
)。
curl(1)
コマンドを使用すると、存在しない gRPC メソッドを要求する動作不良の gRPC クライアントを模倣できます。 ただし、プロトコル バッファはバイナリ データ形式を使用するため、 curl は
通常、gRPC テスト クライアントとしては適していないことに注意してください。 コマンドラインで gRPC をテストするには、 grpc_cli
の使用を検討してください。
$ curl -i --http2 -H "Content-Type: application/grpc" -H "TE: トレーラー" -X POST https://grpc.example.com/does.Not/Exist HTTP/2 204 サーバー: nginx/1.19.5 日付: 2021年1月20日水曜日 15:03:41 GMT grpc-status: 12 grpc-message: 未実装
上記のgrpc_errors.confファイルには、タイムアウトやクライアント証明書エラーなど、NGINX が生成する可能性のあるその他のエラー応答の HTTP から gRPC へのステータス コード マッピングも含まれています。
gRPCメタデータを使用すると、クライアントは、そのデータがプロトコル バッファー仕様 ( .protoファイル) の一部である必要なく、RPC メソッド呼び出しと一緒に追加情報を送信できます。 メタデータはキーと値のペアの単純なリストであり、各ペアは個別の HTTP/2 ヘッダーとして送信されます。 したがって、メタデータは NGINX から簡単にアクセスできます。
メタデータの多くの使用例のうち、gRPC API ゲートウェイではクライアント認証が最も一般的です。 次の構成スニペットは、NGINX Plus が gRPC メタデータを使用してJWT 認証を実行する方法を示しています (JWT 認証は NGINX Plus 専用です)。 この例では、JWT はauth-token
メタデータで送信されます。
すべての HTTP リクエスト ヘッダーは、 $http_ header
という変数として NGINX Plus で使用できます。 ヘッダー名のハイフン ( -
) は変数名ではアンダースコア ( _
) に変換されるため、JWT は$http_auth_token
(行 2) として使用できるようになります。
既存の HTTP/REST API などで認証に API キーが使用される場合、これらも gRPC メタデータで伝送され、NGINX によって検証されます。APIキー認証の構成 <.htmla> は、このブログ シリーズのパート 1 で提供されています。
複数のバックエンドにトラフィックを負荷分散する場合、ダウンしているバックエンドや利用できないバックエンドにリクエストを送信しないようにすることが重要です。 NGINX Plus を使用すると、アクティブ ヘルス チェックを使用して、帯域外リクエストをバックエンドにプロアクティブに送信し、期待どおりにヘルス チェックに応答しない場合は、それらをロード バランシング ローテーションから削除できます。 このようにして、クライアントのリクエストがサービス停止中のバックエンドに届かないようにします。
次の構成スニペットは、RouteGuide および helloworld gRPC サービスのアクティブなヘルス チェックを有効にします。関連する構成を強調するために、前のセクションで使用したgrpc_gateway.confファイルに含まれているいくつかのディレクティブを省略しています。
各ルートに対してhealth_check
ディレクティブも指定します (17 行目と 21 行目)。 type=grpc
引数で指定されているように、NGINX Plus はgRPC ヘルスチェック プロトコルを使用して、アップストリーム グループ内のすべてのサーバーにヘルスチェックを送信します。 ただし、私たちのシンプルな gRPC サービスは gRPC ヘルスチェック プロトコルを実装していないため、「未実装」を意味するステータス コード ( grpc_status=12
) で応答することが予想されます。 そうなれば、アクティブな gRPC サービスと通信していることが分かります。
この構成を採用すると、gRPC クライアントで遅延やタイムアウトが発生することなく、バックエンド コンテナを停止できます。 アクティブ ヘルス チェックは NGINX Plus 専用です。gRPCヘルス チェックの詳細については、当社のブログをご覧ください。
grpc_gateway.confのサンプル設定は、TLS 用に若干の変更を加えることで、実稼働環境での使用に適しています。 パッケージ、サービス、または RPC メソッドに基づいて gRPC リクエストをルーティングする機能は、既存の NGINX 機能を、HTTP/REST API の場合、または通常の Web トラフィックの場合とまったく同じ方法で gRPC トラフィックに適用できることを意味します。 いずれの場合も、レート制限や帯域幅制御などの追加構成によって、関連するロケーション
ブロックを拡張できます。
NGINX Open Source と NGINX Plus を API ゲートウェイとして導入する方法に関するシリーズの 3 番目で最後のブログ投稿では、マイクロサービス アプリケーションを構築するためのクラウド ネイティブ テクノロジーとしての gRPC に焦点を当てました。 NGINX が HTTP/REST API と同様に gRPC アプリケーションを効果的に配信する方法と、両方のスタイルの API を多目的 API ゲートウェイとしての NGINX を通じて公開する方法を示しました。
このブログ記事で使用されているテスト環境の設定手順は、以下の付録に記載されています。また、すべてのファイルはGitHub Gist リポジトリからダウンロードできます。
このシリーズの他のブログ投稿もご覧ください:
NGINX Plus を API ゲートウェイとしてお試しいただくには、今すぐ30 日間の無料トライアルを開始するか、弊社にお問い合わせの上、使用事例についてご相談ください。 試用期間中は、 GitHub Gist リポジトリから構成ファイルの完全なセットを使用してください。
次の手順では、テスト環境を仮想マシンにインストールして、分離して繰り返し実行できるようにします。 ただし、物理的な「ベアメタル」サーバーにインストールできない理由はありません。
テスト環境を簡素化するために、Docker コンテナを使用して gRPC サービスを実行します。 つまり、テスト環境では複数のホストは必要ありませんが、実稼働環境と同様に、NGINX でネットワーク呼び出しによるプロキシ接続を行うことができます。
Docker を使用すると、コードを変更することなく、異なるポートで各 gRPC サービスの複数のインスタンスを実行することもできます。 各 gRPC サービスは、仮想マシン上の一意の localhost ポートにマップされているコンテナ内のポート 50051 をリッスンします。 これにより、ポート 50051 が解放され、NGINX がそれをリッスン ポートとして使用できるようになります。 したがって、テスト クライアントが事前設定されたポート 50051 を使用して接続すると、NGINX に到達します。
NGINX Plus 管理者ガイドの指示に従って、 NGINX Open SourceまたはNGINX Plus をインストールします。
GitHub Gist リポジトリから次のファイルを/etc/nginx/conf.dにコピーします。
注記: TLS を使用しない場合は、 grpc_gateway.confのssl_*
ディレクティブをコメント アウトします。
NGINX Open Source または NGINX Plus を起動します。
$ sudo nginx
Debian および Ubuntu の場合は、次を実行します。
$ sudo apt-get をインストール docker.io
CentOS、RHEL、Oracle Linux の場合は、次を実行します。
$ sudo yum インストール docker
次の Dockerfile から RouteGuide コンテナの Docker イメージをビルドします。
ビルドの前に Dockerfile をローカル サブディレクトリにコピーするか、 docker
build
コマンドの引数として Dockerfile の Gist の URL を指定します。
$ sudo docker build -t ルートガイド https://gist.githubusercontent.com/nginx-gists/87ed942d4ee9f7e7ebb2ccf757ed90be/raw/ce090f92f3bbcb5a94bbf8ded4d597cd47b43cbe/routeguide.Dockerfile
イメージのダウンロードとビルドには数分かかる場合があります。 「正常に
ビルドされました」
というメッセージと 16 進文字列 (イメージ ID) が表示され、ビルドが完了したことが示されます。
docker
images を
実行してイメージがビルドされたことを確認します。
$ sudo docker imagesリポジトリ タグ イメージ ID 作成サイズ routeguide 最新 63058a1cf8ca 1 分前 1.31 GB python 最新 825141134528 9 日前 923 MB
RouteGuide コンテナを起動します。
$ sudo docker run --name rg1 -p 10001:50051 -d ルートガイド$ sudo docker run --name rg2 -p 10002:50051 -d ルートガイド$ sudo docker run --name rg3 -p 10003:50051 -d ルートガイド
各コマンドが成功すると、実行中のコンテナを表す長い 16 進文字列が表示されます。
docker
ps を
実行して、3 つのコンテナすべてが起動していることを確認します。 (読みやすくするために、サンプル出力は複数の行に分割されています。)
$ sudo docker psコンテナ ID イメージ コマンド ステータス ... d0cdaaeddf0f routeguide "python route_g..." 2 秒上昇 ... c04996ca3469 routeguide "python route_g..." 9秒アップ…
2170ddb62898 ルートガイド "python route_g..." 1分上がって…… ポート名... 0.0.0.0:10003->50051/tcp rg3 ... 0.0.0.0:10002->50051/tcp rg2 ... 0.0.0.0:10001->50051/tcp rg1
出力のPORTS
列には、各コンテナーがコンテナー内のポート 50051 に異なるローカル ポートをマップした方法が表示されます。
次の Dockerfile から helloworld コンテナの Docker イメージをビルドします。
ビルドの前に Dockerfile をローカル サブディレクトリにコピーするか、 docker
build
コマンドの引数として Dockerfile の Gist の URL を指定します。
$ sudo docker build -t helloworld https://gist.githubusercontent.com/nginx-gists/87ed942d4ee9f7e7ebb2ccf757ed90be/raw/ce090f92f3bbcb5a94bbf8ded4d597cd47b43cbe/helloworld.Dockerfile
イメージのダウンロードとビルドには数分かかる場合があります。 「正常に
ビルドされました」
というメッセージと 16 進文字列 (イメージ ID) が表示され、ビルドが完了したことが示されます。
docker
images を
実行してイメージがビルドされたことを確認します。
$ sudo docker imagesリポジトリ タグ イメージ ID 作成サイズ helloworld 最新 e5832dc0884a 10 秒前 926MB routeguide 最新 170761fa3f03 4 分前 1.31GB python 最新 825141134528 9 日前 923MB golang 最新 d0e7a411e3da 3 週間前 794MB
helloworld コンテナを起動します。
$ sudo docker run --name hw1 -p 20001:50051 -d helloworld $ sudo docker run --name hw2 -p 20002:50051 -d helloworld
各コマンドが成功すると、実行中のコンテナを表す長い 16 進文字列が表示されます。
docker
ps
を実行して、2 つの helloworld コンテナが起動していることを確認します。
$ sudo docker psコンテナ ID イメージ コマンド ステータス ... e0d204ae860a helloworld "go run greeting..." 5秒上がりました…
66f21d89be78 helloworld 「greeter を実行してください...」 9 秒上昇 ... d0cdaaeddf0f routeguide "python route_g..." 4 分経過... c04996ca3469 routeguide "python route_g..." 4分上がりました…
2170ddb62898 ルートガイド "python route_g..." 5分ほど上がって…… ポート名... 0.0.0.0:20002->50051/tcp hw2 ... 0.0.0.0:20001->50051/tcp hw1 ... 0.0.0.0:10003->50051/tcp rg3 ... 0.0.0.0:10002->50051/tcp rg2 ... 0.0.0.0:10001->50051/tcp rg1
プログラミング言語の前提条件をインストールします。その一部は、テスト環境に既にインストールされている可能性があります。
Ubuntu および Debian の場合は、次を実行します。
$ sudo apt-get インストール golang-go python3 python-pip git
CentOS、RHEL、Oracle Linux の場合は、次を実行します。
$ sudo yum install golang python python-pip git
python-pip
では EPEL リポジトリを有効にする必要があることに注意してください (必要に応じて最初にsudo
yum
install
epel-release
を実行してください)。
helloworld アプリケーションをダウンロードします。
$ google.golang.org/grpc にアクセスします
RouteGuide アプリケーションをダウンロードします:
$ git clone -b v1.14.1 https://github.com/grpc/grpc $ pip install grpcio-tools
helloworld クライアントを実行します。
$ go を実行して、 go/src/google.golang.org/grpc/examples/helloworld/greeter_client/main.go を実行します。
RouteGuide クライアントを実行します。
$ cd grpc/examples/python/route_guide $ python route_guide_client.py
NGINX ログをチェックして、テスト環境が動作していることを確認します。
$テール /var/log/nginx/grpc_log.json
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"