このチュートリアルでは、外部パススルー ネットワーク ロードバランサと 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 を使用して TLS 接続の終端処理を行い、gRPC トラフィックを適切な Kubernetes Service に転送します。Kubernetes Ingress などの他のアプリケーション レイヤのソリューションとは異なり、Envoy を使用すると、次のようないくつものカスタマイズ オプションを直接利用できます。
- サービス ディスカバリ
- 負荷分散アルゴリズム
- リクエストやレスポンスの変換(たとえば JSON や gRPC-Web などへの変換)
- JWT トークンを検証することによるリクエストの認証
- gRPC ヘルスチェック
外部パススルー ネットワーク ロードバランサと Envoy を組み合わせると、Google Kubernetes Engine クラスタで実行されている一連の Envoy インスタンスにトラフィックを転送するエンドポイント(外部 IP アドレス)を設定できます。これらのインスタンスはアプリケーション レイヤの情報を使用して、クラスタ内で実行されているさまざまな gRPC サービスに対するリクエストをプロキシ処理します。Envoy インスタンスは、クラスタ DNS を使用して gRPC の受信リクエストを識別し、各サービスの正常に実行されている Pod に負荷分散します。つまりトラフィックは、クライアントからの TCP 接続単位ではなく、RPC リクエスト単位で Pod に負荷分散されます。
アーキテクチャ
このチュートリアルでは、Google Kubernetes Engine(GKE)クラスタに 2 つの gRPC サービス、echo-grpc
と reverse-grpc
をデプロイし、パブリック IP アドレスでインターネットに公開します。次の図は、これら 2 つのサービスを 1 つのエンドポイントを通して公開するためのアーキテクチャを示しています。
外部パススルー ネットワーク ロードバランサは、インターネットから(例: モバイル クライアントや社外のサービス ユーザーから)受信リクエストを受け入れます。外部パススルー ネットワーク ロードバランサは、次のタスクを実行します。
- 受信接続をプール内のノードへロード バランスします。トラフィックは、クラスタ内のすべてのノードに公開されている
envoy
Kubernetes Service に転送されます。Kubernetes ネットワーク プロキシは、Envoy を実行している Pod にこれらの接続を転送します。 - クラスタ内のノードに対して HTTP ヘルスチェックを実行します。
Envoy は次のタスクを実行します。
- TLS 接続を終端します。
- 内部クラスタの DNS サービスを照会して、gRPC サービスを実行している Pod を検出します。
- トラフィックを gRPC サービスの各ポッドにルーティング、負荷分散します。
- gRPC ヘルスチェック プロトコルに従って gRPC サービスのヘルスチェックを実行します。
- 外部パススルー ネットワーク ロードバランサによる Envoy インスタンスのヘルスチェック用にエンドポイントを公開します。
gRPC サービス(echo-grpc
と reverse-grpc
)は、Kubernetes のヘッドレス Service として公開されます。つまり、clusterIP
アドレスが割り振られず、Kubernetes ネットワーク プロキシによってトラフィックが Pod に負荷分散されないということです。代わりに、クラスタの DNS サービス内に、Pod の IP アドレスが含まれた DNS A レコードが作成されます。Envoy はこの DNS エントリからポッドの IP アドレスを検出し、Envoy に構成されているポリシーに従ってそれらの IP アドレス間で負荷分散を行います。
次の図は、このチュートリアルに含まれる Kubernetes オブジェクトを示しています。
費用
このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。
このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。
始める前に
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, activate Cloud Shell.
環境を準備する
Cloud Shell で、このチュートリアルで使用する Google Cloud プロジェクトを設定します。
gcloud config set project PROJECT_ID
PROJECT_ID
は、実際の Google Cloud プロジェクト ID に置き換えます。Artifact Registry API と GKE API を有効にします。
gcloud services enable artifactregistry.googleapis.com \ container.googleapis.com
GKE クラスタを作成する
Cloud Shell で、gRPC サービスを実行するための GKE クラスタを作成します。
gcloud container clusters create envoy-grpc-tutorial \ --enable-ip-alias \ --release-channel rapid \ --scopes cloud-platform \ --workload-pool PROJECT_ID.svc.id.goog \ --zone us-central1-f
このチュートリアルでは、
us-central1-f
ゾーンを使用します。別のゾーンやリージョンも使用できます。クラスタ内のノードを一覧表示して、
kubectl
コンテキストが設定されていることを確認します。kubectl get nodes --output name
出力は次のようになります。
node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-1kpt node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-qn92 node/gke-envoy-grpc-tutorial-default-pool-c9a3c791-wf2h
Artifact Registry リポジトリを作成する
Cloud Shell で、コンテナ イメージを保存する新しいリポジトリを作成します。
gcloud artifacts repositories create envoy-grpc-tutorial-images \ --repository-format docker \ --location us-central1
GKE クラスタと同じリージョンにリポジトリを作成して、ノードがコンテナ イメージを pull するときにレイテンシとネットワーク帯域幅を最適化できるようにします。
GKE クラスタのノード VM で使用される Google サービス アカウントにリポジトリに対する Artifact Registry 読み取りロールを付与します。
PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'value(projectNumber)') gcloud artifacts repositories add-iam-policy-binding envoy-grpc-tutorial-images \ --location us-central1 \ --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \ --role roles/artifactregistry.reader
リポジトリのホスト名の認証ヘルパー エントリを Cloud Shell ホーム ディレクトリの Docker 構成ファイルに追加します。
gcloud auth configure-docker us-central1-docker.pkg.dev
認証ヘルパー エントリを使用すると、Cloud Shell で実行されるコンテナ イメージツールが、イメージを pull および push するための Artifact Registry リポジトリの場所に対して認証を行うことができます。
gRPC サービスをデプロイする
1 つのロードバランサの背後にある複数の gRPC サービスにトラフィックを転送するため、2 つのサンプル gRPC サービス(echo-grpc
と reverse-grpc
)をデプロイします。どちらのサービスも、content
リクエスト フィールド内の文字列を取得する単項メソッドを公開します。echo-grpc
のレスポンスではコンテンツをそのまま返し、reverse-grpc
のレスポンスではコンテンツの文字列を逆にして返します。
Cloud Shell で、gRPC サービスを含むリポジトリのクローンを作成し、リポジトリ ディレクトリに切り替えます。
git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples cd kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
自己署名 TLS 証明書と秘密鍵を作成します。
openssl req -x509 -newkey rsa:4096 -nodes -sha256 -days 365 \ -keyout privkey.pem -out cert.pem -extensions san \ -config \ <(echo "[req]"; echo distinguished_name=req; echo "[san]"; echo subjectAltName=DNS:grpc.example.com ) \ -subj '/CN=grpc.example.com'
envoy-certs
という名前の Kubernetes Secret を作成し、自己署名 TLS 証明書と秘密鍵を格納します。kubectl create secret tls envoy-certs \ --key privkey.pem --cert cert.pem \ --dry-run=client --output yaml | kubectl apply --filename -
この TLS 証明書と秘密鍵は、Envoy が TLS 接続の終端時に使用します。
Skaffold を使用して、サンプルアプリ
echo-grpc
とreverse-grpc
のコンテナ イメージを作成し、それらのイメージを Artifact Registry に push して、アプリを GKE クラスタにデプロイします。skaffold run \ --default-repo=us-central1-docker.pkg.dev/PROJECT_ID/envoy-grpc-tutorial-images \ --module=echo-grpc,reverse-grpc \ --skip-tests
Skaffold は Google のオープンソース ツールであり、アプリケーションをコンテナとして開発、ビルド、push、デプロイするワークフローを自動化します。
次のように、Skaffold を使用して GKE クラスタに Envoy をデプロイします。
skaffold run \ --digest-source=none \ --module=envoy \ --skip-tests
各 Deployment で 2 つの Pod の準備ができていることを確認します。
kubectl get deployments
出力は次のようになります。すべての Deployment で、
READY
の値は2/2
になっている必要があります。NAME READY UP-TO-DATE AVAILABLE AGE echo-grpc 2/2 2 2 1m envoy 2/2 2 2 1m reverse-grpc 2/2 2 2 1m
echo-grpc
、envoy
、reverse-grpc
が Kubernetes Services として存在することを確認します。kubectl get services --selector skaffold.dev/run-id
出力は次のようになります。
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 2m envoy LoadBalancer 10.40.2.203 203.0.113.1 443:31516/TCP 2m reverse-grpc ClusterIP None <none> 8082/TCP 2m
gRPC サービスをテストする
サービスをテストするには、grpcurl
コマンドライン ツールを使用します。
Cloud Shell で、
grpcurl
をインストールします。go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
envoy
Kubernetes Service の外部 IP アドレスを取得して、環境変数に保存します。EXTERNAL_IP=$(kubectl get service envoy \ --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo-grpc
サンプルアプリにリクエストを送信します。grpcurl -v -d '{"content": "echo"}' \ -proto echo-grpc/api/echo.proto \ -authority grpc.example.com -cacert cert.pem \ $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, 02 Jun 2021 07:18:22 GMT hostname: echo-grpc-75947768c9-jkdcw server: envoy x-envoy-upstream-service-time: 3 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 -v -d '{"content": "reverse"}' \ -proto reverse-grpc/api/reverse.proto \ -authority grpc.example.com -cacert cert.pem \ $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, 02 Jun 2021 07:20:15 GMT hostname: reverse-grpc-5c9b974f54-wlfwt server: envoy x-envoy-upstream-service-time: 1 Response contents: { "content": "esrever" } Response trailers received: (empty) Sent 1 request and received 1 response
Envoy の構成
Envoy の構成をより深く理解するために、Git リポジトリの構成ファイル envoy/k8s/envoy.yaml
で確認できます。
route_config
セクションは、受信リクエストを echo-grpc
サンプルアプリと reverse-grpc
サンプルアプリに転送する方法を指定します。
サンプルアプリは Envoy クラスタとして定義されています。
クラスタ定義の type: STRICT_DNS
フィールドと lb_policy: ROUND_ROBIN
フィールドは、Envoy が address
フィールドで指定されたホスト名の DNS ルックアップを行い、DNS ルックアップのレスポンスの IP アドレス間でロード バランシングを行うように指定します。サンプルアプリを定義する Kubernetes Service オブジェクトはヘッドレス サービスを指定しているため、レスポンスには複数の IP アドレスが含まれます。
http2_protocol_options
フィールドは、Envoy がサンプルアプリで HTTP/2 プロトコルを使用することを指定します。
health_checks
セクションの grpc_health_check
フィールドでは、Envoy が gRPC ヘルスチェック プロトコルを使用してサンプルアプリの状態を判断することを指定します。
トラブルシューティング
このチュートリアルで問題が発生した場合は、次のドキュメントを確認することをおすすめします。
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 が提供するアプリケーション レイヤの機能は、他のロード バランシング ソリューションでも提供されている場合があります。
外部パススルー ネットワーク ロードバランサとセルフマネージド Envoy の代わりに、グローバル外部アプリケーション ロードバランサまたはリージョン外部アプリケーション ロードバランサを使用できます。外部アプリケーション ロードバランサを使用することには、高度なトラフィック管理機能、マネージド TLS 証明書、その他の Google Cloud プロダクト(例: Cloud CDN、Google Cloud Armor、IAP)とのインテグレーションなど、外部パススルー ネットワーク ロードバランサと比較した際にいくつかの利点があります。
利用可能なトラフィック管理機能がユースケースの要件を満たし、相互 TLS(mTLS)認証とも呼ばれる、クライアントの証明書ベースの認証サポートが不要な場合は、グローバル外部アプリケーション ロードバランサまたはリージョン外部アプリケーション ロードバランサを使用することをおすすめします。詳細については、次のドキュメントをご覧ください。
Cloud Service Mesh や Istio を使用する場合は、それらの機能を使用して gRPC トラフィックの転送とロードバランスを行えます。このチュートリアルのアーキテクチャと同様、Cloud Service Mesh と Istio はどちらも、Envoy のバックエンドを持つ外部パススルー ネットワーク ロードバランサとしてデプロイされる Ingress ゲートウェイを備えています。主な違いは、Envoy は Istio のトラフィック ルーティング オブジェクトを介して構成されることです。
このチュートリアルのサンプル サービスを Cloud Service Mesh や Istio サービス メッシュ内で転送できるようにするには、Kubernetes Service マニフェスト(
echo-service.yaml
とreverse-service.yaml
)からclusterIP: None
の行を削除する必要があります。これは、Envoy の同様の機能ではなく、Cloud Service Mesh または Istio のサービス ディスカバリとロード バランシングの機能を使用することを表しています。Cloud Service Mesh または Istio をすでに使用している場合は、Ingress ゲートウェイを使用して gRPC サービスに転送することをおすすめします。
Envoy の代わりに NGINX を Deployment として使用できます。または、NGINX Ingress Controller for Kubernetes を使用することもできます。Envoy では gRPC ヘルスチェック プロトコルのサポートなどの高度な gRPC 機能が提供されるため、このチュートリアルでは Envoy を使用しています。
VPC 内部のネットワーク接続
GKE クラスタの外部にサービスを公開する必要があるものの、VPC ネットワーク内に限定することを必要とする場合は、内部パススルー ネットワーク ロードバランサまたは内部アプリケーション ロードバランサを使用します。
外部パススルー ネットワーク ロードバランサの代わりに内部パススルー ネットワーク ロードバランサを使用するには、envoy-service.yaml
マニフェストにアノテーション cloud.google.com/load-balancer-type: "Internal"
を追加します。
内部アプリケーション ロードバランサを使用するには、内部アプリケーション ロードバランサの Ingress の構成のドキュメントをご覧ください。
クリーンアップ
チュートリアルが終了したら、作成したリソースをクリーンアップして、割り当ての使用を停止し、課金されないようにできます。次のセクションで、リソースを削除または無効にする方法を説明します。
プロジェクトの削除
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
リソースの削除
このチュートリアルで使用した Google Cloud プロジェクトを残しておく場合は、個々のリソースを削除します。
Cloud Shell で、ローカル Git リポジトリのクローンを削除します。
cd ; rm -rf kubernetes-engine-samples/networking/grpc-gke-nlb-tutorial/
GKE クラスタを削除します。
gcloud container clusters delete envoy-grpc-tutorial \ --zone us-central1-f --async --quiet
Artifact Registry でリポジトリを削除します。
gcloud artifacts repositories delete envoy-grpc-tutorial-images \ --location us-central1 --async --quiet
次のステップ
- GKE ネットワーキングを確認する。
- gRPC サービスを Kubernetes クラスタ内のクライアントに公開する方法の例を見る。
gRPC ロード バランシングのオプションを調べる。
Google Cloud に関するリファレンス アーキテクチャ、図、ベスト プラクティスを確認する。Cloud アーキテクチャ センターをご覧ください。