プロキシレス gRPC サービスを使用した高度なトラフィック管理を構成する

このドキュメントでは、次のトラフィック管理機能を使用して Traffic Director を構成する方法について説明します。

  • ルート マッチング
  • トラフィック分割
  • サーキット ブレーカー
  • フォールト インジェクション
  • 最大ストリーミング期間
  • 再試行
  • セッション アフィニティ

このドキュメントでは、Compute Engine でのプロキシレス gRPC を使用した高度なトラフィック管理の設定を中心に説明しますが、Google Kubernetes Engine(GKE)でプロキシレス gRPC を使用する場合は、高度なトラフィック管理もサポートされています。

始める前に

高度なトラフィック管理を構成する前に、プロキシレス gRPC サービスを使用した Traffic Director の設定の準備の要件をご確認ください。すべての要件が満たされていなければ、高度なトラフィック管理を構成することはできません。

これらの機能のコンセプトに関する情報については、高度なトラフィック管理をご覧ください。

gRPC Wallet の例について

gRPC Wallet のサンプルのデプロイを例に、この機能を説明します。この例では、3 つの gRPC サービス(grpc.examples.wallet.Walletgrpc.examples.wallet.stats.Statsgrpc.examples.wallet.account.Account)があり、それぞれが個別のアプリケーションとしてデプロイされています。

次の図に示すように、Wallet を呼び出して口座の残高を取得し、Stats サービスを使用してコインの価格を取得する gRPC クライアントを作成します。Wallet サービスは、StatsAccount サービスを呼び出して残高を計算します。また、Stats サービスは Account サービスを呼び出してユーザー情報を取得します。

gRPC Wallet のトラフィック ルーティング構成の例。
gRPC Wallet のトラフィック ルーティング構成の例(クリックして拡大)

この例では、構成したルールに基づいたリクエストのルーティングを説明するため、WalletStats の実装の 2 つのバージョンをデプロイします。異なるバージョンのサービスのビルドとデプロイをシミュレートするには、サーバーフラグを使用して、1 回のみビルドするバイナリの動作を変更します。

  • --port フラグには、VM インスタンス上のサービスがリッスンするポートを指定します。
  • --hostname_suffix フラグには、リクエストに応答する VM インスタンスのホスト名に追加される値を指定します。生成される値は、レスポンスの hostname メタデータとして追加されます。これは、クライアント リクエストに応答したインスタンス グループ内のインスタンスの識別に役立ちます。
  • --premium_only フラグに値 true を指定すると、サービスが stats サービスのプレミアム バージョンになります。
  • --v1_behavior フラグに値 true を指定すると、ウォレットのバイナリが v1 バージョンとして動作します。

次の表に、gRPC サービスのいずれかを実行している各 VM インスタンス用のこれらのフラグの値、インスタンス グループ内のインスタンス数、これらのインスタンス グループが属するバックエンド サービスを示します。

バックエンド サービス インスタンス グループ インスタンス サーバーフラグ
account account 2 --port=50053
--hostname_suffix="account"
stats stats 2 --port=50052
--hostname_suffix="stats"
--account_server="xds:///account.grpcwallet.io"
stats-premium stats-premium 2 --port=50052
--hostname_suffix="stats_premium"
--account_server="xds:///account.grpcwallet.io"
--premium_only=true
wallet-v1
wallet-v1-affinity
wallet-v1 2 --port=50051
--hostname_suffix="wallet_v1"
--v1_behavior=true
--account_server="xds:///account.grpcwallet.io"
--stats_server="xds:///stats.grpcwallet.io"
wallet-v2 wallet-v2 1 --port=50051
--hostname_suffix "wallet_v2"
--account_server="xds:///account.grpcwallet.io"
--stats_server="xds:///stats.grpcwallet.io"

これらのサービスをデプロイした後、次の表のルーティング ルールに基づいて、テスト クライアントから上に示したバックエンド サービスにリクエストをルーティングするように Traffic Director を構成します。クライアントは、ホスト列に示されているサービスの仮想ホスト名に接続します。

ホスト 一致ルール ルート アクション
wallet.grpcwallet.io パス接頭辞: "/"
ヘッダー: "session_id"
wallet-v1-affinity にルーティングする
パス接頭辞: "/"
ヘッダー: {"route": "timeout"}
5 秒のタイムアウトを設定して
wallet-v2 にルーティングする
パス接頭辞: "/"
ヘッダー: {"route": "fault"}
50% のリクエストが失敗し
残りを wallet-v2 にルーティングする
パス接頭辞: "/"
ヘッダー: {"membership": "premium"}
wallet-v1 にルーティングし
失敗時に最大 3 回再試行する
フルパス: /grpc.examples.wallet.Wallet/FetchBalance ルーティング先:
wallet-v1: 70%
wallet-v2: 30%
デフォルト wallet-v1 にルーティングする
stats.grpcwallet.io パス接頭辞: "/"
ヘッダー: {"membership": "premium"}
stats-premium にルーティングする
デフォルト stats にルーティングする
account.grpcwallet.io パス接頭辞: "/"
ヘッダー: {"route": "account-fault"}
30% のリクエストが失敗し
残りを account にルーティングする
デフォルト account

