すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Service Mesh:カスタム仮想サービスを使用して緩和モードのトラフィックレーンのトラフィックをルーティングする

最終更新日:Jan 13, 2025

サービスメッシュ(ASM)では、カスタム仮想サービスを作成することで、さまざまな複雑なリクエストルーティングルールを柔軟に定義できます。 リクエストルーティングルールは、トラフィックレーンと組み合わせて使用することで、包括的かつ多次元のトラフィック管理を実装し、アプリケーションの可観測性、操作性、およびユーザーエクスペリエンスを最大化できます。 このトピックでは、カスタム仮想サービスを作成して、緩和モードのトラフィックレーンのトラフィックをルーティングする方法について説明します。

重要

この例では、[シナリオ 1: トレース内のトレース ID をパススルーする] および [シナリオ 2: トレース内のカスタムリクエストヘッダーをパススルーする] の手順を変更して、カスタム仮想サービスを作成し、緩和モードでトラフィックレーンのトラフィックをルーティングできます。 [緩和モードでトラフィックレーンを使用してエンドツーエンドのトラフィックを管理する] のトピックと関連コンテンツを既に読んで理解していることを確認してください。

シナリオ

ASM コンソールには、組み込みのリクエストルーティングルール作成機能があります。 トラフィックレーンに対応する ASMイングレスゲートウェイで仮想サービスを作成すると、トラフィックレーン用のリクエストルーティングルールが作成されます。 これにより、ASMイングレスゲートウェイは、リクエストを異なるトラフィックレーンに転送できます。

トラフィックレーンのリクエストルーティングルールには、リクエストヘッダーとリクエストパスの照合ルールが含まれます。 カスタム仮想サービスを作成して、より複雑な照合ルールを定義したり、カスタムリクエストルートを作成したりできます。

説明

カスタム仮想サービスを使用してトラフィックレーンのトラフィックをルーティングする場合は、組み込みのリクエストルーティングルール作成機能を使用してトラフィックレーンのリクエストルーティングルールを作成しないことをお勧めします。 これは、カスタム仮想サービスが、組み込みのリクエストルーティングルール作成機能を使用して作成されたリクエストルーティングルールと競合する可能性があるためです。 その結果、異常なトラフィック分散や予期しないトラフィック分散が発生する可能性があります。

ASMイングレスゲートウェイでカスタム仮想サービスを作成する

  1. [シナリオ 1: トレースでトレース ID をパススルーする] が例です。ステップ 1 のサブステップ 3 [3 つのレーンにドレナージルールを作成する] で、トラフィックルーティングルールを作成するステップを、次のコンテンツを使用して仮想サービスを作成するステップに変更します。詳細については、「[仮想サービスを管理する]」をご参照ください。

    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: swimlane-ingress-vs-custom
      namespace: istio-system
    spec:
      gateways:
        - istio-system/ingressgateway
      hosts:
        - '*'
      http:
        - match: # このマッチングルールは env: dev リクエストヘッダーと完全に一致します。
            - headers:
                env:
                  exact: dev
          name: dev-route
          route:
            - destination:
                host: mocka.default.svc.cluster.local # トラフィック転送の宛先サービス。
                subset: s2 # トラフィックレーンの名前。
              weight: 50
              headers:
                request:
                  set:
                    x-asm-prefer-tag: s2 # トラフィックレーンのリクエストルーティングヘッダーを設定します。
            - destination:
                host: mocka.default.svc.cluster.local # トラフィック転送の宛先サービス。
                subset: s3 # トラフィックレーンの名前。
              fallback:
                target:
                  host: mocka.default.svc.cluster.local
                  subset: s1
              weight: 50
              headers:
                request:
                  set:
                    x-asm-prefer-tag: s3 # トラフィックレーンのリクエストルーティングヘッダーを設定します。
        - name: base-route
          route:
            - destination:
                host: mocka.default.svc.cluster.local # トラフィック転送の宛先サービス。
                subset: s1 # トラフィックレーンの名前。
              headers:
                request:
                  set:
                    x-asm-prefer-tag: s1 # トラフィックレーンのリクエストルーティングヘッダーを設定します。
    説明

    カスタム仮想サービスを作成する場合、上記の YAML ファイルの headers.request.set の値は、対応するリクエストルーティングヘッダーと同じである必要があります。たとえば、シナリオ 2:トレースでカスタムリクエストヘッダーを渡す では、エンドツーエンドのパススルーリクエストヘッダーはリクエストルーティングヘッダーと同じです。そのため、対応する route フィールドの headers.request.set の値は、それぞれ my-trace-id: s1my-trace-id: s2my-trace-id: s3 に変更されます。

  2. 手順 3: エンドツーエンドのカナリアリリース機能が有効になっていることを確認するに記載されているコマンドを実行します。 s1、s2、および s3 レーンのサービスにアクセスすると、次の出力が生成されます。

    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v1, ip: 192.168.0.48)

    env: dev リクエストヘッダーを使用して s1 レーンのサービスにアクセスした場合の結果を確認するには、次のコマンドを実行します。

    for i in {1..100};  do curl -H 'x-asm-prefer-tag: s1' -H 'env: dev' -H'my-trace-id: x000'$i http://${ASM_GATEWAY_IP}/mock ;  echo ''; sleep 1; done;

    予想される出力:

    予想される出力を表示する

    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)

    この出力は、env: dev リクエストヘッダーを使用して s1 レーンのサービスにアクセスすると、v1->v3->v1 と v2->v1->v2 の 2 種類のトレースが関係し、2 種類のトレースのリクエストの比率が約 50:50 であることを示しています。 つまり、env: dev リクエストヘッダーを持つリクエストは、50:50 の比率で s2 レーンと s3 レーンにルーティングされ、残りのリクエストは s1 レーンにルーティングされます。

