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

Alibaba Cloud Service Mesh:動的サブセットルーティング

最終更新日:Mar 11, 2026

静的サブセットルーティングでは、すべての宛先ルール(DestinationRule)に各サブセットを明示的に列挙する必要があります。新しいバージョンがリリースされたり、古いバージョンが廃止されたりすると、誰かが手動でルールを更新する必要があります。動的サブセットルーティングはこのオーバーヘッドを解消します:Service Mesh(ASM)がワークロードのラベルを監視し、エンドポイントを自動的にサブセットにグループ化することで、手動変更なしにルーティングルールを常に最新の状態に保ちます。

以下のエンドツーエンドの例では、マルチバージョンアプリケーションをデプロイし、HTTP ヘッダーに基づいて特定のバージョンおよび環境にリクエストをルーティングしたうえで、ターゲットとなるサブセットが存在しない場合のフォールバック動作を設定します。

動的サブセットの仕組み

標準的な Istio ルーティングでは、宛先ルール(DestinationRule)が固定されたラベルセットで各サブセットを列挙します。バージョンが変更されると、その変更に合わせてルールを更新する必要があります。

動的サブセットは異なるアプローチを取ります。各サブセットを個別に列挙する代わりに、1 つ以上のグループ化キー(例:version および stage)を指定します。ASM はサービスの背後にあるすべてのエンドポイントのラベルを検査し、同じキーと値の組み合わせを持つエンドポイントを自動的に同一のサブセットにグループ化します。

その後、仮想サービス(VirtualService)が着信リクエストのヘッダーをこれらのグループ化キーにマップします。たとえば、x-version: v2 および x-stage: prod を含むリクエストは、version=v2 および stage=prod の条件を満たすサブセットにルーティングされます。

前提条件

開始する前に、以下の条件を満たしていることを確認してください。

ステップ 1:サンプルアプリケーションのデプロイ

この例では、hashicorp/http-echo を使用して、複数の環境およびバージョンにわたるデプロイメントをシミュレートします。

  • dev 環境:バージョン v1、v2、v3

  • prod 環境:バージョン v2、v3

各 Pod は、自身の環境、バージョン、および IP アドレスを応答として返します。helloworld サービス(ポート 8000)がすべての Pod をフロントエンドとして提供し、sleep デプロイメントがテスト用のクライアントとして機能します。

Deployment topology

すべてのリソースを kubectl でデプロイします。詳細については、「ASM インスタンスに追加済みの ACK クラスターへのアプリケーションのデプロイ」をご参照ください。

dev 環境

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-dev-v1
  labels:
    app: helloworld
    version: v1
    stage: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
      version: v1
      stage: dev
  template:
    metadata:
      labels:
        app: helloworld
        version: v1
        stage: dev
    spec:
      containers:
      - name: helloworld
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
        env:
        - name: PODIP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: STAGE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['stage']
        - name: VERSION
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['version']
        command: ["/http-echo"]
        args:
          - "-text"
          - "Welcome to helloworld stage: $(STAGE), version: $(VERSION), ip: $(PODIP)"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-dev-v2
  labels:
    app: helloworld
    version: v2
    stage: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
      version: v2
      stage: dev
  template:
    metadata:
      labels:
        app: helloworld
        version: v2
        stage: dev
    spec:
      containers:
      - name: helloworld
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
        env:
        - name: PODIP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: STAGE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['stage']
        - name: VERSION
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['version']
        command: ["/http-echo"]
        args:
          - "-text"
          - "Welcome to helloworld stage: $(STAGE), version: $(VERSION), ip: $(PODIP)"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-dev-v3
  labels:
    app: helloworld
    version: v3
    stage: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
      version: v3
      stage: dev
  template:
    metadata:
      labels:
        app: helloworld
        version: v3
        stage: dev
    spec:
      containers:
      - name: helloworld
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
        env:
        - name: PODIP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: STAGE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['stage']
        - name: VERSION
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['version']
        command: ["/http-echo"]
        args:
          - "-text"
          - "Welcome to helloworld stage: $(STAGE), version: $(VERSION), ip: $(PODIP)"

