NGINX(エンジンエックス)|日本公式サイト

NGINXはF5ファミリーの一員となりました。新体制の詳細はこちらを御覧ください。

NGINX 1.13.10でのgRPCサポートの紹介

NGINX Open Source 1.13.10でリリースされたgRPCトラフィックの最初のネイティブサポートについて、ここでご紹介します。

NGINX Plusリリース15には、gRPCサポートと、NGINX 1.13.9で導入されたHTTP/2サーバプッシュのサポートが含まれています。

NGINXはgRPC TCP接続をプロキシすることができます。この新しい機能を使用して、gRPCメソッドのコールを終了、検査、およびルーティングすることができます。これらの機能は、以下の目的で使用できます。

  • gRPCサービスを公開し、公開したサービスに対してNGINXを使用してHTTP/2 TLS暗号化、レート制限、IPアドレスベースのアクセス制御リスト、およびロギングなどを行うことが可能です。暗号化を行わないHTTP/2(h2cクリアテキスト)や、TLSによる暗号化と認証を使用してサービスの公開をすることもできます。
  • 単一のエンドポイントを介して複数のgRPCサービスを公開することができ、NGINXを使用し各内部サービス宛のトラフィックの検査やルーティングが可能です。 同じエンドポイントで、WebサイトやRESTベースのAPIなど、他のHTTPSおよびHTTP/2サービス公開することも可能です。
  • ラウンドロビン、Least Connection、またはその他の方法を使用してgRPCサービスのクラスターに対して負荷分散し、クラスター全体にトラフィックを分散します。このため、追加のキャパシティが必要な場合は、gRPCベースのサービスを追加しスケーリングすることが可能です。

詳細については、オンデマンドのウェビナー、NGINX:HTTP / 2サーバプッシュとgRPC(英語版)もご覧ください。

gRPCとは何ですか?

gRPCとは、リモートプロシージャコール用のプロトコルであり、クライアントとサーバアプリケーション間の通信に使用されます。コンパクト(リソース効率が良い)で複数の言語に移植できるように設計されており、要求応答とストリーミング対話の両方をサポートしています。このプロトコルは、その広範な言語サポートとシンプルなユーザー向けの設計により、サービスメッシュ実装を含め、人気が高まっています。

A simple gRPC-based application
gRPCベースのシンプルなアプリケーション例

gRPCは、クリアテキストまたはTLS暗号化のいずれかで、HTTP/2を介して転送されます。 gRPCの呼び出しは、効率的にエンコードされた本文を含むHTTP POSTリクエストとして実装されます(プロトコルバッファーは標準のエンコードです)。 gRPCの応答は、同様にエンコードされた本文を使用し、HTTPトレーラーを使用して、応答の最後にステータスコードを送信します。

設計上、gRPCプロトコルはHTTP/1.xでは転送できません。 gRPCプロトコルは、HTTP/2接続の多重化およびストリーミング機能を利用するために、HTTP/2での利用が必須です。

NGINXを使用したgRPCサービスの管理

下記では、gRPC Hello Worldクイックスタートチュートリアルの複数例を用いて、シンプルなクライアントおよびサーバプリケーションを作成する例を示しています。NGINXにおける設定の詳細を共有します。ヒントとして参考にしていただければ幸いです。

シンプルなgRPCサービスの公開

まず、クライアントアプリケーションとサーバプリケーションの間にNGINXを配置します。NGINXは、サーバプリケーションに安定した信頼性の高いゲートウェイを提供します。

NGINX proxying gRPC traffic
gRPCトラフィックのNGINXプロキシ

まず、gRPCを更新しNGINXをデプロイします。 ソースからNGINXをビルドする場合は、http_sslおよびhttp_v2モジュールを含めることを忘れないでください。

$ auto/configure --with-http_ssl_module --with-http_v2_module

