このチュートリアルでは、ネットワーク負荷分散と Envoy プロキシを使用して、Google Kubernetes Engine(GKE)にデプロイした複数の gRPC サービスを 1 つの外部 IP アドレスで公開する方法について説明します。また、Envoy が gRPC 用に提供している高度な機能の一部についても取り上げます。
はじめに
gRPC は、HTTP/2 をベースとした、言語に依存しないオープンソースの RPC フレームワークです。プロトコル バッファを使用し、効率のよいデータ転送と高速なシリアル化を実現します。Google 内部の RPC フレームワークである Stubby からアイデアを得て、gRPC はマイクロサービス間やモバイル クライアントと API 間の低レイテンシ通信を可能にしています。
gRPC は HTTP/2 で動作するため、効率的なバイナリ エンコード、単一接続上でのリクエストとレスポンスの多重化、自動フロー制御といった、HTTP/1.1 より優れた点がいくつかあります。また、gRPC には負荷分散のオプションもいくつか用意されています。このチュートリアルでは、モバイル クライアントや、サービス プロバイダの信頼境界の外部で実行されているクライアントなど、クライアントを信頼できない状況に焦点を当てています。また、gRPC が提供する負荷分散オプションのうち、プロキシベースの負荷分散を使用します。
このチュートリアルでは、TYPE=LoadBalancer
の Kubernetes Service をデプロイし、Google Cloud 上でトランスポート レイヤ(レイヤ 4)のネットワーク負荷分散として公開します。このサービスはパブリック IP アドレスを 1 つ提供し、構成されているバックエンドに TCP 接続を直接渡します。このチュートリアルでのバックエンドは、Envoy インスタンスの Kubernetes Deployment です。
Envoy は、高度な機能を数多く提供するオープンソースのアプリケーション レイヤ(レイヤ 7)プロキシです。このチュートリアルでは、Envoy を使用して SSL / TLS 接続の終端処理を行い、gRPC トラフィックを適切な Kubernetes Service にルーティングします。Kubernetes Ingress などの他のアプリケーション レイヤのソリューションとは異なり、Envoy を使用すると、次のようないくつものカスタマイズ オプションを直接利用できます。
- サービス ディスカバリ
- 負荷分散アルゴリズム
- リクエストやレスポンスの変換(たとえば JSON や gRPC-Web などへの変換)
- JWT トークンを検証することによるリクエストの認証
- gRPC ヘルスチェック
ネットワーク負荷分散と Envoy を組み合わせることで、1 つのエンドポイント(外部 IP アドレス)をセットアップし、GKE クラスタ内で実行されている一連の Envoy インスタンスにトラフィックを転送できます。これらのインスタンスはアプリケーション レイヤの情報を使用して、クラスタ内で実行されているさまざまな gRPC サービスに対するリクエストをプロキシ処理します。Envoy インスタンスは、クラスタ DNS を使用して gRPC の受信リクエストを識別し、各サービスの正常に実行されている Pod に負荷分散します。つまりトラフィックは、クライアントからの TCP 接続単位ではなく、RPC リクエスト単位で Pod に負荷分散されます。
費用
このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。新しい Google Cloud ユーザーは無料トライアルをご利用いただけます。
このチュートリアルを終了した後、作成したリソースを削除すると、それ以上の請求は発生しません。詳しくは、クリーンアップをご覧ください。
始める前に
- Google アカウントにログインします。
Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。
-
Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。
-
Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する。
- Cloud Build, Container Registry, and Container Analysis API を有効にします。
アーキテクチャ
このチュートリアルでは、Google Kubernetes Engine(GKE)クラスタに 2 つの gRPC サービス、echo-grpc
と reverse-grpc
をデプロイし、パブリック IP アドレスでインターネットに公開します。次の図は、これら 2 つのサービスを 1 つのエンドポイントを通して公開するためのアーキテクチャを示しています。
ネットワーク負荷分散がインターネットから(たとえば、モバイル クライアントや社外のサービス ユーザーから)受信リクエストを受け入れます。ネットワーク負荷分散は次のタスクを実行します。
- 受信接続をプール内のワーカーノードへ負荷分散します。トラフィックは、クラスタ内のすべてのワーカーノードに公開されている
envoy
Kubernetes Service に転送されます。Kubernetes ネットワーク プロキシは、Envoy を実行している Pod にこれらの接続を転送します。 - クラスタ内のワーカーノードに対して HTTP ヘルスチェックを実行します。
Envoy は次のタスクを実行します。
- SSL/TLS 接続の終端処理を行います。
- 内部クラスタの DNS サービスを照会して、gRPC サービスを実行しているポッドを検出します。
- トラフィックを gRPC サービスの各ポッドにルーティング、負荷分散します。
- gRPC ヘルスチェック プロトコルに従って gRPC サービスのヘルスチェックを実行します。
- ネットワーク負荷分散を使用して、ヘルスチェックのためにエンドポイントを公開します。
gRPC サービス(echo-grpc
と reverse-grpc
)は、Kubernetes のヘッドレス Service として公開されます。つまり、clusterIP
アドレスが割り振られず、Kubernetes ネットワーク プロキシによってトラフィックが Pod に負荷分散されないということです。代わりに、クラスタの DNS サービス内に、Pod の IP アドレスが含まれた DNS A レコードが作成されます。Envoy はこの DNS エントリからポッドの IP アドレスを検出し、Envoy に構成されているポリシーに従ってそれらの IP アドレス間で負荷分散を行います。
次の図は、このチュートリアルに含まれる Kubernetes オブジェクトを示しています。
環境を初期化する
このセクションでは、チュートリアルで使用する環境変数を設定します。
Cloud Shell を開きます。
このチュートリアルでは、コマンドの実行はすべて Cloud Shell で行います。
Cloud Shell で現在のプロジェクト ID を表示します。
gcloud config list --format 'value(core.project)'
コマンドから選択したプロジェクトの ID が返されない場合は、プロジェクトが使用されるように Cloud Shell を構成します。project-id はプロジェクトの名前に置き換えます。
gcloud config set project project-id
このチュートリアルで使用するリージョンとゾーンの環境変数を定義します。
REGION=us-central1 ZONE=$REGION-b
このチュートリアルでは、
us-central1
リージョンとus-central1-b
ゾーンを使用します。ただし、リージョンとゾーンは必要に応じて変更できます。
GKE クラスタを作成する
gRPC サービスを実行するための GKE クラスタを作成します。
gcloud container clusters create grpc-cluster --zone $ZONE
クラスタ内の動作中のノードを一覧表示して、
kubectl
コンテキストが設定されていることを確認します。kubectl get nodes -o name
出力は次のようになります。
node/gke-grpc-cluster-default-pool-c9a3c791-1kpt node/gke-grpc-cluster-default-pool-c9a3c791-qn92 node/gke-grpc-cluster-default-pool-c9a3c791-wf2h
gRPC サービスをデプロイする
1 つのロードバランサの背後にある複数の gRPC サービスにトラフィックをルーティングするために、2 つのシンプルな gRPC サービス、echo-grpc
と reverse-grpc
をデプロイします。どちらのサービスも、コンテンツのリクエスト フィールド内の文字列を取得する単項メソッドを公開します。echo-grpc
のレスポンスではコンテンツを変更せずに返し、reverse-grpc
のレスポンスではコンテンツの文字列を逆にして返します。
これらの gRPC サービスが含まれているリポジトリのクローンを作成し、作業ディレクトリに切り替えます。
git clone https://github.com/GoogleCloudPlatform/grpc-gke-nlb-tutorial cd grpc-gke-nlb-tutorial
Cloud Build を使用して、Echo と Reverse の gRPC サービス用にコンテナ イメージを作成し、それらを Container Registry に格納します。
gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc echo-grpc gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc reverse-grpc
Container Registry にイメージが存在することを確認します。
gcloud container images list --repository gcr.io/$GOOGLE_CLOUD_PROJECT
出力は次のようになります。
NAME gcr.io/grpc-gke-nlb-tutorial/echo-grpc gcr.io/grpc-gke-nlb-tutorial/reverse-grpc
echo-grpc
とreverse-grpc
用に Kubernetes Deployment を作成します。sed s/GOOGLE_CLOUD_PROJECT/$GOOGLE_CLOUD_PROJECT/ \ k8s/echo-deployment.yaml | kubectl apply -f - sed s/GOOGLE_CLOUD_PROJECT/$GOOGLE_CLOUD_PROJECT/ \ k8s/reverse-deployment.yaml | kubectl apply -f -
各 Deployment で 2 つの Pod が利用可能であることを確認します。
kubectl get deployments
出力は次のようになります。どちらの Deployment でも、
DESIRED
、CURRENT
、UP-TO-DATE
、AVAILABLE
の値が2
になっているはずです。NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE echo-grpc 2 2 2 2 1m reverse-grpc 2 2 2 2 1m
echo-grpc
とreverse-grpc
用に Kubernetes ヘッドレス Service を作成します。以下のコマンドはクラスタの DNS サービスに DNS A レコードを作成しますが、仮想 IP アドレスは割り振りません。kubectl apply -f k8s/echo-service.yaml kubectl apply -f k8s/reverse-service.yaml
echo-grpc
とreverse-grpc
が Kubernetes Services として存在することを確認します。kubectl get services
出力は次のようになります。
echo-grpc
とreverse-grpc
はどちらもTYPE=ClusterIP
とCLUSTER-IP=None
になります。NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE echo-grpc ClusterIP None <none> 8081/TCP 35s kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 47m reverse-grpc ClusterIP None <none> 8082/TCP 21s
ネットワーク負荷分散を設定する
クラスタ内に
LoadBalancer
タイプの Kubernetes Service を作成します。kubectl apply -f k8s/envoy-service.yaml
このコマンドはネットワーク負荷分散に必要なリソースをプロビジョニングし、エフェメラル パブリック IP アドレスを割り振ります。パブリック IP アドレスが割り振られるまで数分かかることがあります。
次のコマンドを実行して、
envoy
サービスのEXTERNAL-IP
の値が<pending>
からパブリック IP アドレスに変わるまで待機します。kubectl get services envoy --watch
Control+C
キーを押すと、待機を終了します。
自己署名 SSL / TLS 証明書を作成する
Envoy は SSL/TLS 接続の終端処理を行う際に証明書と鍵を使用します。まずは、自己署名 SSL/TLS 証明書を作成します。
環境変数を作成し、前のセクションで作成した Envoy サービスのパブリック IP アドレスを格納します。
EXTERNAL_IP=$(kubectl get service envoy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
自己署名 SSL / TLS 証明書と鍵を作成します。
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \ -keyout privkey.pem -out cert.pem -subj "/CN=$EXTERNAL_IP"
envoy-certs
という名前の Kubernetes TLS Secret を作成し、自己署名 SSL / TLS 証明書と鍵を格納します。kubectl create secret tls envoy-certs \ --key privkey.pem --cert cert.pem \ --dry-run -o yaml | kubectl apply -f -
Envoy をデプロイする
Envoy の構成ファイル(
envoy.yaml
)を保存するための Kubernetes ConfigMap を作成します。kubectl apply -f k8s/envoy-configmap.yaml
Envoy 用の Kubernetes Deployment を作成します。
kubectl apply -f k8s/envoy-deployment.yaml
2 つの
envoy
Pod が実行されていることを確認します。kubectl get deployment envoy
出力は次のようになります。
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE envoy 2 2 2 2 1m
これで、gRPC サービスをテストする準備が整いました。
gRPC サービスをテストする
サービスをテストするには、grpcurl
コマンドライン ツールを使用します。
Cloud Shell で、
grpcurl
をインストールします。go get github.com/fullstorydev/grpcurl go install github.com/fullstorydev/grpcurl/cmd/grpcurl
Echo gRPC サービスにリクエストを送信します。
grpcurl -d '{"content": "echo"}' -proto echo-grpc/api/echo.proto \ -insecure -v $EXTERNAL_IP:443 api.Echo/Echo
出力は次のようになります。
Resolved method descriptor: rpc Echo ( .api.EchoRequest ) returns ( .api.EchoResponse ); Request metadata to send: (empty) Response headers received: content-type: application/grpc date: Wed, 27 Feb 2019 04:40:19 GMT hostname: echo-grpc-5c4f59c578-wcsvr server: envoy x-envoy-upstream-service-time: 0 Response contents: { "content": "echo" } Response trailers received: (empty) Sent 1 request and received 1 response
hostname
レスポンス ヘッダーには、リクエストを処理したecho-grpc
Pod の名前が表示されます。このコマンドを数回繰り返すと、hostname
レスポンス ヘッダーにはecho-grpc
Pod の名前に対応した 2 つの異なる値が表示されます。Reverse gRPC サービスでも同じ動作を確認します。
grpcurl -d '{"content": "reverse"}' -proto reverse-grpc/api/reverse.proto \ -insecure -v $EXTERNAL_IP:443 api.Reverse/Reverse
出力は次のようになります。
Resolved method descriptor: rpc Reverse ( .api.ReverseRequest ) returns ( .api.ReverseResponse ); Request metadata to send: (empty) Response headers received: content-type: application/grpc date: Wed, 27 Feb 2019 04:45:56 GMT hostname: reverse-grpc-74cdc4849f-tvsfb server: envoy x-envoy-upstream-service-time: 2 Response contents: { "content": "esrever" } Response trailers received: (empty) Sent 1 request and received 1 response
トラブルシューティング
このチュートリアルで問題が発生した場合は、次のドキュメントを確認することをおすすめします。
Envoy 管理インターフェースを使用して、Envoy の構成に関する問題を診断することもできます。
管理インターフェースを開くには、Cloud Shell からいずれかの Envoy Pod の
admin
ポートへのポート転送を設定します。kubectl port-forward \ $(kubectl get pods -o name | grep envoy | head -n1) 8080:8090
コンソールに次の出力が表示されるまで待ちます。
Forwarding from 127.0.0.1:8080 -> 8090
Cloud Shell で [ウェブでプレビュー] ボタンをクリックし、[プレビューのポート: 8080] を選択します。新しいブラウザ ウィンドウが開き、管理インターフェースが表示されます。
完了したら Cloud Shell に戻り、
Control+C
キーを押してポート転送を終了します。
gRPC トラフィックをルーティングするための代替方法
このソリューションは、ご使用の環境に合うようにさまざまな方法で変更できます。
代替のアプリケーション レイヤ ロードバランサ
Envoy が提供するアプリケーション レイヤの機能は、他の負荷分散ソリューションでも提供されている場合があります。
Kubernetes Ingress オブジェクトを使用して HTTP(S) 負荷分散を構成し、ネットワーク負荷分散と Envoy の代わりに使用できます。HTTP(S) 負荷分散の使用には、ネットワーク負荷分散と比べていくつかの利点があります。たとえば、マネージド型の SSL / TLS 証明書、Cloud CDN や IAP などの他の Google Cloud プロダクトとの統合などが挙げられます。
以下のいずれにも対応する必要がない場合は、HTTP(S) 負荷分散を使用することをおすすめします。
- gRPC ヘルスチェック
- 負荷分散アルゴリズムに対するきめ細かな制御
- 50 以上のサービスの公開
HTTP(S) 負荷分散とサンプル gRPC サービスのデプロイについては、Google Kubernetes Engine の Ingress に関するドキュメントと、GitHub の GKE gRPC Ingress LoadBalancing チュートリアルをご覧ください。
Istio を使用する場合は、Istio の機能を使用して gRPC トラフィックのルーティングと負荷分散を行うことができます。Istio の Ingress Gateway は、このチュートリアルのアーキテクチャと同様に、Envoy バックエンドと組み合わせたネットワーク負荷分散としてデプロイされます。主な違いは、Envoy プロキシが Istio のトラフィック ルーティング オブジェクトを通じて構成されることです。このチュートリアルのサンプル サービスを Istio のサービス メッシュ内でルーティングできるようにするには、Kubernetes Service のマニフェスト(
echo-service.yaml
とreverse-service.yaml
)からclusterIP: None
の行を削除する必要があります。これは、Istio のサービス ディスカバリと負荷分散の機能を使用し、Envoy の同様の機能は使用しないことを意味します。すでに Istio を使用している場合は、gRPC サービスへのルーティングに Ingress Gateway を使用することをおすすめします。Envoy の代わりに NGINX を Deployment として使用できます。または、NGINX Ingress Controller for Kubernetes を使用することもできます。Envoy では gRPC ヘルスチェック プロトコルのサポートなどの高度な gRPC 機能が提供されるため、このチュートリアルでは Envoy を使用しています。
Ambassador と Contour を使用できます。これらは Kubernetes Ingress コントローラを提供し、Envoy をベースにしています。
Voyager を使用できます。Voyager は、HAProxy をベースにした Kubernetes Ingress コントローラです。
VPC 内部のネットワーク接続
GKE クラスタの外部にサービスを公開したいが、公開範囲を VPC ネットワーク内に限定したい場合は、ネットワーク負荷分散の代わりに内部 TCP / UDP 負荷分散を使用します。これを行うには、envoy-service.yaml
マニフェストにアノテーション cloud.google.com/load-balancer-type: "Internal"
を追加します。
Envoy の Deployment と DaemonSet
このチュートリアルでは、Envoy は Kubernetes Deployment として構成されます。この構成では、Deployment マニフェストの replica
設定によって Envoy の Pod 数が決定されます。ロードバランサが Envoy Pod を実行していないワーカーノードに受信リクエストを転送すると、Kubernetes ネットワーク プロキシはそのリクエストを Envoy Pod を実行しているワーカーノードに転送します。
DaemonSet は、Envoy の Deployment に代わるものです。DaemonSet では、Envoy ポッドは GKE クラスタ内のすべてのワーカーノードで実行されます。この方法を使用すると、大規模なクラスタ(多数の Envoy ポッド)においてはリソースの使用量が多くなりますが、Envoy ポッドを実行しているワーカーノードに受信リクエストが必ず到達します。Envoy ポッドに到達させるためにワーカーノード間でリクエストが転送されることがないため、結果としてクラスタ内のネットワーク トラフィックが減少し、平均レイテンシが小さくなります。
クリーンアップ
現在のチュートリアルが終了したら、Google Cloud で作成したリソースをクリーンアップして、今後料金が発生しないようにします。次のセクションで、リソースを削除または無効にする方法を説明します。
プロジェクトの削除
- Cloud Console で [リソースの管理] ページに移動します。
- プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
- ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。
リソースの削除
このチュートリアルで使用した Google Cloud プロジェクトを残しておく場合は、個々のリソースを削除します。
Cloud Shell で、ローカル Git リポジトリのクローンを削除します。
cd ; rm -rf ~/grpc-gke-nlb-tutorial
Container Registry 内のイメージを削除します。
gcloud container images list-tags gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc \ --format 'value(digest)' | xargs -I {} gcloud container images delete \ --force-delete-tags --quiet gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc@sha256:{} gcloud container images list-tags gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc \ --format 'value(digest)' | xargs -I {} gcloud container images delete \ --force-delete-tags --quiet gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc@sha256:{}
Google Kubernetes Engine クラスタを削除します。
gcloud container clusters delete grpc-cluster --zone $ZONE --quiet --async
次のステップ
- Google Kubernetes Engine のネットワークについて読む。
- gRPC サービスを Kubernetes クラスタ内のクライアントに公開する方法の例を見る。
- gRPC 負荷分散のオプションを調べる。
- Google Kubernetes Engine の本番環境を準備する方法を学習する。
- Google Kubernetes Engine 入門ガイドを確認する。
- Google Cloud で GitOps スタイルの継続的デリバリー パイプラインを作成する方法を学習する。
- Cloud Endpoints の Extensible Service Proxy と Google Kubernetes Engine を使用して gRPC サービスをデプロイする方法を学習する。
- Google Cloud のその他の機能を試す。チュートリアルをご覧ください。