Istio を使用して内部 gRPC サービスを負荷分散する

このチュートリアルでは、Google Kubernetes Engine(GKE)で動作する gRPC サービス用に Istio を使用して内部 TCP / UDP 負荷分散を設定する方法を説明します。この設定によって、VPC ネットワーク内の他のリソースが、プライベートな内部(RFC 1918)IP アドレスを使用して gRPC サービスと通信できるようになります。同時に Istio は、gRPC サービスを実行している Kubernetes ポッド間でルーティングと負荷分散のリクエストを処理します。

このチュートリアルは、gRPC と GKE または Kubernetes の基本的な知識があることを前提にしています。

はじめに

Istio は、サービス メッシュを出入りするトラフィックを管理するゲートウェイを提供します。Istio 内部ロードバランサ(ILB)ゲートウェイは、内部 VPC ネットワークのソースからのインバウンド トラフィックを、サービス メッシュの Kubernetes ポッドに送信します。このアーキテクチャでは、Google Cloud の内部 TCP / UDP 負荷分散は、GKE クラスタ内のノード間でレイヤ 4(トランスポート レイヤ)負荷分散を実行します。Istio ILB ゲートウェイはトラフィックを受信し、仮想サービスで定義されたルールと宛先ルールを使用して Istio サービス メッシュ内のサービスにトラフィックを配分しながら、レイヤ 7(アプリケーション レイヤ)の負荷分散を実行します。

このチュートリアルで使用されるサンプル gRPC サービスは、リクエストを処理した Kubernetes ポッドの名前を含むレスポンス ヘッダーを返します。この情報を使用すると、Istio ILB ゲートウェイによる負荷分散が、単一の接続を介したクライアントによる要求が GKE クラスタ内の複数の Kubernetes ポッドに分散されることがわかります。

目標

  • Istio と Istio ILB ゲートウェイを使用した GKE クラスタの作成。
  • サンプル gRPC サービスのデプロイ。
  • 内部接続の確認。

費用

このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。新しい Google Cloud ユーザーは無料トライアルをご利用いただけます。

このチュートリアルを終了した後、作成したリソースを削除すると、それ以上の請求は発生しません。詳しくは、クリーンアップをご覧ください。

始める前に

  1. Google アカウントにログインします。アカウントがない場合は、新しいアカウントを登録します。
  2. Cloud Console で、[VM インスタンス] ページに移動します。

    プロジェクト セレクタのページに移動

  3. Cloud プロジェクトを選択または作成します。

  4. Google Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

環境を初期化する

  1. Cloud Console の [プロジェクトの選択] のプルダウンから、使用するプロジェクトを選択します。

  2. Cloud Shell を開きます。

    Cloud Shell に移動

    このチュートリアルでは、コマンドの実行はすべて Cloud Shell で行います。

  3. Cloud Build API、Google Kubernetes Engine API、Container Analysis API、Cloud API を有効にします。

    gcloud services enable \
        cloudapis.googleapis.com \
        cloudbuild.googleapis.com \
        container.googleapis.com \
        containeranalysis.googleapis.com
    
  4. このチュートリアルで使用する gcloud のデフォルト Compute Engine ゾーンを設定します。

    gcloud config set compute/zone us-central1-b
    

    このチュートリアルでは、us-central1-b ゾーンを使用します。必要に応じてゾーンを変更できます。

  5. サンプル gRPC サービスが含まれているリポジトリのクローンを作成し、作業ディレクトリに移動します。

    git clone https://github.com/GoogleCloudPlatform/istio-samples.git
    cd istio-samples/sample-apps/grpc-greeter-go
    