上記の仮想サービスは、ASM ゲートウェイのカスタムルーティングルールを指定して、トラフィックを異なるトラフィックレーンに転送します。厳格モードでトラフィックレーンのリクエストをルーティングするカスタム仮想サービスを作成する場合、特定のトラフィックレーンにリクエストをルーティングするには、route.destination.subset フィールドをトラフィックレーンの名前に設定するだけで済みます。リクエストがトラフィックレーンにルーティングされると、トレース内の後続のリクエストは常にそのトラフィックレーンにルーティングされます。

サイドカープロキシでカスタム仮想サービスを作成する

ASMイングレスゲートウェイでカスタム仮想サービスを作成することに加えて、クラスター内のアプリケーションがトラフィックレーン内のサービスにアクセスするためのリクエストルーティングルールを、すべてのサイドカープロキシで仮想サービスを作成することによって指定することもできます。 ASMイングレスゲートウェイのカスタム仮想サービスとは異なり、サイドカープロキシの仮想サービスには gateway フィールドは不要です。hosts フィールドに mocka サービスのクラスタードメインを指定する必要があります。 YAML ファイルの例:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: swimlane-ingress-vs-custom
  namespace: istio-system
spec:
  hosts:
    - mocka.default.svc.cluster.local
  http:
    - match: # このマッチングルールは、env: dev リクエストヘッダーと完全に一致します。
        - headers:
            env:
              exact: dev
      name: dev-route
      route:
        - destination:
            host: mocka.default.svc.cluster.local # トラフィック転送の宛先サービス。
            subset: s2 # トラフィックレーンの名前。
          weight: 50
          headers:
            request:
              set:
                x-asm-prefer-tag: s2 # トラフィックレーンのリクエストルーティングヘッダーを構成します。
        - destination:
            host: mocka.default.svc.cluster.local # トラフィック転送の宛先サービス。
            subset: s3 # トラフィックレーンの名前。
          weight: 50
          fallback:
            target:
              host: mocka.default.svc.cluster.local
              subset: s1
          headers:
            request:
              set:
                x-asm-prefer-tag: s3 # トラフィックレーンのリクエストルーティングヘッダーを構成します。
    - name: base-route
      route:
        - destination:
            host: mocka.default.svc.cluster.local # トラフィック転送の宛先サービス。
            subset: s1 # トラフィックレーンの名前。
          headers:
            request:
              set:
                x-asm-prefer-tag: s1 # トラフィックレーンのリクエストルーティングヘッダーを構成します。
説明

ASMイングレスゲートウェイでカスタム仮想サービスを作成する 手順と同様に、上記の YAML ファイルの headers.request.set の値は、対応するリクエストルーティングヘッダーと同じである必要があります。

  1. サイドカープロキシでカスタム仮想サービスを作成した後、env: dev リクエストヘッダーなしで s1、s2、s3 レーンのサービスにアクセスするには、次の 3 つのコマンドを実行します。

    kubectl exec -it deploy/sleep -c sleep --  sh -c 'for i in $(seq 1 100);  do curl -H "my-trace-id: s1" http://mocka:8000;  echo ""; sleep 1; done;'
    kubectl exec -it deploy/sleep -c sleep --  sh -c 'for i in $(seq 1 100);  do curl -H "my-trace-id: s2" http://mocka:8000;  echo ""; sleep 1; done;'
    kubectl exec -it deploy/sleep -c sleep --  sh -c 'for i in $(seq 1 100);  do curl -H "my-trace-id: s3" http://mocka:8000;  echo ""; sleep 1; done;'

    期待される出力:

    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v1, ip: 192.168.0.48)

    env: dev リクエストヘッダーのないすべてのリクエストが s1 レーンに送信されることを示しています。

  2. サイドカープロキシでカスタム仮想サービスを作成した後、env: dev リクエストヘッダーを使用して s1、s2、s3 レーンのサービスにアクセスするには、次の 3 つのコマンドを実行します。

    kubectl exec -it deploy/sleep -c sleep --  sh -c 'for i in $(seq 1 100);  do curl -H "my-trace-id: s1" -H "env: dev" http://mocka:8000;  echo ""; sleep 1; done;'
    kubectl exec -it deploy/sleep -c sleep --  sh -c 'for i in $(seq 1 100);  do curl -H "my-trace-id: s2" -H "env: dev" http://mocka:8000;  echo ""; sleep 1; done;'
    kubectl exec -it deploy/sleep -c sleep --  sh -c 'for i in $(seq 1 100);  do curl -H "my-trace-id: s3" -H "env: dev" http://mocka:8000;  echo ""; sleep 1; done;'

    期待される出力:

    s1 レーンのサービスにアクセスする場合の期待される出力の表示

    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    (snip)
    

    s2 レーンのサービスにアクセスする場合の期待される出力の表示

    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v2, ip: 192.168.0.47)-> mockb(version: v1, ip: 192.168.0.46)-> mockc(version: v2, ip: 192.168.0.43)
    (snip)
    

    s3 レーンのサービスにアクセスする場合の期待される出力の表示

    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    -> mocka(version: v1, ip: 192.168.0.50)-> mockb(version: v3, ip: 192.168.0.42)-> mockc(version: v1, ip: 192.168.0.48)
    (snip)
    

    出力は ASMイングレスゲートウェイでカスタム仮想サービスを作成する の出力と同じです。つまり、クラスター内のサービスがトラフィックレーン内の mocka サービスを呼び出すと、トレース内の後続のリクエストは常にそのトラフィックレーンにルーティングされます。