この例では、2 つの既存のサービス間で 70/30 のトラフィック分割を使用します。URL マップでまだ参照されていない新しいサービスにトラフィックを分割する場合は、まず新しいサービスを weightedBackendServices に追加し、それに 0 の重みを設定します。次に、そのサービスに割り当てられた重みを徐々に増やしていきます。

テスト クライアントには、トラフィック管理機能を実証するために適切なリクエストを生成できるようにする次のオプションがあります。

オプション 説明
--watch=true ストリーミング メソッドを呼び出して、残高または価格を確認する
--unary_watch=true 単項メソッドを繰り返し呼び出して、残高または価格を確認する
--user=Alice ヘッダー {"membership": "premium"} を挿入する
--user=Bob ヘッダー {"membership": "normal"} を挿入する
--route=value ヘッダー {"route": "value"} を挿入する
--affinity=true ヘッダー {"session_id": "value"} を挿入する

ローカルの環境を準備する

これらの例でローカル環境を設定するには、次のコマンドを実行します。

  1. gcloud バイナリを更新して、最新バージョンであることを確認します。

    gcloud components update
    
  2. サンプル リポジトリをダウンロードします。

    sudo apt-get install git -y
    
  3. サンプルに適切なリポジトリのクローンを作成します。

    export EXAMPLES_VERSION=v1.1.x
    git clone -b $EXAMPLES_VERSION --single-branch --depth=1 \
      https://github.com/GoogleCloudPlatform/traffic-director-grpc-examples.git
    

Cloud Router インスタンスを作成して構成する

このセクションでは、各リージョンに Cloud Router インスタンスを作成し、Cloud NAT 用に構成します。この例で作成される VM には外部 IP アドレスはありませんが、インターネットにアクセスする必要があります。Cloud NAT を使用して Cloud Router を構成すると、必要なアクセス権が提供されます。

gcloud

  1. Cloud Router インスタンスを作成します。

    gcloud compute routers create nat-router-us-central1 \
        --network=default \
        --region=us-central1
    
  2. Cloud NAT のルーターを構成します。

    gcloud compute routers nats create nat-config \
        --router-region=us-central1 \
        --router=nat-router-us-central1 \
        --nat-all-subnet-ip-ranges \
        --auto-allocate-nat-external-ips
    

gRPC ヘルスチェックとファイアウォール ルールを作成する

このセクションでは、gRPC ヘルスチェック リクエストがネットワークへアクセスすることを許可する gRPC ヘルスチェックとファイアウォール ルールを作成します。その後、gRPC ヘルスチェックはバックエンド サービスに関連付けられ、これらのバックエンド サービスのバックエンド インスタンスの健全性を確認できます。

gcloud

  1. ヘルスチェックを作成します。

    gcloud compute health-checks create grpc grpcwallet-health-check \
        --use-serving-port
    
  2. ヘルスチェック用のファイアウォール ルールを作成します。

    gcloud compute firewall-rules create grpcwallet-allow-health-checks \
        --network default --action allow --direction INGRESS \
        --source-ranges 35.191.0.0/16,130.211.0.0/22 \
        --target-tags allow-health-checks \
        --rules tcp:50051-50053
    

インスタンス テンプレートを作成する

このセクションでは、ポート 50053 で公開されている account gRPC サービスをデプロイするインスタンス テンプレートを作成します。

