ChatWork Creator's Note

ビジネスチャット「チャットワーク」のエンジニアとデザイナーのブログです。

nginx-ingress on AWS で gRPC をロードバランスさせる検証

SRE部のcw-sakamotoです。

この記事では、nginx-ingressをk8s on AWSで動かして、さらにbackendにはgRPC serverを立てて、ロードバランスできるかどうかの検証を行いたいと思います。

アジェンダ

背景

環境

環境は下記の通りです。

  • AWS
  • kubernetes 1.8.4
  • kubernetes構築ツールはkube-aws

なお、この記事の内容の7割ぐらいは下記と同じ内容となります。元手が@mumoshu(現在freee勤務兼ChatWork技術顧問)*1さんのため。

developers.freee.co.jp

そのため、freeeさんのブログで書かれている内容に関しては、このブログでは説明を省略させて頂きます。 この記事と同じように、gRPC on h2cの構成です。

手順等で使用したソースは下記にあります。

今回の検証における、全体の構成は下記のようになります。

  grpc client
   ↓
  AWS ELB(classic)
   ↓
  kubernetes(service)
   ↓
  nginx-ingress(on k8s) ... 
  ↓                 ↓
grpc server1    grpc server2

ingressを利用する場合、GKE等ではservice経由での接続になりますが、nginx-ingressはserviceを利用せず、直接podにルーティングするので、上記のような構成になります。

nginx-ingressのingressの挙動についての補足

それでは具体的な手順等を記載してきます。

gRPC serverの起動

ここでは、2つのgRPC serverをkubernetesに起動します。起動後、gRPC server単体での確認(ingressを経由しない)のためにgrpcurlを利用して確認します。

gRPCの各リソースの作成

まずは、それぞれのdeployment, serviceを作成します。

nginx-ingressはルーティングの際には、serviceは経由しないのですが、ルーティング先のpodを取得するために、endpoint apiを利用します。そのためpodに紐付いたendpoint*2を作成するためにserviceを作成します。

使用するファイルのディレクトリ https://github.com/cw-sakamoto/grpc-example/tree/master/kubernetes

$ kubectl create -f goodbye-grpc-deployment.yaml
$ kubectl create -f hello-grpc-deployment.yaml
$ kubectl create -f goodbye-grpc-svc.yaml
$ kubectl create -f hello-grpc-svc.yaml

それぞれのdeployment, service, endpointが作成されたことを確認します。

cluster ipはついていても構わないのですが(nginx-ingressはservice経由でアクセスしない)、serviceのvipと結びつかないように、Noneとしています。

$ kubectl get deployment | grep grpc-deployment
goodbye-grpc-deployment               3         3         3            3           5m
hello-grpc-deployment                 3         3         3            3           5m

$ kubectl get svc | grep grpc
goodbye-grpc                            ClusterIP      None         <none>             8999/TCP                     4m
hello-grpc                              ClusterIP      None         <none>             8999/TCP                     5m

$ kubectl get endpoints | grep grpc
goodbye-grpc                            11.2.27.40:8999,11.2.61.36:8999,11.2.64.79:8999   20h
hello-grpc                              11.2.27.41:8999,11.2.61.37:8999,11.2.64.80:8999   20h

grpcurlを利用して確認

grpcurlを利用して、先程起動したgRPC serverに疎通確認してみます。

$ kubectl run golang-pod --tty -i --image=golang:1.10 bash
root@golang-pod-77d8d5554c-j5p89:/go# go get github.com/fullstorydev/grpcurl
root@golang-pod-77d8d5554c-j5p89:/go# go install github.com/fullstorydev/grpcurl/cmd/grpcurl
root@golang-pod-77d8d5554c-j5p89:/go# grpcurl -plaintext -d '{"name":"Ryo Sakamoto"}' hello-grpc:8999 helloworld.Hello.SayHello
{
  "message": "Hello I'm Ryo Sakamoto.  years old. At hello-grpc-deployment-74f89d8cf7-dn697"
}
root@golang-pod-77d8d5554c-j5p89:/go# grpcurl -plaintext -d '{"name":"Ryo Sakamoto"}' goodbye-grpc:8999 helloworld.Goodbye.SayGoodbye
{
  "message": "Goodbye Ryo Sakamoto. At goodbye-grpc-deployment-66f89b667b-ffnvf.\n I sent chandler's words in Collection of famous sayings: The more you reason the less you create."
}

大丈夫そうです。

ちなみに、serviceのhostnameは、同じnamespaceであれば、service nameだけ、違うnamespaceの場合は、servce-name.namespaceで名前解決されます(FQDNは、service-name.namespace.svc.cluster.local)。

これでgRPC serverが起動した状態になりました。

nginx-ingress-controllerのインストールとingressの作成

nginx-ingress-controllerのインストール

nginx-ingressは公式のchartsを利用します。検証段階のversionは下記のようになります。

  • nginx-image 0.15
  • charts 0.20.3

早速入れていきます。

使用するファイルのディレクトリ https://github.com/cw-sakamoto/grpc-example/tree/master/kubernetes/nginx-ingress

# 80番ポートでgRPCが受けられるようにpatchを当てたnginx.tmplを記載したconfigmapを作成
$ kubectl apply -f nginx-template-configmap.yaml

# nginx-ingressをinstall
$ helm upgrade nginx-ingress-grpc stable/nginx-ingress -f setting-grpc.yaml --install