prod 環境

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-prod-v2
  labels:
    app: helloworld
    version: v2
    stage: prod
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
      version: v2
      stage: prod
  template:
    metadata:
      labels:
        app: helloworld
        version: v2
        stage: prod
    spec:
      containers:
      - name: helloworld
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
        env:
        - name: PODIP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: STAGE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['stage']
        - name: VERSION
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['version']
        command: ["/http-echo"]
        args:
          - "-text"
          - "Welcome to helloworld stage: $(STAGE), version: $(VERSION), ip: $(PODIP)"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-prod-v3
  labels:
    app: helloworld
    version: v3
    stage: prod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: helloworld
      version: v3
      stage: prod
  template:
    metadata:
      labels:
        app: helloworld
        version: v3
        stage: prod
    spec:
      containers:
      - name: helloworld
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
        env:
        - name: PODIP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: STAGE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['stage']
        - name: VERSION
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['version']
        command: ["/http-echo"]
        args:
          - "-text"
          - "Welcome to helloworld stage: $(STAGE), version: $(VERSION), ip: $(PODIP)"

helloworld サービス

apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
    service: helloworld
spec:
  ports:
  - port: 8000
    name: http
    targetPort: 5678
  selector:
    app: helloworld

sleep クライアント

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      terminationGracePeriodSeconds: 0
      serviceAccountName: sleep
      containers:
      - name: sleep
        image: curlimages/curl
        command: ["/bin/sleep", "infinity"]
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - mountPath: /etc/sleep/tls
          name: secret-volume
      volumes:
      - name: secret-volume
        secret:
          secretName: sleep-secret
          optional: true

ステップ 2:特定のバージョンおよび環境へのリクエストのルーティング

デプロイ後、Kubernetes はバージョンや環境に関係なく、すべての helloworld Pod 間でリクエストを負荷分散します。特定の組み合わせをターゲットにするには、グループ化キーを定義する宛先ルール(DestinationRule)と、リクエストヘッダーをこれらのキーにマップする仮想サービス(VirtualService)を作成します。

宛先ルールの作成

以下の宛先ルールを適用して、stage および version に基づいてエンドポイントをグループ化します。詳細については、「宛先ルールの管理」をご参照ください。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: helloworld
  namespace: default
spec:
  host: helloworld.default.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      dynamicSubset:
        subsetSelectors:
          - fallbackPolicy: NO_FALLBACK
            keys:
              - stage
              - version

ASM は各エンドポイントの stage および version ラベルを読み取り、以下のサブセットを生成します。

サブセットPodIP アドレス
stage=dev, version=v1helloworld-dev-v1-67b6876778-nf7pz192.168.0.5
stage=dev, version=v2helloworld-dev-v2-68f65bbc99-v957l192.168.0.1
stage=dev, version=v3helloworld-dev-v3-7f6978bc56-hqzgg192.168.0.252
stage=prod, version=v2helloworld-prod-v2-b5745b949-p8rc4192.168.0.103
stage=prod, version=v3helloworld-prod-v3-6768bf56f8-6bd6h192.168.0.104, 192.168.0.6

仮想サービスの作成

以下の仮想サービスを適用して、HTTP ヘッダーを動的サブセットキーにマップします。詳細については、「仮想サービスの管理」をご参照ください。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: helloworld
  namespace: default
spec:
  hosts:
    - helloworld.default.svc.cluster.local
  http:
    - headerToDynamicSubsetKey:
        - header: x-ip
          key: '%ip%'
      name: default
      route:
        - destination:
            host: helloworld.default.svc.cluster.local
            port:
              number: 8000

この仮想サービスは、以下の 2 つのマッピングを確立します。

  • x-version ヘッダーを version グループ化キーにマップ。デフォルト:ヘッダーが存在しない場合の値は v3

  • x-stage ヘッダーを stage グループ化キーにマップ。デフォルト:ヘッダーが存在しない場合の値は prod

ルーティングの確認

dev 環境、バージョン v1 をターゲットとするリクエストを送信します。

kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: dev' -H 'x-version: v1' helloworld:8000

期待される出力:

Welcome to helloworld stage: dev, version: v1, ip: 192.168.0.5

このリクエストは、stage=dev および version=v1 の条件を満たす Pod に到達し、動的サブセットルーティングが正しく機能していることを確認できます。