gcloud

  • インスタンス テンプレートを作成します。

    gcloud compute instance-templates create grpcwallet-account-template \
       --scopes=https://www.googleapis.com/auth/cloud-platform \
       --tags=allow-health-checks \
       --network-interface=no-address \
       --image-family=debian-10 \
       --image-project=debian-cloud \
       --metadata-from-file=startup-script=<(echo "#! /bin/bash
    set -ex
    cd /root
    export HOME=/root
    sudo apt-get update -y
    pushd \$(mktemp -d)
    sudo apt-get install -y wget git
    wget https://dl.google.com/go/go1.16.5.linux-amd64.tar.gz
    sudo tar -C /usr/local -xvf go1.16.5.linux-amd64.tar.gz
    sudo cp /usr/local/go/bin/go /usr/bin/go
    popd
    git clone -b $EXAMPLES_VERSION --single-branch --depth=1 https://github.com/GoogleCloudPlatform/traffic-director-grpc-examples.git
    cd traffic-director-grpc-examples/go/account_server/
    go build .
    sudo systemd-run ./account_server --port 50053 --hostname_suffix account")
    

マネージド インスタンス グループを作成します。

マネージド インスタンス グループ(MIG)は、自動スケーリングを使用して、必要に応じて新しい VM インスタンスを作成します。このセクションでは、前のセクションで作成したインスタンス テンプレートを使用して MIG を作成します。

gcloud

  • インスタンス グループを作成します。

    gcloud compute instance-groups managed create grpcwallet-account-mig-us-central1 \
       --zone=us-central1-a \
       --size=2 \
       --template=grpcwallet-account-template
    

名前付きポートを構成する

このセクションでは、gRPC サービスの名前付きポートを構成します。名前付きポートとは、gRPC サービスがリクエストをリッスンするポートです。この例では、名前付きポートはポート 50053 です。

gcloud

  • 名前付きポートを作成します。

    gcloud compute instance-groups set-named-ports grpcwallet-account-mig-us-central1 \
       --named-ports=grpcwallet-account-port:50053 \
       --zone=us-central1-a
    

バックエンド サービスを作成する

このセクションでは、INTERNAL_SELF_MANAGED とプロトコル GRPC の負荷分散方式でグローバル バックエンド サービスを作成します。次に、バックエンド サービスにヘルスチェックとインスタンス グループを関連付けます。この例では、マネージド インスタンス グループの作成で作成した MIG を使用します。この MIG は account gRPC サービスを実行します。--port-name フラグのポートは、名前付きポートの構成で作成した名前付きポートです。

gcloud

  1. バックエンド サービスを作成し、新しいバックエンド サービスにヘルスチェックを関連付けます。

    gcloud compute backend-services create grpcwallet-account-service \
        --global \
        --load-balancing-scheme=INTERNAL_SELF_MANAGED \
        --protocol=GRPC \
        --port-name=grpcwallet-account-port \
        --health-checks=grpcwallet-health-check
    
  2. マネージド インスタンス グループをバックエンドとして追加します。

    gcloud compute backend-services add-backend grpcwallet-account-service \
        --instance-group=grpcwallet-account-mig-us-central1 \
        --instance-group-zone=us-central1-a \
        --global
    

gRPC Wallet の例で使用されている残りのバックエンド サービスを作成する手順は、前述の手順と同様です。シェル スクリプトを実行して残りのサービスを作成します。このスクリプトは、次のバックエンド サービスをデプロイします。

  • stats
  • stats-premium
  • wallet-v1
  • wallet-v1-affinity
  • wallet-v2

追加のバックエンド サービスを作成するシェル スクリプトを実行します。

traffic-director-grpc-examples/scripts/create_service.sh go stats 50052 stats '--account_server="xds:///account.grpcwallet.io"'

traffic-director-grpc-examples/scripts/create_service.sh go stats 50052 stats-premium '--account_server="xds:///account.grpcwallet.io" --premium_only=true'

# This command creates wallet-v1 and wallet-v1-affinity backend services.
traffic-director-grpc-examples/scripts/create_service.sh java wallet 50051 wallet-v1 '--account_server="xds:///account.grpcwallet.io" --stats_server="xds:///stats.grpcwallet.io" --v1_behavior=true'

traffic-director-grpc-examples/scripts/create_service.sh java wallet 50051 wallet-v2 '--account_server="xds:///account.grpcwallet.io" --stats_server="xds:///stats.grpcwallet.io"'

ルーティング ルールを作成する

このセクションでは、さまざまなトラフィック管理機能を実証するために使用される URL マップを作成します。URL マップでは、この例のサービスの仮想ホスト名と、関連するルーティング ルールを指定します。詳細については、ルーティング ルールマップをご覧ください。

URL マップでは、この例のサービスのホスト名を hostRules で指定します。これらのホスト名は、特定のサービスに接続するためにクライアントがチャンネル URI で使用する名前です。たとえば、account サービスにリクエストを送信する場合は、クライアントがチャンネル URI で xds:///account.grpcwallet.io を使用します。hostRules で、account.grpcwallet.io という値を持つ hosts エントリを構成します。

hosts エントリに関連付けられた pathMatcher は、その仮想ホストのすべてのルーティング ルールを含む pathMatcher の名前を指定します。pathMatcher 構成は、gRPC Wallet の例についてで説明されているように、マッチング ルールと対応するアクション ルールで構成されます。

gcloud

URL マップを作成します。

export PROJECT_ID=$(gcloud config list --format 'value(core.project)')
export BS_PREFIX=projects/$PROJECT_ID/global/backendServices/grpcwallet

gcloud compute url-maps import grpcwallet-url-map << EOF
name: grpcwallet-url-map
defaultService: $BS_PREFIX-account-service

hostRules:
- hosts:
  - account.grpcwallet.io
  pathMatcher: grpcwallet-account-path-matcher
- hosts:
  - stats.grpcwallet.io
  pathMatcher: grpcwallet-stats-path-matcher
- hosts:
  - wallet.grpcwallet.io
  pathMatcher: grpcwallet-wallet-path-matcher

pathMatchers:
- name: grpcwallet-account-path-matcher
  defaultService: $BS_PREFIX-account-service
  routeRules:
  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: route
        exactMatch: account-fault
    priority: 0
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-account-service
        weight: 100
      faultInjectionPolicy:
        abort:
          httpStatus: 503
          percentage: 30

- name: grpcwallet-stats-path-matcher
  defaultService: $BS_PREFIX-stats-service
  routeRules:
  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: membership
        exactMatch: premium
    priority: 0
    service: $BS_PREFIX-stats-premium-service

- name: grpcwallet-wallet-path-matcher
  defaultService: $BS_PREFIX-wallet-v1-service
  routeRules:
  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: session_id
        presentMatch: true
    priority: 0
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v1-affinity-service
        weight: 100

  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: route
        exactMatch: timeout
    priority: 1
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 100
      maxStreamDuration:
        seconds: 5

  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: route
        exactMatch: fault
    priority: 2
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 100
      faultInjectionPolicy:
        abort:
          httpStatus: 503
          percentage: 50

  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: membership
        exactMatch: premium
    priority: 3
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v1-service
        weight: 100
      retryPolicy:
        retryConditions:
          - unavailable
        numRetries: 3

  - matchRules:
    - fullPathMatch: /grpc.examples.wallet.Wallet/FetchBalance
    priority: 4
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v1-service
        weight: 70
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 30

  - matchRules:
    - prefixMatch: /grpc.examples.wallet.Wallet/
    priority: 5
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 100
EOF

ターゲット プロキシと転送ルールを作成する

このセクションでは、ターゲット gRPC プロキシと転送ルールを作成します。

ターゲット gRPC プロキシは、前のステップで作成した URL マップを参照します。--validate-for-proxyless フラグを指定すると、構成チェックが有効になり、プロキシレス gRPC デプロイと互換性のない機能が誤って有効にならないようにできます。

gcloud

  • ターゲット gRPC プロキシを作成します。

    gcloud compute target-grpc-proxies create grpcwallet-proxy \
       --url-map=grpcwallet-url-map \
       --validate-for-proxyless
    

作成したターゲット gRPC プロキシが転送ルールによって参照されます。負荷分散スキームが INTERNAL_SELF_MANAGED に設定され、この転送ルールが Traffic Director によって使用されていることを示します。これはグローバル転送ルールである必要があります。プロキシレス gRPC クライアントは、DNS ルックアップを実行する代わりに LDS リクエストを Traffic Director に送信することで、ターゲット URI で hostname:port を解決するため、IP アドレスは 0.0.0.0 に設定されます。詳細については、名前解決スキームをご覧ください。

ターゲット URI にポートが指定されていない場合、デフォルト値は 80 です。たとえば、ターゲット URI xds:///foo.myservice:8080 は、ポート 8080 を使用して構成された転送ルールと一致します。この例では、転送ルールはポート 80 を使用して構成されています。

gcloud

  • 転送ルールを作成します。

    gcloud compute forwarding-rules create grpcwallet-forwarding-rule \
       --global \
       --load-balancing-scheme=INTERNAL_SELF_MANAGED \
       --address=0.0.0.0 \
       --address-region=us-central1 \
       --target-grpc-proxy=grpcwallet-proxy \
       --ports=80 \
       --network=default
    

構成を確認する

構成プロセスが完了したら、Google Cloud Console の [Traffic Director] ページで、構成したバックエンド サービスが使用可能であることを確認します。バックエンド サービスと関連するバックエンドが正常であると報告されていることを確認します。この処理には数分かかることがあります。

ルーティング構成を確認する

このセクションでは、ルーティング構成が正しく機能していることを確認します。grpcurl ツールを使用して構成をテストします。

gcloud

  1. クライアントを実行するクライアント VM を作成し、サービスをテストします。 必要に応じて --network-interface=no-address フラグを指定できます。

    gcloud compute instances create grpc-wallet-client \
        --zone=us-central1-a \
        --scopes=https://www.googleapis.com/auth/cloud-platform \
        --image-family=debian-10 \
        --image-project=debian-cloud \
        --metadata-from-file=startup-script=<(echo '#! /bin/bash
    set -e
    export GRPC_XDS_BOOTSTRAP=/run/td-grpc-bootstrap.json
    # Expose bootstrap variable to SSH connections
    echo export GRPC_XDS_BOOTSTRAP=$GRPC_XDS_BOOTSTRAP | sudo tee /etc/profile.d/grpc-xds-bootstrap.sh
    # Create the bootstrap file
    curl -L https://storage.googleapis.com/traffic-director/td-grpc-bootstrap-0.11.0.tar.gz | tar -xz
    ./td-grpc-bootstrap-0.11.0/td-grpc-bootstrap | sudo tee $GRPC_XDS_BOOTSTRAP')
    
  2. SSH を使用して VM にアクセスし、次のコマンドを実行して VM を準備します。

    export EXAMPLES_VERSION=v1.1.x
    sudo apt-get update
    sudo apt-get install git -y
    
  3. 次のコマンドを実行します。

    git clone -b $EXAMPLES_VERSION --single-branch --depth=1 \
       https://github.com/GoogleCloudPlatform/traffic-director-grpc-examples.git
    curl -L https://github.com/fullstorydev/grpcurl/releases/download/v1.6.1/grpcurl_1.6.1_linux_x86_64.tar.gz | tar -xz
    chmod +x grpcurl
    

サイドカー プロキシなしでサービスにアクセスするには、gRPC クライアントが xds 名前解決スキームを使用する必要があります。このスキームは、クライアントで使用される gRPC ライブラリに、xDS サーバーを使用してホスト名を解決するように指示します。これを実行するには、ブートストラップの構成が必要です。

前のセクションの起動スクリプトで GRPC_XDS_BOOTSTRAP 環境変数を設定し、ヘルパー スクリプトを使用してブートストラップ ファイルを生成します。生成されたブートストラップ ファイル内の TRAFFICDIRECTOR_GCP_PROJECT_NUMBERTRAFFICDIRECTOR_NETWORK_NAME、ゾーンの値は、Compute Engine VM インスタンスの詳細を把握しているメタデータ サーバーから取得されます。これらの値は、-gcp-project-number オプションと -vpc-network-name オプションを使用してヘルパー スクリプトに手動で指定できます。

grpcurl ツールを使用して構成を確認する

SSH シェルで次のコマンドを実行して、wallet-servicestats-serviceaccount-service サービスが実行されていることを確認します。

./grpcurl -plaintext xds:///account.grpcwallet.io list
./grpcurl -plaintext -d '{"token": "2bd806c9"}' xds:///account.grpcwallet.io grpc.examples.wallet.account.Account/GetUserInfo
./grpcurl -plaintext -H 'authorization:2bd806c9' -H 'membership:premium' xds:///stats.grpcwallet.io grpc.examples.wallet.stats.Stats/FetchPrice
./grpcurl -plaintext -H 'authorization:2bd806c9' -H 'membership:premium' -d '{"include_balance_per_address": true}' xds:///wallet.grpcwallet.io grpc.examples.wallet.Wallet/FetchBalance

次の結果が表示されます。

grpc.examples.wallet.account.Account
grpc.health.v1.Health
grpc.reflection.v1alpha.ServerReflection

{
  "name": "Alice",
  "membership": "PREMIUM"
}

{
  "price": "10295"
}

{
  "balance": "5089953"
}

grpc-wallet クライアントでの確認

次の言語別の手順で構成を確認します。一連のコマンドで複数の RPC(追加のメタデータを含む)が送信され、URL マップのマッチング ルールに基づいてバックエンド サービスにリクエストがルーティングされます。各レスポンスの VM インスタンスのホスト名も出力されます。これは、リクエストがルーティングされた VM インスタンスを示します。

Java

  1. gRPC Java クライアントでサービスを確認するには、次のコマンドを実行します。

    sudo apt-get install -y openjdk-11-jdk-headless
    
  2. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/java
    ./gradlew installDist
    
    # This command calls 'FetchBalance' from 'wallet-service' in a loop,
    # to demonstrate that 'FetchBalance' gets responses from 'wallet-v1' (70%)
    # and 'wallet-v2' (30%).
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob
    
    # This command calls the streaming RPC 'WatchBalance' from 'wallet-service'.
    # The RPC path matches the service prefix, so all requests are
    # sent to 'wallet-v2'.
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
    # This command calls 'WatchPrice' from 'stats-service'. It sends the
    # user's membership (premium or not) in metadata. Premium requests are
    # all sent to 'stats-premium' and get faster responses. Alice's requests
    # always go to premium and Bob's go to regular.
    ./build/install/wallet/bin/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Bob
    ./build/install/wallet/bin/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Alice
    

Go

  1. gRPC Go クライアントでサービスを確認するには、golang をインストールするか、公式の手順に従います。

    sudo apt-get install -y wget
    wget https://dl.google.com/go/go1.16.5.linux-amd64.tar.gz
    sudo tar -C /usr/local -xvf go1.16.5.linux-amd64.tar.gz
    sudo ln -s /usr/local/go/bin/go /usr/bin/go
    
  2. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/go/wallet_client
    go build
    
    # This command calls 'FetchBalance' from 'wallet-service' in a loop,
    # to demonstrate that 'FetchBalance' gets responses from 'wallet-v1' (70%)
    # and 'wallet-v2' (30%).
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch --user=Bob
    
    # This command calls the streaming RPC 'WatchBalance' from 'wallet-service'.
    # The RPC path matches the service prefix, so all requests
    # are sent to 'wallet-v2'.
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob
    
    # This command calls 'WatchPrice' from 'stats-service'. It sends the
    # user's membership (premium or not) in metadata. Premium requests are
    # all sent to 'stats-premium' and get faster responses. Alice's requests
    # always go to premium and Bob's go to regular.
    ./wallet_client price --stats_server="xds:///stats.grpcwallet.io" --watch --user=Bob
    ./wallet_client price --stats_server="xds:///stats.grpcwallet.io" --watch --user=Alice
    

C++

  1. gRPC C++ クライアントでサービスを確認するには、次のコマンドを実行します。

    sudo apt-get install -y build-essential
    
  2. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/cpp
    ../tools/bazel build :client
    
    # This command calls 'FetchBalance' from 'wallet-service' in a loop,
    # to demonstrate that 'FetchBalance' gets responses from 'wallet-v1' (70%)
    # and 'wallet-v2' (30%).
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob
    
    # This command calls the streaming RPC 'WatchBalance'  from 'wallet-service'.
    # The RPC path matches the service prefix, so all requests are sent to 'wallet-v2'.
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
    # This command calls 'WatchPrice' from 'stats-service'. It sends the
    # user's membership (premium or not) in metadata. Premium requests are
    # all sent to 'stats-premium' and get faster responses. Alice's requests
    # always go to premium, and Bob's go to regular.
    ../bazel-bin/cpp/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Bob
    ../bazel-bin/cpp/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Alice
    

より高度なオプションを構成する

以降のセクションの手順を使用すると、追加の高度なトラフィック ルーティング オプションを構成できます。

サーキット ブレーカー

クライアント リクエストでバックエンド サービスが過負荷にならないように、サーキット ブレークで障害しきい値を設定できます。未処理のリクエスト数が設定した上限に達すると、クライアントは追加のリクエストの送信を停止し、バックエンド サービスの復旧のための時間を確保します。

バックエンド サービスで過負荷を発生させずにエラーをクライアントに返すことで、カスケード障害を回避できます。これにより、自動スケーリングによって容量を増やしてトラフィックの急増に対応するなど、過負荷状態を管理する時間を確保しつつ、一部のトラフィックを処理できます。

stats-service のバックエンド サービスを作成する場合、create_service.sh スクリプトの構成には次の行が含まれます。

circuitBreakers:
  maxRequests: 1

この設定では、クライアントが一度に stats-service に保留できるリクエストは 1 つに制限されます。wallet-v2 サービスは 1 つのみ存在するため、2 つのウォレット クライアントで WatchBalance オペレーションを同時実行すると、2 番目のインスタンスで障害が発生します。

Java

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob  2>/dev/null 1>/dev/null &
    
    sleep 10 # Wait a few seconds to allow the watch to begin.
    
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. 出力は次のようになります。

    io.grpc.examples.wallet.Client run
    INFO: Will try to run balance
    io.grpc.examples.wallet.Client run
    WARNING: RPC failed: Status{code=INTERNAL, description=RPC to stats server failed: UNAVAILABLE: Cluster max concurrent requests limit exceeded, cause=null}
    
  3. kill コマンドを発行します。

    kill %%
    

Go

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob 2> /dev/null 1> /dev/null &
    
    sleep 10 # Wait a few seconds to allow the watch to begin.
    
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob
    
  2. 出力は次のようになります。

    server host: error: no hostname
    failed to fetch balance: rpc error: code = Internal desc = RPC to stats server failed:
    UNAVAILABLE: Cluster max concurrent requests limit exceeded
    
  3. kill コマンドを発行します。

    kill %%
    

C++

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob 2>/dev/null 1>/dev/null &
    
    sleep 10 # Wait a few seconds to allow the watch to begin.
    
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. 出力は次のようになります。

    Client arguments: command: balance, wallet_server: xds:///wallet.grpcwallet.io, stats_server:
    localhost:18882, user: Bob, watch: 1 ,unary_watch: 0, observability_project: , route:
    13: RPC to stats server failed: UNAVAILABLE: Cluster max concurrent requests limit exceeded
    
  3. kill コマンドを発行します。

    kill %%
    

フォールト インジェクション

フォールト インジェクションは、高レイテンシ、サービス過負荷、サービス障害、ネットワーク パーティショニングなどの障害をシミュレートするリクエストを処理する際にエラーを発生させます。この機能は、サービスの復元力を疑似的にテストするために利用できます。

以前に URL マップを作成したときに、route=fault ヘッダーを使用して wallet.grpcwallet.io に送信される RPC の 50% が失敗するフォールト インジェクション ポリシーを設定しました。

フォールト インジェクションを実証するには、次の言語でコードを使用します。

Java

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=fault
    
  2. 出力は次のようになります。

    server host: grpcwallet-wallet-v2-mig-us-central1-zznc
    total balance: 10340491
    - address: 148de9c5, balance: 2549839
    - address: 2e7d2c03, balance: 7790652
    io.grpc.examples.wallet.Client run
    WARNING: RPC failed: Status{code=UNAVAILABLE, description=RPC terminated due to fault injection: HTTP status code 503, cause=null}
    

Go

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch --user=Bob --route=fault
    
  2. 出力は次のようになります。

     server host: grpcwallet-wallet-v1-mig-us-central1-bm1t_wallet-v1
     user: Bob, total grpc-coin balance: 10452589.
      - address: 2e7d2c03, balance: 7875108.
      - address: 148de9c5, balance: 2577481.
     failed to fetch balance: rpc error: code = Unavailable desc = RPC terminated due to fault injection
    

C++

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=fault
    
  2. 出力は次のようになります。

    Client arguments: command: balance, wallet_server: xds:///wallet.grpcwallet.io,
    stats_server: localhost:18882, user: Bob, watch: 0 ,unary_watch: 1, observability_project: ,
    route: fault server host: grpcwallet-wallet-v2-mig-us-central1-1lm6
    user: Bob total grpc-coin balance: 10211908
     - address: 148de9c5, balance: 2518132
     - address: 2e7d2c03, balance: 7693776
    14: Fault injected
    

最大ストリーミング期間

最大ストリーミング期間により、すべての RPC に最大タイムアウトを適用できます。これにより、クライアントが期限の設定を忘れたり、長すぎる期限を設定することで、サーバー リソースを無駄にすることがなくなります。

上の URL マップを作成した際に、route=timeout ヘッダーを使用して、wallet.grpcwallet.io に送信される RPC の最大ストリーミング期間を 5 秒に設定しました。

タイムアウトを確認するには、まず wallet-v2 サービスを停止します。

gcloud

gcloud compute instance-groups managed resize \
    --size=0 grpcwallet-wallet-v2-mig-us-central1 \
    --zone=us-central1-a

処理するバックエンド サービスが存在せず、アプリケーションが期限を設定しないため、次のコマンドは恒久的に停止します。

Java

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. コマンドがレスポンスを停止します。コマンドを中断するには、^C キーを押してください。

Go

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob
    
  2. コマンドがレスポンスを停止します。コマンドを中断するには、^C キーを押してください。

C++

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. コマンドがレスポンスを停止します。コマンドを中断するには、^C キーを押してください。

ただし、timeout ルートを使用した次のコマンドは、maxStreamDuration 設定により 5 秒後に失敗します。

Java

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob --route=timeout
    
  2. 出力は次のようになります。

    io.grpc.examples.wallet.Client run
    WARNING: RPC failed: Status{code=DEADLINE_EXCEEDED, description=deadline exceeded after 4.999594070s.         [wait_for_ready, buffered_nanos=5000553401, waiting_for_connection], cause=null}
    

Go

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob --route=timeout
    
  2. 出力は次のようになります。

    failed to create stream: rpc error: code = DeadlineExceeded desc = context deadline exceeded.
    

C++

  1. 次のコマンドを実行します。

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob --route=timeout
    
  2. 出力は次のようになります。

    Client arguments: command: balance, wallet_server: xds:///wallet.grpcwallet.io, stats_server:
    localhost:18882, user: Bob, watch: 1 ,unary_watch: 0, observability_project: , route: timeout
    4: Deadline Exceeded
    

wallet-v2 サービスを再起動します。

gcloud

gcloud compute instance-groups managed resize \
    --size=1 grpcwallet-wallet-v2-mig-us-central1 \
    --zone=us-central1-a

再試行

再試行は、gRPC アプリケーションがポリシーに従って送信リクエストを再試行できるようにすることで、サービスの可用性の向上に役立ちます。再試行ポリシーでは、失敗したリクエストを再試行する条件と、再試行回数の上限を構成できます(たとえば、リクエストが特定のレスポンス コードで失敗する場合など)。

以前に URL マップを作成したときに、membership=premium ヘッダーを使用して RPC の再試行ポリシーを FetchBalance メソッドに設定しました。このポリシーは、ステータス コード unavailable で失敗する RPC を最大で 3 回再試行します。また、Wallet サービスから Account サービスへの RPC の 30% を失敗させる route=account-fault ヘッダーを使用して、RPC のフォールト インジェクション ポリシーを account.grpcwallet.io に設定しました。その結果、membership=normal ヘッダーを使用するテスト クライアントからの RPC の 30% は失敗しますが、membership=premium ヘッダーを使用する RPC の失敗率は 1% 未満になります。

再試行を実証するには、次の言語でコードを使用します。

Java

次のコマンドを実行します。

cd ~/traffic-director-grpc-examples/java
# 30% of the requests fail because Bob is a normal user.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=account-fault
# Less than 1% of the requests fail because Alice is a premium user.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --route=account-fault

Go

次のコマンドを実行します。

cd ~/traffic-director-grpc-examples/go/wallet_client
# 30% of the requests fail because Bob is a normal user.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=account-fault
# Less than 1% of the requests fail because Alice is a premium user.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --route=account-fault

C++

次のコマンドを実行します。

cd ~/traffic-director-grpc-examples/cpp
# 30% of the requests fail because Bob is a normal user.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=account-fault
# Less than 1% of the requests fail because Alice is a premium user.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --route=account-fault

セッション アフィニティ

セッション アフィニティでは、インスタンスが良好な状態で処理能力があれば、特定の特性(HTTP ヘッダー)を持つリクエストを同じインスタンスに送信するための試行をベストエフォート型で行います。これは、特定のクライアントからのリクエストが異なるインスタンスにラウンドロビン分散されるのではなく、同じインスタンスに送信される場合に、パフォーマンスと効率性が向上するステートフル アプリケーション サーバーにとって有用です。

バックエンド サービス grpcwallet-wallet-v1-affinity-service を作成したときに、create_service.sh スクリプトで localityLbPolicyROUND_ROBIN に設定されていました。この例では、次の構成を適用して localityLbPolicyRING_HASH に変更します。

sessionAffinity: HEADER_FIELD
localityLbPolicy: RING_HASH
consistentHash:
  httpHeaderName: "session_id"

gcloud

  1. バックエンド サービス grpcwallet-wallet-v1-affinity-service の構成を保存します。

    gcloud compute backend-services export grpcwallet-wallet-v1-affinity-service \
     --destination=bs_config.yaml \
     --global
    
  2. バックエンド サービス grpcwallet-wallet-v1-affinity-service を更新します。

    project_id="$(gcloud config list --format 'value(core.project)')"
    backend_config="
    backends:
    - balancingMode: UTILIZATION
      capacityScaler: 1.0
      group: projects/${project_id}/zones/us-central1-a/instanceGroups/grpcwallet-wallet-v1-mig-us-central1
    connectionDraining:
      drainingTimeoutSec: 0
    healthChecks:
    - projects/${project_id}/global/healthChecks/grpcwallet-health-check
    loadBalancingScheme: INTERNAL_SELF_MANAGED
    name: grpcwallet-wallet-v1-affinity-service
    portName: grpcwallet-wallet-port
    protocol: GRPC
    sessionAffinity: HEADER_FIELD
    localityLbPolicy: RING_HASH
    consistentHash:
      httpHeaderName: session_id"
    gcloud compute backend-services import grpcwallet-wallet-v1-affinity-service --global <<< "${backend_config}"
    

セッション アフィニティを確認するには、次の言語のコードを使用します。--affinity=true フラグを指定すると、クライアントはユーザーごとに一意の値を持つヘッダー session-id を挿入します。この値のハッシュは、バックエンド サービス grpcwallet-wallet-v1-affinity-service のインスタンス グループ内の特定のインスタンスにリクエストを送信するために使用されます。

Java

次のコマンドを実行します。

cd ~/traffic-director-grpc-examples/java
# Without affinity, requests are sent to both instances.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice
# With affinity, requests are sent to only one instance.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --affinity=true

Go

次のコマンドを実行します。

cd ~/traffic-director-grpc-examples/go/wallet_client
# Without affinity, requests are sent to both instances.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice
# With affinity, requests are sent to only one instance.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --affinity=true

C++

次のコマンドを実行します。

cd ~/traffic-director-grpc-examples/cpp
# Without affinity, requests are sent to both instances.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice
# With affinity, requests are sent to only one instance.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --affinity=true

バックエンド サービス grpcwallet-wallet-v1-affinity-service の構成を復元します。

gcloud

gcloud compute backend-services import grpcwallet-wallet-v1-affinity-service \
  --source=bs_config.yaml \
  --global

リソースをクリーンアップする

リソースをクリーンアップするには、ローカル システムから次のコマンドを実行します。

traffic-director-grpc-examples/scripts/cleanup.sh

次のステップ