nginx に実装された gRPC サポートを試してみる


Announcing gRPC Support in NGINX ということで、nginx 1.13.9 で gRPC サポートが入り、HTTP と同じように gRPC ストリームを扱えるようになるようです。めでたい!

grpc_pass ディレクティブが新規に実装され、grpc:// と grpcs:// なバックエンドに対してリバースプロキシを行えるようになるようです。これを使って、

  • TLS 終端を nginx にやってもらったり
  • 複数のバックエンドを置いて柔軟にロードバランスしてもらったり
  • 同一のエンドポイントに複数 gRPC service を設定して、nginx にルーティングしてもらったり

などの設定をすることが可能になるようです。

まだ正式にリリースされているわけではないので、今回は HEAD を持ってきて、リリースに載っている例を試してみます。

下準備

今回は適当に EC2 で Ubuntu 16.04 インスタンスを立ち上げました。必要なパッケージをインストールし、https://hg.nginx.org/ から HEAD の tar.gz を持ってきて、ビルドします。

特に configure にオプションを渡さなければ、すべてのファイルが /usr/local/nginx 以下に配置されます。

$ sudo apt install build-essential libssl-dev libpcre3-dev
$ curl -O https://hg.nginx.org/nginx/archive/tip.tar.gz
$ tar xvf tip.tar.gz
$ cd nginx-c2a0a838c40f
$ ./auto/configure --with-http_ssl_module --with-http_v2_module
$ make
$ sudo make install

# nginx を foreground で起動
sudo ./nginx -g 'daemon off;'

また、実験用の gRPC サーバー・クライアントを準備します。今回は grpc/grpc の examples にある、Python 版を使います。いつもの greeter のやつですね。

$ sudo apt install python-pip3
$ sudo pip3 install grpcio-tools
$ git clone https://github.com/grpc/grpc.git

# 試しに動かしてみる
$ cd ~/grpc/examples/python/helloworld
$ python3 greeter_server.py
$ python3 greeter_client.py
2018/03/18 14:46:12 Greeting: Hello world

普通にリバースプロキシ

http {
    server {
        listen 80 http2;  // http2 必須

        location / {
          grpc_pass grpc://localhost:50051;
        }
    }
}

greeter_client.py を書き換えて、 localhost:80 を向くようにし、実行。

$ python3 greeter_client.py
Greeter client received: Hello, you!

$ tail /usr/local/nginx/logs/access.log
127.0.0.1 - - [18/Mar/2018:16:46:09 +0000] "POST /helloworld.Greeter/SayHello HTTP/2.0" 200 18 "-" "grpc-python/1.10.0 grpc-c/6.0.0 (manylinux; chttp2; glamorous)"

あっさりリバースプロキシできましたね。

普通に TLS 終端してもらう

server {
    listen 443 ssl http2;

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

今回は証明書を用意するのが面倒だったので試さなかったが、これだけで普通に grpcs:// を受けられるようになる、とのこと。外部のクライアントとの間は grpcs で通信したいが、内部の Microservice 同士は平文でいい…… といったケースで便利?

複数 gRPC Service へのルーティング + ロードバランス + REST API との共存例

別の Service ("DaininkiService") を提供する gRPC サーバーを立ててみて、ロードバランスさせてみます。

upstream daininki_service_servers {
    # helloworld.Daininki を提供するサーバーたち
    server localhost:50052;
    server localhost:50053;
}

server {
    listen 80 http2;

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

    location /helloworld.Daininki {
        # HTTP と同じようにロードバランス
        grpc_pass grpc://daininki_service_servers;

        error_page 502 = /error502grpc;
    }

    # バックエンドが unavailable だった場合は gRPC 形式でエラーを返す
    location = /error502grpc {
        internal;
        default_type application/grpc;
        add_header grpc-status 14;
        add_header grpc-message "unavailable";
        return 204;
    }

    location / {
        proxy_pass http://rest_api_server;
    }
}

大人気である DaininkiService は大人気なので、2つのサーバーを構えて nginx にロードバランスしてもらっていることが分かると思います。また、バックエンドのいずれもが応答できなかったときは、nginx 自身に application/grpc 形式の応答を返してもらうことも出来るようです。

アナウンスのページには REST API のような non-gRPC サービスも同じ endpoint に混ぜることができる、と書いてありますが、この例のように location / を記述しても上手く動作しませんでした……。上手く使えた場合、REST API を提供していたアプリケーションを拡張して gRPC も話せるようにするようなケースでは便利ではないでしょうか。

今後

gRPC 関連機能の実装はメーリングリストでのフィードバックを受け、今後の nginx のリリースに含まれるようです。期待ですね!