ステップ 3:フォールバックポリシーの設定

v1 バージョンは prod 環境には存在しません。stage=prod, version=v1 をターゲットとするリクエストは、どのサブセットにも一致しません。

kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: prod' -H 'x-version: v1' helloworld:8000

一致しないサブセットを処理するには、宛先ルール(DestinationRule)に fallbackPolicy を設定します。ASM では、以下の 3 つのポリシーがサポートされています。

ポリシー動作推奨用途
NO_FALLBACKno healthy upstream エラーを返します。これがデフォルトです。誤ったルーティングに対して即座に失敗させる厳格なルーティング
ANY_ENDPOINTすべてのサブセットにわたる利用可能なエンドポイントのいずれかにルーティングします可用性が精度よりも重要となる開発またはテスト環境
DEFAULT_SUBSET(本番環境向け推奨)事前に設定されたデフォルトサブセットにルーティングします一致しないリクエストが既知の正常なバージョンにルーティングされるべき本番ワークロード

NO_FALLBACK

NO_FALLBACK はデフォルトの動作です。一致するサブセットがないリクエストはエラーを返します。意図的に即時失敗させる場合は、明示的にこのポリシーを設定することを推奨します。

以下の宛先ルールを適用します。詳細については、「宛先ルールの管理」をご参照ください。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: helloworld
  namespace: default
spec:
  host: helloworld.default.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      dynamicSubset:
        subsetSelectors:
          - fallbackPolicy: NO_FALLBACK
            keys:
              - stage
              - version

確認を行います。

kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: prod' -H 'x-version: v1' helloworld:8000

期待される出力:

no healthy upstream

ANY_ENDPOINT

一致するサブセットがない場合、ラベルに関係なく、利用可能なエンドポイントのいずれかにリクエストがルーティングされます。

以下の宛先ルールを適用します。詳細については、「宛先ルールの管理」をご参照ください。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: helloworld
  namespace: default
spec:
  host: helloworld.default.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      dynamicSubset:
        subsetSelectors:
          - fallbackPolicy: ANY_ENDPOINT
            keys:
              - stage
              - version

2 回のリクエストを送信して確認します。それぞれ異なる Pod に到達する可能性があります。

# 最初のリクエスト
kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: prod' helloworld:8000

例の出力:

Welcome to helloworld stage: prod, version: v2, ip: 192.168.0.103
# 2 回目のリクエスト — 存在しないサブセットをターゲット
kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: prod' -H 'x-version: v1' helloworld:8000

例の出力:

Welcome to helloworld stage: dev, version: v2, ip: 192.168.0.1

prod/v1 サブセットが存在しないため、リクエストはランダムなエンドポイントにフォールバックします。

DEFAULT_SUBSET

一致するサブセットがない場合、リクエストは defaultSubset で定義されたサブセットにルーティングされます。これは、一致しないリクエストが失敗したりランダムに分散したりするのではなく、既知の正常なバージョンに到達すべき本番ワークロード向けに推奨されるポリシーです。

以下の宛先ルールを適用します。この例では、defaultSubsetstage=prod, version=v3 を指しています。詳細については、「宛先ルールの管理」をご参照ください。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: helloworld
  namespace: default
spec:
  host: helloworld.default.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      dynamicSubset:
        defaultSubset:
          stage: prod
          version: v3
        subsetSelectors:
          - fallbackPolicy: DEFAULT_SUBSET
            keys:
              - stage
              - version

prod/v1 という存在しないサブセットをターゲットとした 2 回のリクエストを送信して確認します。

kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: prod' -H 'x-version: v1' helloworld:8000

期待される出力:

Welcome to helloworld stage: prod, version: v3, ip: 192.168.0.6
kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: prod' -H 'x-version: v1' helloworld:8000

期待される出力:

Welcome to helloworld stage: prod, version: v3, ip: 192.168.0.104

両方のリクエストは prod/v3 のデフォルトサブセットに到達します。2 つの異なる IP アドレス(192.168.0.6 および 192.168.0.104)は、2 つの prod-v3 レプリカ間での負荷分散を反映しています。