どうしてpatchをあてるかなどは、freeeさんのブログをご参照ください。 ちなみにtemplateもnginx-imageのversion依存があるので、0.15で0.14のtemplateを利用しようとすると、nginxが起動しません。

nginx-ingressが、無事にインストールできると下記のように確認できます。 今回利用しているsetting-grpc.yamlを見ていただくとわかりますが、prometheus用のmetricsをtrueにしているため、それぞれのserviceも立ち上がっています。 これに関しては、別記事で、Datadog Prometheus Checkを試す、という内容で記載しています。

$ kubectl get svc | grep nginx-ingress-grpc
nginx-ingress-grpc-controller           LoadBalancer   11.3.0.29    ae7e6a1ff751c...   80:30178/TCP,443:31492/TCP   1h
nginx-ingress-grpc-controller-metrics   ClusterIP      11.3.0.216   <none>             9913/TCP                     1h
nginx-ingress-grpc-controller-stats     ClusterIP      11.3.0.16    <none>             18080/TCP                    1h
nginx-ingress-grpc-default-backend      ClusterIP      11.3.0.199   <none>             80/TCP                       1h

今回はtype:LoadBalancerで起動しているので、ELBが作成されます。http2 on ELB(classic)の場合、TCPモードにする必要がありますが、type:LoadBalancerで作成されるELBはもともとTPCモードで作成されるので、そのままで問題ありません。

Route 53にて、作成されたELBのendpointを、ingress resourceで設定するhostsのAlias targetに設定しておきます。

ingressの作成

下記の内容のリソースを作成します。

使用するファイル: https://github.com/cw-sakamoto/grpc-example/blob/master/kubernetes/nginx-ingress/nginx-ingress-grpc.yaml

$ kubectl apply -f nginx-ingress-grpc.yaml

ingressのpathですが、gRPCのpathなので、.が入ります。

今回は、

  • package helloworld
    • service Hello
    • service Goodbye

の2つのserviceを、ルーティングを確かめるために別々のgRPC serverで動かします。 grpcのexampleを少しだけ改良して利用しています。

また、ingress-classを今回は、nginx-grpcとしています。これはhttp用のnginx-ingressを別途作成する場合に、ingress-classを分けたいためです。

ところで、ingress-controllerにnginx-ingressを利用している場合、ingress resourceのservicePortのportは、serviceのportでも、containerportでもどちらでも動きます*3。 今回はわかりやすいようにserviceのportを記載しています。

ELBの準備

下記に注意して作成してください。

  • ingress-resourceに設定したhostと同じドメインである必要があります
    • route53のエイリアスなどで、ELBのDNS nameを設定しましょう
    • ELBのportはTPCで開けておく必要があります。 f:id:cw-sakamoto:20180901130709p:plain

clientからの確認

ということで、すべて準備はできたので、clientから確認してみます。At ...はpod idです。

clientのソース grpc-example/greeter_client at master · cw-sakamoto/grpc-example · GitHub

$ go build -o greeter_client main.go
$ ./greeter_client nginx-ingress-grpc.example.com:80 "Ryo"
2018/06/21 16:40:32 Hello: Hello I'm Ryo.  years old. At hello-grpc-deployment-74f89d8cf7-dn697
2018/06/21 16:40:32 Goodbye: Goodbye Ryo. At goodbye-grpc-deployment-66f89b667b-sjmck.
 I sent chandler's words in Collection of famous sayings: There are two kinds of truth: the truth that lights the way and the truth that warms the heart. The first of these is science, and the second is art. Without art, science would be as useless as a pair of high forceps in the hands of a plumber. Without science, art would become a crude mess of folklore and emotional quackery.

$ ./greeter_client nginx-ingress-grpc.example.com:80 "Ryo"
2018/06/21 16:40:37 Hello: Hello I'm Ryo.  years old. At hello-grpc-deployment-74f89d8cf7-6bb98
2018/06/21 16:40:37 Goodbye: Goodbye Ryo. At goodbye-grpc-deployment-66f89b667b-ffnvf.
 I sent chandler's words in Collection of famous sayings: There are two kinds of truth: the truth that lights the way and the truth that warms the heart. The first of these is science, and the second is art. Without art, science would be as useless as a pair of high forceps in the hands of a plumber. Without science, art would become a crude mess of folklore and emotional quackery.

$ ./greeter_client nginx-ingress-grpc.example.com:80 "Ryo"
2018/06/21 16:40:39 Hello: Hello I'm Ryo.  years old. At hello-grpc-deployment-74f89d8cf7-mpxlv
2018/06/21 16:40:39 Goodbye: Goodbye Ryo. At goodbye-grpc-deployment-66f89b667b-gtgm9.
 I sent chandler's words in Collection of famous sayings: If I wasn’t hard, I wouldn’t be alive. If I couldn’t ever be gentle, I wouldn’t deserve to be alive.

無事にpodがロードバランスされているようですね。

ということで、nginx-ingress-controllerを利用して、gRPC serverをロードバランスさせる手順でした。

まとめ

  • nginx-ingress-controllerをkubernetesで動かし、ELB経由で、外部clientからアクセスできることが確認できました
  • 複数のpodにロードバランスされることが確認できました

本番利用の際には、ELBのidle timeoutや、性能面など検証することがまだまだありますが、機能面では問題なさそうです。