全部產品
Search
文件中心

Alibaba Cloud Service Mesh:通過配置ASMHeaderPropagation實現寬鬆模式的流量泳道

更新時間:Jun 30, 2024

本文介紹Baggage作為鏈路透傳的要求標頭時,如何通過虛擬服務以及ASMHeaderPropagation等流量規則資源的配置實現寬鬆模式的流量泳道和流量降級。

前提條件

功能介紹

Baggage是OpenTelemetry推出的一種標準化機制,旨在實現分布式系統調用鏈路中跨進程傳遞上下文資訊。它通過在HTTP頭部增加名為“Baggage”的欄位實現,欄位值為索引值對格式,可傳遞租戶ID、追蹤ID、安全憑證等上下文資料,支援鏈路追蹤、日誌關聯等功能而無需修改代碼。例如:

baggage: userId=alice,serverNode=DF%2028,isProduction=false

基於透傳的Baggage上下文資訊,服務網格ASM可以利用ASMHeaderPropagation資源協助您在服務調用鏈路上透傳任意要求標頭、並基於該要求標頭實現寬鬆模式的流量泳道。有關寬鬆模式的流量泳道,請參見流量泳道概述

步驟一:佈建服務透傳Baggage上下文

本節主要展示如何通過OpenTelemetry Operator自動插裝的方法,為Kubernetes叢集中的服務添加Baggage透傳能力。

  1. 部署OpenTelemetry Operator。

    1. 通過kubectl串連到ASM執行個體添加的Kubernetes叢集。執行以下命令,建立opentelemetry-operator-system命名空間。

      kubectl create namespace opentelemetry-operator-system
    2. 執行以下命令,使用Helm在opentelemetry-operator-system命名空間下安裝OpenTelemetry Operator。(關於Helm安裝步驟,請參見安裝Helm

      helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
      helm install  \
          --namespace=opentelemetry-operator-system \
          --version=0.46.0 \
          --set admissionWebhooks.certManager.enabled=false \
          --set admissionWebhooks.certManager.autoGenerateCert=true \
          --set manager.image.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/opentelemetry-operator" \
          --set manager.image.tag="0.92.1" \
          --set kubeRBACProxy.image.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/kube-rbac-proxy" \
          --set kubeRBACProxy.image.tag="v0.13.1" \
          --set manager.collectorImage.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/opentelemetry-collector" \
          --set manager.collectorImage.tag="0.97.0" \
          --set manager.opampBridgeImage.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/operator-opamp-bridge" \
          --set manager.opampBridgeImage.tag="0.97.0" \
          --set manager.targetAllocatorImage.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/target-allocator" \
          --set manager.targetAllocatorImage.tag="0.97.0" \
          --set manager.autoInstrumentationImage.java.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-java" \
          --set manager.autoInstrumentationImage.java.tag="1.32.1" \
          --set manager.autoInstrumentationImage.nodejs.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-nodejs" \
          --set manager.autoInstrumentationImage.nodejs.tag="0.49.1" \
          --set manager.autoInstrumentationImage.python.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-python" \
          --set manager.autoInstrumentationImage.python.tag="0.44b0" \
          --set manager.autoInstrumentationImage.dotnet.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/autoinstrumentation-dotnet" \
          --set manager.autoInstrumentationImage.dotnet.tag="1.2.0" \
          --set manager.autoInstrumentationImage.go.repository="registry-cn-hangzhou.ack.aliyuncs.com/acs/opentelemetry-go-instrumentation" \
          --set manager.autoInstrumentationImage.go.tag="v0.10.1.alpha-2-aliyun" \
          opentelemetry-operator open-telemetry/opentelemetry-operator
    3. 執行以下命令,檢查opentelemetry-operator是否正常運行。

      kubectl get pod -n opentelemetry-operator-system

      預期輸出:

      NAME                                      READY   STATUS    RESTARTS   AGE
      opentelemetry-operator-854fb558b5-pvllj   2/2     Running   0          1m
  2. 配置自動插裝(auto-instrumentation)。

    1. 使用以下內容,建立instrumentation.yaml檔案。

      apiVersion: opentelemetry.io/v1alpha1
      kind: Instrumentation
      metadata:
        name: demo-instrumentation
      spec:
        propagators:
          - baggage
        sampler:
          type: parentbased_traceidratio
          argument: "1"
    2. 執行以下命令,在default命名空間下聲明自動插裝。

      kubectl apply -f instrumentation.yaml
      說明

      對於OpenTelemetry架構來說,其最佳實務還包括部署OpenTelemetry Collector以收集可觀測資料。由於本文主要示範OpenTelemetry自動插裝實現的Baggage鏈路透傳,因此沒有包含部署OpenTelemetry Collector的步驟。有關服務網格ASM如何通過OpenTelemetry上報鏈路追蹤資料,請參考見將鏈路追蹤資料擷取到阿里雲可觀測鏈路OpenTelemetry版

步驟二:部署樣本服務

  1. 為default命名空間啟用Sidecar網格代理自動注入。具體操作,請參見管理全域命名空間

    說明

    關於自動注入的更多資訊,請參見配置Sidecar注入策略

  2. 使用以下內容,建立mock.yaml檔案。

    展開查看mock.yaml內容

    apiVersion: v1
    kind: Service
    metadata:
      name: mocka
      labels:
        app: mocka
        service: mocka
    spec:
      ports:
      - port: 8000
        name: http
      selector:
        app: mocka
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mocka-v1
      labels:
        app: mocka
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mocka
          version: v1
          ASM_TRAFFIC_TAG: v1
      template:
        metadata:
          labels:
            app: mocka
            version: v1
            ASM_TRAFFIC_TAG: v1
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v1
            - name: app
              value: mocka
            - name: upstream_url
              value: "http://mockb:8000/"
            ports:
            - containerPort: 8000
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: mockb
      labels:
        app: mockb
        service: mockb
    spec:
      ports:
      - port: 8000
        name: http
      selector:
        app: mockb
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mockb-v1
      labels:
        app: mockb
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mockb
          version: v1
          ASM_TRAFFIC_TAG: v1
      template:
        metadata:
          labels:
            app: mockb
            version: v1
            ASM_TRAFFIC_TAG: v1
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v1
            - name: app
              value: mockb
            - name: upstream_url
              value: "http://mockc:8000/"
            ports:
            - containerPort: 8000
    
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: mockc
      labels:
        app: mockc
        service: mockc
    spec:
      ports:
      - port: 8000
        name: http
      selector:
        app: mockc
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mockc-v1
      labels:
        app: mockc
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mockc
          version: v1
          ASM_TRAFFIC_TAG: v1
      template:
        metadata:
          labels:
            app: mockc
            version: v1
            ASM_TRAFFIC_TAG: v1
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v1
            - name: app
              value: mockc
            ports:
            - containerPort: 8000
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mocka-v2
      labels:
        app: mocka
        version: v2
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mocka
          version: v2
          ASM_TRAFFIC_TAG: v2
      template:
        metadata:
          labels:
            app: mocka
            version: v2
            ASM_TRAFFIC_TAG: v2
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v2
            - name: app
              value: mocka
            - name: upstream_url
              value: "http://mockb:8000/"
            ports:
            - containerPort: 8000
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mockb-v2
      labels:
        app: mockb
        version: v2
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mockb
          version: v2
          ASM_TRAFFIC_TAG: v2
      template:
        metadata:
          labels:
            app: mockb
            version: v2
            ASM_TRAFFIC_TAG: v2
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v2
            - name: app
              value: mockb
            - name: upstream_url
              value: "http://mockc:8000/"
            ports:
            - containerPort: 8000
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mockc-v2
      labels:
        app: mockc
        version: v2
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mockc
          version: v2
          ASM_TRAFFIC_TAG: v2
      template:
        metadata:
          labels:
            app: mockc
            version: v2
            ASM_TRAFFIC_TAG: v2
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v2
            - name: app
              value: mockc
            ports:
            - containerPort: 8000
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mocka-v3
      labels:
        app: mocka
        version: v3
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mocka
          version: v3
          ASM_TRAFFIC_TAG: v3
      template:
        metadata:
          labels:
            app: mocka
            version: v3
            ASM_TRAFFIC_TAG: v3
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v3
            - name: app
              value: mocka
            - name: upstream_url
              value: "http://mockb:8000/"
            ports:
            - containerPort: 8000
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mockb-v3
      labels:
        app: mockb
        version: v3
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mockb
          version: v3
          ASM_TRAFFIC_TAG: v3
      template:
        metadata:
          labels:
            app: mockb
            version: v3
            ASM_TRAFFIC_TAG: v3
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v3
            - name: app
              value: mockb
            - name: upstream_url
              value: "http://mockc:8000/"
            ports:
            - containerPort: 8000
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mockc-v3
      labels:
        app: mockc
        version: v3
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mockc
          version: v3
          ASM_TRAFFIC_TAG: v3
      template:
        metadata:
          labels:
            app: mockc
            version: v3
            ASM_TRAFFIC_TAG: v3
          annotations:
            instrumentation.opentelemetry.io/inject-java: "true"
            instrumentation.opentelemetry.io/container-names: "default"
        spec:
          containers:
          - name: default
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
            imagePullPolicy: IfNotPresent
            env:
            - name: version
              value: v3
            - name: app
              value: mockc
            ports:
            - containerPort: 8000

    對於每個執行個體服務Pod,都加入了instrumentation.opentelemetry.io/inject-java: "true"instrumentation.opentelemetry.io/container-names: "default"兩個註解,以聲明該執行個體服務使用Java語言實現,並要求OpenTelemetry Operator對名稱為default的容器進行自動插裝。

  3. 執行以下命令,部署執行個體服務。

    kubectl apply -f mock.yaml

    基於OpenTelemetry自動插裝機制,部署的服務Pod將自動具有在調用鏈路中傳遞Baggage的能力。

步驟三:建立流量規則實現寬鬆模式流量泳道

  1. 建立DestinationRule流量規則。

    1. 使用以下內容,建立dr-mock.yaml檔案。

      展開查看YAML內容

      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: dr-mock-default-mocka
      spec:
        host: mocka.default.svc.cluster.local
        subsets:
          - labels:
              version: v1
            name: v1
          - labels:
              version: v2
            name: v2
          - labels:
              version: v3
            name: v3
      ---
      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: dr-mock-default-mockb
      spec:
        host: mockb.default.svc.cluster.local
        subsets:
          - labels:
              version: v1
            name: v1
          - labels:
              version: v3
            name: v3
      ---
      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: dr-mock-default-mockc
      spec:
        host: mockc.default.svc.cluster.local
        subsets:
          - labels:
              version: v1
            name: v1
          - labels:
              version: v2
            name: v2

      此檔案表示將mocka、mockb、mockc三個服務按照Pod的version標籤分為v1、v2、v3三個版本子集,其中mocka有v1、v2、v3三個版本,mockb有v1、v3兩個版本,mockc有v1、v2兩個版本。

    2. 通過kubectl串連ASM執行個體,執行以下命令,建立DestinationRule流量規則。

      kuebctl apply -f dr-mock.yaml
  1. 建立ASMHeaderPropagation流量規則。服務已經實現了Baggage透傳能力的前提下,可以通過ASMHeaderPropagation流量規則來指定利用Baggage在鏈路上透傳自訂要求標頭。

    1. 使用以下內容,建立propagation.yaml檔案。以下檔案表示利用Baggage中的上下文資訊,在調用鏈路中透傳名為version的要求標頭。

      apiVersion: istio.alibabacloud.com/v1beta1
      kind: ASMHeaderPropagation
      metadata:
        name: version-propagation
      spec:
        headers:
          - version
    2. 通過kubectl串連ASM執行個體,執行以下命令, 建立ASMHeaderPropagation流量規則。

      kuebctl apply -f propagation.yaml
  1. 建立VirtualService流量規則。

    1. 使用以下內容,建立vs-mock.yaml檔案。

      展開查看YAML內容

      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: vs-mock-default-mocka
      spec:
        hosts:
          - mocka.default.svc.cluster.local
        http:
          - match:
              - headers:
                  version:
                    exact: v1
            route:
              - destination:
                  host: mocka.default.svc.cluster.local
                  subset: v1
                fallback:
                  target:
                    host: mocka.default.svc.cluster.local
                    subset: v1
          - match:
              - headers:
                  version:
                    exact: v2
            route:
              - destination:
                  host: mocka.default.svc.cluster.local
                  subset: v2
                fallback:
                  target:
                    host: mocka.default.svc.cluster.local
                    subset: v1
          - match:
              - headers:
                  version:
                    exact: v3
            route:
              - destination:
                  host: mocka.default.svc.cluster.local
                  subset: v3
                fallback:
                  target:
                    host: mocka.default.svc.cluster.local
                    subset: v1
      ---
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: vs-mock-default-mockb
      spec:
        hosts:
          - mockb.default.svc.cluster.local
        http:
          - match:
              - headers:
                  version:
                    exact: v1
            route:
              - destination:
                  host: mockb.default.svc.cluster.local
                  subset: v1
                fallback:
                  target:
                    host: mockb.default.svc.cluster.local
                    subset: v1
          - match:
              - headers:
                  version:
                    exact: v2
            route:
              - destination:
                  host: mockb.default.svc.cluster.local
                  subset: v2
                fallback:
                  target:
                    host: mockb.default.svc.cluster.local
                    subset: v1
          - match:
              - headers:
                  version:
                    exact: v3
            route:
              - destination:
                  host: mockb.default.svc.cluster.local
                  subset: v3
                fallback:
                  target:
                    host: mockb.default.svc.cluster.local
                    subset: v1
      ---
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: vs-mock-default-mockc
      spec:
        hosts:
          - mockc.default.svc.cluster.local
        http:
          - match:
              - headers:
                  version:
                    exact: v1
            route:
              - destination:
                  host: mockc.default.svc.cluster.local
                  subset: v1
                fallback:
                  target:
                    host: mockc.default.svc.cluster.local
                    subset: v1
          - match:
              - headers:
                  version:
                    exact: v2
            route:
              - destination:
                  host: mockc.default.svc.cluster.local
                  subset: v2
                fallback:
                  target:
                    host: mockc.default.svc.cluster.local
                    subset: v1
          - match:
              - headers:
                  version:
                    exact: v3
            route:
              - destination:
                  host: mockc.default.svc.cluster.local
                  subset: v3
                fallback:
                  target:
                    host: mockc.default.svc.cluster.local
                    subset: v1
      

      此檔案為mocka→mockb→mockc的服務調用鏈路建立流量泳道路由規則。具體來說,是通過匹配調用鏈路中透傳的version要求標頭內容、將請求轉寄到對應的版本(例如,帶有version: v2要求標頭的請求都發送到v2版本的服務)。同時,該流量規則也指定了流量回退規則:當調用鏈路中的某個服務的對應版本不存在時,統一將請求回退到服務的v1版本。

    2. 通過kubectl串連ASM執行個體,執行以下命令,建立VirtualService流量規則。

      kubectl apply -f vs-mock.yaml
  2. 建立網關引流規則。

    1. 使用以下內容,建立gw-mock.yaml檔案。

      展開查看YAML內容

      apiVersion: networking.istio.io/v1beta1
      kind: Gateway
      metadata:
        name: mockgw
        namespace: default
      spec:
        selector:
          istio: ingressgateway
        servers:
          - hosts:
              - '*'
            port:
              name: http
              number: 80
              protocol: HTTP
      ---
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: swimlane-ingress-vs-weighted-mock
        namespace: default
      spec:
        gateways:
          - default/mockgw
        hosts:
          - '*'
        http:
          - name: mock-weighted
            route:
              - destination:
                  host: mocka.default.svc.cluster.local
                  subset: v1
                headers:
                  request:
                    set:
                      version: v1
                weight: 40
              - destination:
                  host: mocka.default.svc.cluster.local
                  subset: v2
                headers:
                  request:
                    set:
                      version: v2
                weight: 30
              - destination:
                  host: mocka.default.svc.cluster.local
                  subset: v3
                headers:
                  request:
                    set:
                      version: v3
                weight: 30

      此檔案表示為mocka→mockb→mockc的服務調用鏈路建立網關引流規則。引流方式則為權重引流,對於發送到ASM網關的流量,以4:3:3的比例分別轉寄到mocka服務的v1、v2、v3版本。當網關向mocka服務轉寄請求時,會根據轉寄的目標版本為請求添加version要求標頭,保證調用鏈路中存在泳道對應的版本資訊。

    2. 通過kubectl串連ASM執行個體,執行以下命令,建立網關引流規則。

      kubectl apply -f gw-mock.yaml

步驟四:驗證流量泳道是否生效

  1. 擷取ASM網關的公網IP。具體操作,請參見步驟二:擷取ASM網關地址

  2. 執行以下命令,設定環境變數。xxx.xxx.xxx.xxx為上一步擷取的IP。

    export ASM_GATEWAY_IP=xxx.xxx.xxx.xxx
  3. 驗證全灰階鏈路功能是否生效。

    1. 執行以下命令,查看v1泳道的訪問效果。

      for i in {1..100};  do curl http://${ASM_GATEWAY_IP} ;  echo ''; sleep 1; done;

      預期輸出:

      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1)
      -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1)
      -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1)
      -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v2, ip: 192.168.1.28)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v2, ip: 192.168.1.1)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v1, ip: 192.168.1.27)-> mockb(version: v1, ip: 192.168.1.30)-> mockc(version: v1, ip: 192.168.1.14)
      -> mocka(version: v3, ip: 192.168.1.26)-> mockb(version: v3, ip: 192.168.1.29)-> mockc(version: v1, ip: 192.168.1.14)

      可以看到,流量將以約4:3:3的比例發送到服務調用鏈路的v1、v2、v3三個版本,並由v1作為基準版本,當調用鏈路中不存在某個服務的特定版本時,將會調用該服務的v1版本。