envoy + (http, grpc)

Vue.js + grpc(gke)の構成やってみようかーと思ったらブラウザさんはgrpcをそのままだとうまくしゃべれないと知り、折角だし
envoy + grpc(gke)やってみるかということでチュートリアルとかやってみたのはいいものの地味にハマッたりしたので、一通りここに記そうと思う。

なお、envoyが何者なのかとかgrpcが何者なのかとかは説明しません。他に良い記事山盛りあります

## やること
envoy + httpサーバ(go, ローカル)
envoy + grpcサーバ(go, ローカル)
envoy + grpcサーバ(go, GKE)

環境はcentos7 + rootless docker
ネイティブでもほとんど変わりませんが、GKE前提なので全てdockerでうごかしました
composeは面倒くさくなってつくりませんでした。

[こういうの](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcweb)を利用すればenvoyなしでいけるっぽいのでIngress使ってマネージド証明書使えていいことずくめではないかと思いましたが、まあとりあえずenvoy触っておこうか精神で。

## ｻﾝﾌﾟﾙｿｰﾇ
https://github.com/syanhaiD/envoy-grpc-prac

前提としてこのｿｰﾇのプロジェクトルートで色々やります。

## 参考URL
[GCP公式ドキュメント](https://cloud.google.com/solutions/exposing-grpc-services-on-gke-using-envoy-proxy?hl=ja)
[envoy quick start](https://www.envoyproxy.io/docs/envoy/latest/start/start)

GCP公式の方を最新のenvoyでやろうとすると色々とフィールド名が変わってたりしてそのままだと動きません

## 事前準備
考えやすくするため専用にdocker networkを1個作っておきます。
名前はお好みで。

```
docker network create envoy-network
docker network ls
```

あとはgrpc cliやgrpcurlなどのgrpcの疎通確認に使えるツールを1個刺しておきます。
自分はgrpcurlを使ってます。

## ふつうのhttpサーバ
goのビルド環境が詰まったイメージを先にビルドしてからアプリ本体のイメージをビルドしてコンテナ起動します。
これはただの趣味なのでこうしなきゃ動かないってもんじゃありません

app

```
docker build -f build/dockerfiles/go_base . --no-cache=true --rm=true --tag=envoy-grpc-prac/go_base:v1.00
docker build -f build/dockerfiles/http . --no-cache=true --rm=true --tag=envoy-grpc-prac/http:v1.00
docker run --network envoy-network --name go_http --rm -it envoy-grpc-prac/http:v1.00
```

envoy

```
docker pull envoyproxy/envoy-alpine:v1.17.0
docker run --rm -it --network envoy-network -v $(pwd)/settings/envoy-custom-http.yaml:/envoy-custom.yaml -p 25001:25001 -p 25002:25002 envoyproxy/envoy-alpine:v1.17.0 -c /envoy-custom.yaml
```

```
curl -v http://localhost:25002
```

200が返ると思います。

## grpc
まずは動かします。

app

```
docker build -f build/dockerfiles/grpc_base . --no-cache=true --rm=true --tag=envoy-grpc-prac/grpc_base:v1.00
docker build -f build/dockerfiles/egrpc . --no-cache=true --rm=true --tag=envoy-grpc-prac/egrpc:v1.00
docker run --network envoy-network --name go_grpc --rm -it envoy-grpc-prac/egrpc:v1.00
```

envoy

```
docker run --rm -it --network envoy-network -v $(pwd)/settings/envoy-custom-grpc.yaml:/envoy-custom.yaml -p 26001:26001 -p 26002:26002 envoyproxy/envoy-alpine:v1.17.0 -c /envoy-custom.yaml
```

```
grpcurl -insecure -proto build/protofiles/egrpc.proto localhost:26002 egrpc.EGRPC/Test
```

正常レスポンスが返るかと思います。

### 設定の微妙な違い

#### envoy.filters.http.grpc_web
https://github.com/syanhaiD/envoy-grpc-prac/blob/main/settings/envoy-custom-grpc.yaml#L40
こいつがないとgrpc-webと通信できませんでした。まあそりゃそうだろうな感もありますが

#### transport_socket
https://github.com/syanhaiD/envoy-grpc-prac/blob/main/settings/envoy-custom-grpc.yaml#L49
grpcはhttp/2で通信するとのもっぱらの噂ですが、http/2はTLSが必須なのでいわゆる証明書が必要になります。この証明書自体は[envoy公式](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/front_proxy)のyamlからひっぺがしてきたやつです。
実際にブラウザからgrpc-webで通信する際、この証明書をブラウザにインポートしてないと弾き飛ばされるのでgrpcurlだとうまくいってるのにブラウザからうまくいかない場合はこの辺疑いましょう。

#### http2_protocol_options
https://github.com/syanhaiD/envoy-grpc-prac/blob/main/settings/envoy-custom-grpc.yaml#L112
これつけないとhttp/2で通信してくれないっぽいです。

#### cluster healthcheck
https://github.com/syanhaiD/envoy-grpc-prac/blob/main/settings/envoy-custom-grpc.yaml#L123
envoyくん本人が撃ってくれるhealth checkです。ローカルだと本来必要ないですが、アプリ側でログのフィルターとかかけたい場合にローカルで確認できればラクチンだろうという思いから入れてます。

#### global_downstream_max_connections
https://github.com/syanhaiD/envoy-grpc-prac/blob/main/settings/envoy-custom-grpc.yaml#L130
設定してないとenvoy起動時に「おいおい青天井やで？」って警告が出てうざいです。

## GKE用
大きくは変わりませんが

#### livenessprobe
https://github.com/syanhaiD/envoy-grpc-prac/blob/main/settings/envoy-custom-gke.yaml#L43

k8sの機能でenvoy podの健康状態を確認してくれるやつです。
[参考1](https://kubernetes.io/ja/docs/concepts/workloads/pods/pod-lifecycle/#when-should-you-use-a-liveness-probe)
[参考2](https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)

grpc側の健康はenvoyくんに見てもらえればいいやと思いますがenvoyくんの健康状態はこいつで見るのがいいのかなと。
でもこれで異常検知した場合にフックしたりする機構入れられんだろうか。
not_healthcheck_filterじゃなくてステータスコードでフィルタして異常値が出たらstackdriverで拾うノリでいいのかな。

#### cluster address
https://github.com/syanhaiD/envoy-grpc-prac/blob/main/settings/envoy-custom-gke.yaml#L87

[参考](https://kubernetes.io/ja/docs/concepts/services-networking/dns-pod-service/)

serviceを解決できるドメインを指定しましょう。GCP公式ドキュメントはしれっとnamespaceがdefaultになってたりするので気づかないと意外とハマるかも

## おわり
[公式APIリファレンス](https://www.envoyproxy.io/docs/envoy/latest/api-v3/api)がちゃんとあるので結構いけますが、「これはhttpのフィルタだよ！」的な説明しかなくて具体的に何が起きるのかわかんないのが結構あったり、巷に転がってる資料が新旧混在してたりして「チュートリアルまんま」から一歩進もうとすると意外とハマる感ありました。

あと、alpineでprotoをコンパイルするのが地味にひと手間だったりして苦しみました。結局alpine用glibc入れましたが。