ステップ 4:IP アドレスによる特定の Pod へのルーティング

リクエストを個別の Pod に固定するには、組み込みの %ip% 属性をグループ化キーとして使用します。各 Pod は、単一メンバーのサブセットとなります。

宛先ルールの作成

以下の宛先ルールを適用して、Pod を IP アドレスでグループ化します。詳細については、「宛先ルールの管理」をご参照ください。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: helloworld
  namespace: default
spec:
  host: helloworld.default.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      dynamicSubset:
        defaultSubset:
          stage: prod
          version: v3
        subsetSelectors:
          - keys:
              - '%ip%'

仮想サービスの作成

x-ip ヘッダーを %ip% キーにマップし、ヘッダーの値でターゲット Pod の IP アドレスを指定します。詳細については、「仮想サービスの管理」をご参照ください。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: helloworld
  namespace: default
spec:
  hosts:
    - helloworld.default.svc.cluster.local
  http:
    - headerToDynamicSubsetKey:
        - header: x-ip
          key: '%ip%'
      name: default
      route:
        - destination:
            host: helloworld.default.svc.cluster.local
            port:
              number: 8000

ルーティングの確認

Pod の IP アドレス 192.168.0.6 をターゲットとする複数のリクエストを送信します。

kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-ip: 192.168.0.6' helloworld:8000

期待される出力(繰り返し呼び出しでも一貫):

Welcome to helloworld stage: prod, version: v3, ip: 192.168.0.6

すべてのリクエストが同一の Pod に到達し、IP ベースのピン留めが正しく機能していることを確認できます。

CRD フィールドリファレンス

仮想サービス(VirtualService)

HTTPRoute

ASM は HTTPRoute リソースを headerToDynamicSubsetKey フィールドで拡張しています。

フィールド説明
headerToDynamicSubsetKeyHeaderToMetadataSubsetKey[]リクエストヘッダーを動的サブセットのグループ化キーにマップします。各要素は 1 つのヘッダーからキーへのマッピングを定義します。

HeaderToMetadataSubsetKey

フィールド説明
headerstringリクエストヘッダーの名前。
keystring動的サブセットのグループ化キーの名前。パーセント記号で囲まれた値(例:%ip%)は、組み込みのワークロード属性を参照します。
defaultValuestringリクエストに指定されたヘッダーが存在しない場合に使用される値。省略されており、かつヘッダーが存在しない場合は、キーは未設定となり、サブセットマッチングから除外されます。

宛先ルール(DestinationRule)

ASM は trafficPolicy 構造体を dynamicSubset フィールドで拡張しています。

TrafficPolicy

フィールド説明
dynamicSubsetDynamicSubsetLB動的サブセットのグループ化ルールを設定します。

DynamicSubsetLB

フィールド説明
defaultSubsetmap[string]stringfallbackPolicyDEFAULT_SUBSET であり、リクエストに一致するサブセットがない場合に使用されるデフォルトサブセット。
subsetSelectorsSubsetSelector[]グループ化ルールのリスト。各要素は独立したグループ化ディメンションを定義します。
fallbackPolicyDynamicSubsetLB_FallbackPolicy一致するサブセットがない場合のグローバルフォールバックポリシー。デフォルトは NO_FALLBACK です。

SubsetSelector

フィールド説明
keysstring[]ワークロードラベルにマップされるグループ化ディメンション。たとえば、versionversion ラベルに基づいてエンドポイントをグループ化します。%ip% などの組み込みワークロード属性もサポートされます。
fallbackPolicyDynamicSubsetLB_FallbackPolicyこの特定のグループ化ルールに対するフォールバックポリシー。DynamicSubsetLB で設定されたポリシーを上書きします。

DynamicSubsetLB_FallbackPolicy

説明
NO_FALLBACK一致するサブセットがない場合にエラーを返します。これがデフォルトです。
ANY_ENDPOINT一致するサブセットがない場合に、サービスの任意のエンドポイントにルーティングします。
DEFAULT_SUBSET一致するサブセットがない場合に、defaultSubset で定義されたサブセットにルーティングします。

組み込みワークロード属性

属性説明
%ip%stringワークロードが実行される Pod の IP アドレス。

関連ドキュメント