NGINXは、gRPCトラフィックをHTTPサーバを使用して待ち受け、grpc_passディレクティブを使用してトラフィックをプロキシします。 続くプロキシ設定をNGINXで作成し、ポート80で暗号化されていないgRPCトラフィックをリッスンし、ポート50051でサーバにリクエストを転送します。

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent"';

    server {
        listen 80 http2;

        access_log logs/access.log main;

        location / {
            # Replace localhost:50051 with the address and port of your gRPC server
            # The 'grpc://' prefix is optional; unencrypted gRPC is the default
            grpc_pass grpc://localhost:50051;
        }
    }
}

grpc_passディレクティブのアドレスが正しいことを確認してください。 クライアントを再コンパイルして、NGINXのIPアドレスと待ち受けるポートを指定します。

変更したクライアントを実行すると、以前と同じ応答が表示されますが、トランザクションはNGINXによって終了および転送されます。 設定したアクセスログで確認できます。

$ tail logs/access.log
192.168.20.11 - - [01/Mar/2018:13:35:02 +0000] "POST /helloworld.Greeter/SayHello HTTP/2.0" 200 18 "-" "grpc-go/1.11.0-dev"
192.168.20.11 - - [01/Mar/2018:13:35:02 +0000] "POST /helloworld.Greeter/SayHelloAgain HTTP/2.0" 200 24 "-" "grpc-go/1.11.0-dev"

注:NGINXは、クリアテキスト(非TLS)ポート上でHTTP/1.xとHTTP/2を同時にサポートしません。 使用するプロトコルのバージョンに関する事前確認が必要です。 両方のプロトコルバージョンをクリアテキストで処理する場合は、それぞれにリッスンポートを作成します。

TLS暗号化を使用したgRPCサービスの公開

Hello Worldクイックスタートの例では、暗号化されていないHTTP/2(クリアテキスト)を使用して通信します。テスト用のためかなりシンプルデプロイですが、実運用の環境に必要な暗号化は提供されません。NGINXを用いて、この暗号化レイヤーを追加して見ましょう。

NGINX SSL-terminating gRPC traffic
gRPC トラフィックのNGINX SSL接続終端処理

自己署名証明書のセットを作成し、NGINXサーバの設定を次のように変更します。

server {
    listen 1443 ssl http2;

    ssl_certificate     ssl/cert.pem;
    ssl_certificate_key ssl/key.pem;

    #...
}

TLSを使用するためgRPCクライアントは宛先ポート1443に接続し、証明書チェックを無効にします。(この設定は自己署名証明書または信頼されていない証明書を使用する場合に必要となります)Goを使用している場合は、crypto/tlsgoogle.golang.org/grpc/credentialsをインポートリストに追加し、grpc.Dial()呼び出しを次のように変更する必要があります。

creds := credentials.NewTLS( &tls.Config{ InsecureSkipVerify: true } )

// remember to update address to use the new NGINX listen port
conn, err := grpc.Dial( address, grpc.WithTransportCredentials( creds ) )

NGINXでgRPCトラフィックをセキュアに処理するために必要な設定はこれだけです。本番の実行環境では、自己署名証明書を信頼できる認証局(CA)が発行した証明書に置き換える必要があります。 次に、そのCAを信頼するようにクライアントを設定する必要があります。

暗号化されたgRPCサービスへのプロキシ

内部にプロキシするgRPCトラフィックを暗号化する必要が出てくる場合があります。 その際には、まず非暗号化(grpc)接続ではなくTLS暗号化(grpcs)接続をリッスンするようにサーバアプリケーションを変更する必要があります。

cer, err := tls.LoadX509KeyPair( "cert.pem", "key.pem" )
config := &tls.Config{ Certificates: []tls.Certificate{cer} }
lis, err := tls.Listen( "tcp", port, config )

そして、NGINXの設定で、gRPCトラフィックをアップストリームのサーバにプロキシするために使用するプロトコルを変更します。

# Use grpcs for TLS-encrypted gRPC traffic
grpc_pass grpcs://localhost:50051;

gRPCトラフィックのルーティング