Istio で GKE クラスタを作成する

  1. Cloud Shell で、GKE クラスタを作成します。

    gcloud beta container clusters create grpc-istio-ilb-tutorial \
        --machine-type n1-standard-2 \
        --enable-ip-alias
    
  2. クラスタの管理者権限を自分に付与します。

    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin \
        --user $(gcloud config get-value account)
    

    Istio をインストールするには、cluster-admin Kubernetes クラスタ役割で定義されている権限が必要です。

  3. istio-system という Kubernetes 名前空間を作成します。

    kubectl create namespace istio-system
    
  4. Istio をダウンロードして抽出します。

    ISTIO_VERSION=1.2.7
    curl -L https://github.com/istio/istio/releases/download/$ISTIO_VERSION/istio-$ISTIO_VERSION-linux.tar.gz | tar -zxf -
    
  5. Helm のローカル テンプレート レンダリングを使用して、Istio カスタム リソース定義(CRD)をインストールします。

    helm template \
        istio-$ISTIO_VERSION/install/kubernetes/helm/istio-init \
        --namespace istio-system | kubectl apply -f -
    
  6. Helm を使用して Istio を ILB ゲートウェイ(istio-ilbgateway)と一緒にインストールします。

    ISTIO_PACKAGE=$ISTIO_VERSION-gke.0
    
    helm template \
        istio-$ISTIO_VERSION/install/kubernetes/helm/istio \
        --set gateways.istio-ingressgateway.enabled=false \
        --set gateways.istio-ilbgateway.enabled=true \
        --set gateways.istio-ilbgateway.ports[0].name=grpc-pilot-mtls \
        --set gateways.istio-ilbgateway.ports[0].port=15011 \
        --set gateways.istio-ilbgateway.ports[1].name=grpc \
        --set gateways.istio-ilbgateway.ports[1].port=443 \
        --set gateways.istio-ilbgateway.ports[2].name=tcp-citadel-grpc-tls \
        --set gateways.istio-ilbgateway.ports[2].port=8060 \
        --set gateways.istio-ilbgateway.ports[2].targetPort=8060 \
        --set gateways.istio-ilbgateway.ports[3].name=tcp-dns \
        --set gateways.istio-ilbgateway.ports[3].port=5353 \
        --set global.hub=gcr.io/gke-release/istio \
        --set global.tag=$ISTIO_PACKAGE \
        --namespace istio-system | kubectl apply -f -
    
  7. istio-ilbgateway Kubernetes Service の外部 IP アドレスの作成ステータスを確認します。

    kubectl get services istio-ilbgateway -n istio-system --watch
    

    EXTERNAL-IP 値が <pending> から IP アドレスに変化するまで待機します。Control+C キーを押すと、待機を終了します。

Istio ILB ゲートウェイ用の TLS 証明書を作成する

  1. Cloud Shell で、TLS 証明書と秘密鍵を作成し、Istio ILB ゲートウェイによる TLS ターミネーションを許可します。

    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
        -keyout privkey.pem -out cert.pem -subj "/CN=grpc.example.com"
    
  2. Kubernetes Secret を作成して、TLS 証明書と秘密鍵を保存します。

    kubectl -n istio-system create secret tls istio-ilbgateway-certs \
        --key privkey.pem --cert cert.pem \
        --dry-run -o yaml | kubectl apply -f -
    

サンプル アプリケーションをインストールする

次のステップでは、サンプル gRPC サービスのコンテナ イメージを作成し、GKE クラスタにデプロイします。サンプル gRPC サービスは、gRPC クライアントと呼ばれるクライアント コンポーネントと、gRPC サーバーと呼ばれるサーバー コンポーネントで構成されます。

  1. Cloud Shell で、default 名前空間での自動 Istio サイドカー インジェクションを有効にします。

    kubectl label namespace default istio-injection=enabled
    
  2. Cloud Build を使用して、gRPC サーバーのコンテナ イメージを作成します。

    gcloud builds submit server -t gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-server
    
  3. gRPC サーバー用の Kubernetes Deployment とサービス オブジェクトを作成します。

    envsubst < manifests/greeter-k8s.template.yaml | kubectl apply -f -
    
  4. ClusterIP Kubernetes Service が作成され、ポッドが実行されていることを確認します。

    kubectl get services,pods
    

    出力は次のようになります。

    NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
    service/greeter      ClusterIP   10.0.18.67   <none>        8080/TCP   11s
    service/kubernetes   ClusterIP   10.0.16.1    <none>        443/TCP    1h
    NAME READY STATUS RESTARTS AGE pod/greeter-844cffd75-7hpcb 2/2 Running 0 56s pod/greeter-844cffd75-ffccl 2/2 Running 0 56s pod/greeter-844cffd75-zww6h 2/2 Running 0 56s

    ポッドでは、READY 列に 2/2 が表示されます。この出力は、各ポッドに対して gRPC サーバー コンテナと Envoy サイドカー コンテナの両方が実行されていることを表します。

  5. gRPC サーバーに、Istio ゲートウェイ仮想サービス宛先ルール オブジェクトを作成します。

    kubectl apply -f manifests/greeter-istio-ilbgateway.yaml \
        -f manifests/greeter-istio-virtualservice.yaml \
        -f manifests/greeter-istio-destinationrule.yaml
    
  6. 3 つのオブジェクトがすべて正常に作成されたことを確認します。

    kubectl get gateway,virtualservice,destinationrule
    

    出力は次のようになります。

    NAME                                  AGE
    gateway.networking.istio.io/greeter   1m
    NAME GATEWAYS HOSTS AGE virtualservice.networking.istio.io/greeter [greeter] [*] 1m
    NAME HOST AGE destinationrule.networking.istio.io/greeter greeter 1m

内部接続を確認する

