👏
Envoyを使ってgRPCをREST APIクライアントから叩かせる
やりたいこと
SPAからgRPCのメソッドを呼び出したいが、SPA側をgRPCに対応させるのは面倒。
よって、gRPCをREST APIに変換するプロキシを立てたい。
grpc-webやgrpc-gatewayでも実現できるらしいが、勉強のためEnvoyを使う
前提
- localhost:8080でリッスンしているgRPCサーバーがある
- gRPCサーバーのメソッドの構成は以下の通り
- パッケージ: articlespb
- サービス: ArticlesService
- メソッド: GetArticles
→articlespb.ArticlesService.GetArticles
という形式で呼び出す
- localhost:51010/articlesでREST APIとして受け入れられるようにする
- 一連の設定をdocker composeで連携させる
手順
1. protoファイルにREST APIのパスを追加
articles.api.proto
+
+import "google/api/annotations.proto";
+import "google/api/httpbody.proto";
+
service ArticlesService {
rpc GetArticles(GetArticlesRequest) returns (GetArticlesResponse) {
+ option (google.api.http) = {
+ get: "/articles"
+ };
};
}
2. buf.yamlに依存関係を追加
buf.yaml
+ deps:
+ - buf.build/googleapis/googleapis
3. protoファイルをビルドしてpbファイルを作成
buf build articlespb/articles.api.proto -o articlespb/articles.api.pb
4. Envoyを立てる
EnvoyのgRPC-JSON transcoderを使って、gRPCサーバへのリクエストをRESTに変換する。
上記のドキュメントを参考にすると、設定ファイルは以下の通り。
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9901
static_resources:
listeners:
- name: listener1
address:
socket_address:
address: 0.0.0.0
port_value: 51051 # REST APIはポート番号51051でリッスンする
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: grpc_json
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: /articlespb.ArticlesService # <パッケージ>/<サービス>の構成にする
route:
cluster: grpc
timeout: 60s
http_filters:
- name: envoy.filters.http.grpc_json_transcoder
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
proto_descriptor: /etc/envoy/protos/articles.api.pb # protobufのディスクリプターのパスを指定
services:
- articlespb.ArticlesService
print_options:
preserve_proto_field_names: false
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: grpc
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
connect_timeout: 60s
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: grpc
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: app # gRPCサーバーのホスト名
port_value: 8080 # gRPCサーバーのポート
5. compose.yamlを用意する
services:
app:
build:
context: .
dockerfile: docker/Dockerfile
container_name: app_container
ports:
- "8080:8080"
envoy:
image: envoyproxy/envoy:dev-d6120f3c769e70c988ddcc5c7e9cbc2737b5f63c
ports:
- "51051:51051"
- "9901:9901"
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml
- ./articles/articlespb/articles.api.pb:/etc/envoy/protos/articles.api.pb # envoy.yamlで指定したパスにarticles.api.pbを配置
6. 実行
docker compose up -d --build
無事にlocalhost:51051/articlesを呼び出せた。
Discussion