複数のgRPCサービスがあり、それぞれが異なるサーバアプリケーションによって実装されている場合は、何をすべきでしょうか? これらすべてのサービスを単一のTLS暗号化エンドポイントを介して公開できたら素晴らしいと思いませんか?

 NGINX routing and SSL-terminating gRPC traffic
NGINXを用いたgRPC トラフィックのルーティングおよびSSL終端処理

NGINXでは、サービスとメソッドを識別し、locationディレクティブを使用してトラフィックをルーティングすることができます。プロトコルの仕様にあるパッケージ、サービス、およびメソッド名になどを参考に、すでに各gRPCリクエストのURLを導き出すことができるかもしれません。SayHello RPCメソッドの例を見てみましょう

package helloworld;

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

SayHello RPCメソッドを呼び出すと、次のログエントリに示すように、/helloworld.Greeter/SayHelloに対するPOSTリクエストが発行されます。

192.168.20.11 - - [01/Mar/2018:13:35:02 +0000] "POST /helloworld.Greeter/SayHello HTTP/2.0" 200 18 "-" "grpc-go/1.11.0-dev"

NGINXでのトラフィックのルーティングは非常に簡単です。

location /helloworld.Greeter {
    grpc_pass grpc://192.168.20.11:50051;
}

location /helloworld.Dispatcher {
    grpc_pass grpc://192.168.20.11:50052;
}

location / {
    root html;
    index index.html index.htm;
}

こちらについて自分で試すことが可能です。 ここでは、サンプルのHello Worldパッケージ(helloworld.proto内)を拡張してDispatcherという名前の新しいサービスを追加し、Dispatcherメソッドを実装する新しいサーバアプリケーションを作成しました。 クライアントは単一のHTTP/2接続を使用して、GreeterサービスとDispatcherサービスの両方にRPC呼び出しを発行します。 NGINXは呼び出しを分離し、それぞれを適切なgRPCサーバにルーティングします。

すべてをキャッチする「location /」の箇所に注意してください。 このブロックは、既知のgRPC呼び出しと一致しないリクエストを処理します。 このように、locationブロックを使用して、同じTLSの暗号化を行うエンドポイントからWebコンテンツや、その他のgRPC以外のサービスを提供することが可能です。

gRPC呼び出しの負荷分散

キャパシティを増やして高可用性を実現するためには、gRPCサービスをどのようにスケーリングするのかを見ていく必要があります。NGINXのアップストリームグループでこの要望を実現することが可能です:

upstream grpcservers {
    server 192.168.20.11:50051;
    server 192.168.20.12:50051;
}

server {
    listen 1443 ssl http2;

    ssl_certificate     ssl/certificate.pem;
    ssl_certificate_key ssl/key.pem;

    location /helloworld.Greeter {
        grpc_pass grpc://grpcservers;
        error_page 502 = /error502grpc;
    }

    location = /error502grpc {
        internal;
        default_type application/grpc;
        add_header grpc-status 14;
        add_header grpc-message "unavailable";
        return 204;
    }
}

もちろん、アップストリームでTLSをリッスンしている場合は、grpc_passgrpcs://upstreamsを使用できます。

NGINXは、さまざまな負荷分散アルゴリズムを使用して、gRPCの呼び出しをアップストリームのgRPCサーバ全体に分散することができます。 NGINXのヘルスチェック機能では、応答していないサーバや、エラーがでているかどうかを検出し、その問題のあるサーバをローテーションから外します。この設定サンプルでは、使用可能なサーバがない場合、「/error502grpc」のロケーションを用いて、gRPC準拠のエラーメッセージを返すよう動作します。

本内容に、コメントやフィードバックなどあれば、コミュニティメーリングリストを通じてぜひご意見お聞かせください。

確認されたバグのレポートは、Tracバグトラッカーに送信してください。

下記、関連ウェビナーもご参照ください。

「NGINX HTTP/2 Server Push and gRPC(英語版)」

関連資料