envoy の gRPC proxy に関する便利機能
Adventarを支える技術 Advent Calendar 2019 の6日目です。
今日は envoy の gRPC に関する便利機能について紹介しようと思います。
gRPC-Web proxy
4日目の記事でも書きましたが、今回は gRPC-Web の proxy レイヤーとして envoy を利用しています。envoy で gRPC-Web の機能を有効するのは簡単で、HTTP filters に envoy.grpc_web
を書いて、ヘッダの設定をするだけです。
- https://github.com/adventar/adventar/blob/f580de20510f9debe6356a5ad193c4532d8f6a0d/api-server/envoy/envoy-prod.yaml#L44
- https://github.com/adventar/adventar/blob/f580de20510f9debe6356a5ad193c4532d8f6a0d/api-server/envoy/envoy-prod.yaml#L36-L38
これだけで gRPC-Web を受けて、upstream のクラスタに gRPC でリクエストするようになります。超簡単。他に書くことがありません。
gRPC-JSON transcoder
5日目の記事に書きましたが、Adventar では、gRPC と JSON API の両方を envoy によって実現しています。JSON API で受けて、upstream の gRPC に流すのは、envoy の gRPC-JSON transcoder という機能を使っています。
https://www.envoyproxy.io/docs/envoy/latest/api-v2/config/filter/http/transcoder/v2/transcoder.proto
これは grpc-gateway と同じようなものです。Protocol Buffers の定義から生成したスキーマを envoy が読んで、gRPC をバックエンドにした JSON API を提供します。例えば、次のような Protocol Buffers を定義します。
syntax = "proto3"; package adventar.v1; import "google/api/annotations.proto"; message GetCalendarRequest { int64 id = 1; } message Calendar { int64 id = 1; string title = 2; ... } service Adventar { rpc GetCalendar(GetCalendarRequest) returns (Calendar) { option (google.api.http) = { get : "/v1/calendars/" } } }
option
で指定しているのは、grpc-gateway でも使われているもので、 gRPC と JSON API のマッピングを定義するためのものです。
envoy 側の設定は簡単で、envoy.grpc_json_transcoder
を設定するだけです。
ここで指定ているproto.pb
は以下のようなコマンドで出力しています。
$ protoc \ --include_imports \ --include_source_info \ --descriptor_set_out=../api-server/envoy/proto.pb \ adventar/v1/*.proto
これで envoy と gRPC サーバーを起動すると、以下のようにアクセスすることが可能になります。
$ curl http://localhot:8080/v1/calendars?id=1 | jq { "id": 1, "title": "xxx", ... }
クライアント側が gRPC を使えなくても、普通の HTTP で通信できるので、様々な場面で便利な機能だと思います。
auto_mapping
gRPC-JSON transcoder は便利なのですが、Protocol Buffers の定義に
option (google.api.http) = { get : "/v1/calendars/" }
のようなアノテーションを書かないといけないのが面倒です。これを解消するのに、envoy v1.11.0 から auto_mapping という機能が追加されました。
https://github.com/envoyproxy/envoy/pull/6731
これはgoogle.api.http
の定義を書かなくても、POST /<package>.<service>/<method>
という URL でアクセス可能になるという機能です。
設定は簡単でauto_mapping: true
と書くだけです。
これで以下のようにアクセスできます。
$ curl -X POST -d '{"calendar_id":1}' https://localhost:8080/adventar.v1.Adventar/GetCalendar | jq { "id": 1, "title": "xxx", ... }
Adventar ではこの機能を使っているので、google.api.http
の定義は書いていません。
(余談)google.api.http を捨てられていない理由
google.api.http
の定義は書いていません、と言いつつ、実は一番下にgoogle.api.http
の記述があるのに気づいたと思いますが、これは gRPC サーバーの前に立てている AWS Application Load Balancer(ALB) のヘルスチェックが GET でしかできず、auto_mapping は POST にしか対応してないので、しかたなく書いています。
ちなみに ALB は今現在 HTTP/2 の pass through に対応していないので、普通は gRPC の前段に置くことはできなくて色々と面倒なのですが、今回は gRPC-Web と JSON API しか通しておらず、直接 ALB が gRPC をしゃべる必要がないので ALB が利用できて便利です。このあたりのインフラの概要については後日書く予定でいます。
明日は Firebase Authentication について書く予定です。