内部 TCP / UDP 負荷分散はリージョン内で行われるので、同じゾーンまたはリージョン内の VM から接続をテストできます。

  1. Cloud Shell で、Cloud Build を使用して gRPC クライアントのコンテナ イメージを作成します。

    gcloud builds submit client \
        -t gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-client
    
  2. GKE クラスタと同じゾーンまたはリージョンに Compute Engine インスタンスを作成します。

    gcloud compute instances create grpc-istio-ilb-tutorial-client-vm \
        --scopes https://www.googleapis.com/auth/devstorage.read_only \
        --image-project cos-cloud \
        --image-family cos-stable
    

    Container Registry からイメージをダウンロードするには、devstorage.read_only スコープが必要です。

  3. Istio ILB ゲートウェイの IP アドレスを ilb-ip.txt というファイルに保存します。

    kubectl -n istio-system get services istio-ilbgateway \
        -o jsonpath='{.status.loadBalancer.ingress[0].ip}' > ilb-ip.txt
    
  4. 自己署名 TLS 証明書と Istio ILB ゲートウェイの IP アドレスを含むファイルを VM にコピーします。

    gcloud compute scp cert.pem ilb-ip.txt grpc-istio-ilb-tutorial-client-vm:~
    
    
  5. SSH で VM に接続します。

    gcloud compute ssh grpc-istio-ilb-tutorial-client-vm
    
  6. VM で、Compute Engine メタデータ サーバーにプロジェクト ID をクエリし、それを環境変数 GOOGLE_CLOUD_PROJECT に保存します。

    GOOGLE_CLOUD_PROJECT=$(curl -sH "Metadata-Flavor: Google" \
        http://metadata.google.internal/computeMetadata/v1/project/project-id)
    

    Cloud Shell では、環境変数 GOOGLE_CLOUD_PROJECT がデフォルトで設定されていますが、VM ではデフォルト設定されていません。

  7. docker コマンドで使用する Container Registry の認証情報を取得します。

    docker-credential-gcr configure-docker
    
  8. gRPC クライアント コンテナ イメージを実行します。

    docker run --rm -v $(pwd)/cert.pem:/data/cert.pem \
        --add-host grpc.example.com:$(cat ilb-ip.txt) \
        gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-client \
        --address=grpc.example.com:443
    

    出力は次のようになります。

    2019/03/27 15:12:53 Hello world from greeter-844cffd75-ffccl
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-zww6h
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-7hpcb
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-ffccl
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-zww6h
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-7hpcb
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-ffccl
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-zww6h
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-7hpcb

    この出力は、Istio 宛先ルールの loadBalancer 構成(この場合は ROUND_ROBIN)に従い、gRPC 単項リクエストが gRPC サーバー Pod(名前は greeter-*)によって処理されることを示します。

  9. SSH セッションを終了します。

    exit
    

ソースコードを調べる

負荷分散の構成をより深く理解するために、サンプル アプリケーションのソースコードを見ることができます。

たとえば、gRPC クライアントから出力されたメッセージを理解するには、サーバーとクライアントの Go ソースコードを見ます。gRPC サーバーはリクエストを処理するとき、ホスト名を検索し、hostname という名前のレスポンス ヘッダーとして追加します。サーバーは Kubernetes ポッドで実行されているため、ホスト名は Pod の名前になります。

hostname, err := os.Hostname()
if err != nil {
	log.Printf("Unable to get hostname %v", err)
}
if hostname != "" {
	grpc.SendHeader(ctx, metadata.Pairs("hostname", hostname))
}

gRPC クライアントはサーバーからレスポンスを受け取ると hostname ヘッダーを取得してコンソールに出力します。

if len(header["hostname"]) > 0 {
	hostname = header["hostname"][0]
}
log.Printf("%s from %s", r.Message, hostname)

gRPC クライアントによってコンソールに出力される Kubernetes ポッドの名前を理解するには、gRPC サーバーの Istio 構成を見ます。DestinationRule オブジェクトの loadBalancer アルゴリズムとして ROUND_ROBIN が指定されていることに注意します。このアルゴリズムが原因で、受信したリクエストは Kubernetes Deployment のポッドに振り分けられます。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: greeter
spec:
  host: greeter
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    tls:
      mode: ISTIO_MUTUAL

トラブルシューティング

このチュートリアルで問題が発生した場合は、次のドキュメントを確認することをおすすめします。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにする手順は次のとおりです。

  1. Cloud Shell で、GKE クラスタを削除します。

    gcloud container clusters delete grpc-istio-ilb-tutorial --quiet --async
    
  2. Container Registry 内のイメージを削除します。

    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-client \
        --force-delete-tags --quiet
    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-server \
        --force-delete-tags --quiet
    
  3. Compute Engine インスタンスを削除します。

    gcloud compute instances delete grpc-istio-ilb-tutorial-client-vm --quiet
    

次のステップ