全部產品
Search
文件中心

Alibaba Cloud Service Mesh:動態子集路由

更新時間:Jun 30, 2024

當您需要對微服務應用中的工作負載進行更精細化的管理和路由控制時,可以使用動態子集路由功能。在多應用多版本發布的情境下,ASM會自動根據應用特徵將工作負載劃分至對應的動態子集,減輕營運人員的手動設定負擔。ASM還支援通過請求的特定Header與子集特徵匹配,實現更靈活的請求路由。

前提條件

已添加叢集到ASM執行個體,且ASM執行個體版本為1.18及以上。

功能介紹

ASM支援通過目標規則DestinationRule將服務下的工作負載通過標籤劃分為不同的子集,並支援聲明路由規則,指向特定的子集。這種使用方式較為簡明,也可以滿足絕大多數情況下的需求。但在一些情境中,這種靜態分組並靜態路由的方式可能不夠便利。例如,在以版本將工作負載劃分為不同的子集的情境中,版本可能是不斷增長的。這要求營運人員在新版本發布或舊版本下線時,新增或移除在DestinationRule中相應的子集配置。在多應用多版本頻繁發布的情境下,這項工作會給營運人員增加一些額外的負擔。

為了提高使用體驗,ASM自1.18版本起,支援動態子集路由。您可以通過指定維度(例如版本),動態地將指定維度值相同的工作負載劃分至同一個動態子集。使用動態子集應對上述情境則無需營運人員手動為每個新版本靜態配置子集。部署工作負載時,ASM會自動地根據應用特徵將其劃分至對應動態子集。同時,ASM支援配置通過請求的特定Header與子集特徵進行匹配,根據請求攜帶的Header將請求路由到指定動態子集。

步驟一:部署應用樣本

本文以hashicorp/http-echo應用為例,部署dev和prod兩套環境,分別類比開發環境和生產環境。在dev環境下部署v1、v2、v3版本,在prod環境下只部署v2、v3版本。helloworld監聽在5678連接埠,收到請求會回複自己的環境和版本。同時,部署服務helloworld,暴露8000連接埠,選中apphelloworld的工作負載。image.png

在ACK叢集對應的KubeConfig環境下,使用以下YAML分別部署helloworld應用dev環境v1、v2和v3版本、prod環境v2和v3版本、helloworld服務以及用於發起測試的sleep應用。關於如何部署應用,請參見在ASM執行個體關聯的叢集中部署應用

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 
        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)"
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
---
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 
        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)"
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
---
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 
        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)"
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678

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 
        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)"
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678
---
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 
        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)"
        image: hashicorp/http-echo:0.2.3
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5678

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

步驟二:訪問指定環境的指定版本

  1. 部署目標規則和虛擬服務。

    1. 使用以下內容,為helloworld服務配置目標規則。具體操作,請參見管理目標規則

      Helloworld應用部署完畢後,在預設情況下,訪問helloworld服務的請求將被K8s負載平衡分配到任意一個helloworld應用Pod。本文為helloworld應用部署2個環境多個版本(dev環境v1、v2和v3版本、prod環境v2和v3版本)。要訪問指定環境和版本,需要為helloworld服務配置目標規則DestinationRule。

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

      以上目標規則為helloworld服務配置了一個以stage和version標籤進行分組的規則。本例中部署的工作負載將被該規則分為以下幾個分組。

      分組

      Pod

      地址

      stage = dev

      version = v1

      helloworld-dev-v1-67b6876778-nf7pz

      192.168.0.5

      stage = dev

      version = v2

      helloworld-dev-v2-68f65bbc99-v957l

      192.168.0.1

      stage = dev

      version = v3

      helloworld-dev-v3-7f6978bc56-hqzgg

      192.168.0.252

      stage = prod

      version = v2

      helloworld-prod-v2-b5745b949-p8rc4

      192.168.0.103

      stage = prod

      version = v3

      helloworld-prod-v3-6768bf56f8-6bd6h

      192.168.0.104

      helloworld-prod-v3-6768bf56f8-6bd6h

      192.168.0.6

    2. 使用以下內容,為helloworld服務建立虛擬服務,建立請求到動態分組key之間的映射關係。具體操作,請參見管理虛擬服務

      展開查看虛擬服務YAML

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

      以上虛擬服務指定:

      • 將請求中名為x-version的Header的值對應到分組名為version的分組key。若請求未攜帶x-version Header,則使用預設值v3

      • 將請求中名為x-stage的Header的值對應到名為stage的分組key。若請求未攜帶x-stage Header,則使用預設值prod

  2. 在ACK叢集對應的KubeConfig環境下,執行以下命令,從sleep應用發起對helloworld應用dev環境的v1版本的訪問。

    關於如何通過kubectl管理叢集,請參見擷取叢集KubeConfig並通過kubectl工具串連叢集

    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標籤為devversion標籤為v1的Pod。

步驟三:為分組配置回退策略

prod環境中未部署v1版本,若嘗試訪問prod環境的v1版本,會因為該分組不存在而報錯。

kubectl exec -it deploy/sleep -c sleep -- curl -H 'x-stage: prod' -H 'x-version: v1' helloworld:8000
預期輸出:
no healthy upstream # 由於分組不存在,產生報錯。

為了避免分組不存在時訪問失敗,您可以為動態分組規則配置回退策略。下文介紹ASM動態分組支援的三種回退策略:NO_FALLBACKANY_ENDPOINTDEFAULT_SUBSET

NO_FALLBACK

為動態分組規則使用該策略,當無法匹配任何分組時,請求將會報no healthy upstream錯誤。

  1. 使用如下目標規則,為stageversion分組規則指定fallbackPolicyNO_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
  2. 執行以下命令,訪問stageprodversionv1的helloworld應用。

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

    預期輸出:

    no healthy upstream # 由於分組不存在,產生報錯。

ANY_ENDPOINT

為動態分組規則使用該策略,當無法匹配任何分組時,請求將被路由至服務下的任意端點。

  1. 使用如下目標規則,為stageversion分組規則指定fallbackPolicyANY_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. 執行以下命令,訪問stageprodversionv1的helloworld應用。

    • 第一次訪問:

      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 # 第一次訪問被路由至prod, v2。
    • 第二次訪問:

      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 # 第二次訪問被路由至dev, v2。

      由預期輸出得到,請求被路由至helloworld服務下的隨機Pod。

DEFAULT_SUBSET

為動態分組規則使用該策略,當無法匹配任何分組時,請求將被路由至defaultSubset指定的key匹配的分組。

  1. 使用如下目標規則,為stageversion分組規則指定fallbackPolicyDEFAULT_SUBSET,並配置defaultSubsetkeyvalue。具體操作,請參見管理目標規則

    key

    value

    stage

    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
  2. 執行以下命令,訪問stageprodversionv1的分組。

    • 第一次訪問:

      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

      由預期輸出得到,由於分組不存在,觸發回退,使用stagprodversionv3匹配分組,符合預期。

步驟四:訪問指定Pod

若您需要訪問到指定Pod,可以通過ASM動態分組內建keys%ip%進行分組,將每個Pod劃分獨立分組。

  1. 使用以下目標規則,新增一個以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%'
  2. 使用如下虛擬服務,將請求的x-ip Header映射到%ip%分組的規則,使請求可以通過x-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
    
  3. 執行以下命令,對Pod helloworld-prod-v3-6768bf56f8-6bd6h進行訪問,指定Pod地址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
    • 第二次訪問:

      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
    • 第三次訪問:

      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地址192.168.0.6,符合預期。

CRD說明

VirtualService

  • HTTPRoute

    ASM對HTTPRoute進行擴充,增加headerToDynamicSubsetKey欄位。

    欄位

    類型

    說明

    headerToDynamicSubsetKey

    HeaderToMetadataSubsetKey[]

    用於配置請求Header到動態子集key的映射。數組中的每一個元素為一個Header到一個key的映射。

  • HeaderToMetadataSubsetKey

    配置請求Header到動態子集key的映射關係或預設值。

    欄位

    類型

    說明

    header

    string

    要求標頭名稱。

    key

    string

    動態子集key名稱,其中%開頭結尾的表示工作負載內建屬性。

    defaultValue

    string

    若請求沒有攜帶key指定的Header,則使用該預設值。如果不配置預設值,則在請求未攜帶該Header時視作該維度缺失,該維度不參與匹配。

DestinationRule

ASM對trafficPolicy結構進行擴充,增加dynamicSubset欄位。

  • TrafficPolicy

    欄位

    類型

    說明

    dynamicSubset

    DynamicSubsetLB

    用於配置動態分組規則。

  • DyunamicSubsetLB

    欄位

    類型

    說明

    defaultSubset

    map[string]string

    用於配置預設分組,當請求無法匹配任何動態分組,且分組規則的回退策略為DEFAULT_SUBSET時,使用該配置聲明的維度值選擇分組。

    subsetSelectors

    SubsetSelector[]

    用於配置動態分組規則,數組中每一項為一個獨立的分組規則。

    fallbackPolicy

    DynamicSubsetLB_FallbackPolicy

    指定無法匹配動態子集時的回退策略。若不指定該配置,則預設為NO_FALLBACK。

  • SubsetSelector

    欄位

    類型

    說明

    keys

    string[]

    分組維度列表,值對應至工作負載標籤。例如,version表示以工作負載的名為version的標籤作為分組維度。除標籤外,還支援以工作負載內建屬性作為維度。更多資訊,請參見工作負載內建屬性

    fallbackPolicy

    DynamicSubsetLB_FallbackPolicy

    指定無法匹配動態子集時的回退策略。若不指定該配置,則使用DyunamicSubsetLB中指定的fallbackPolicy。

  • DynamicSubsetLB_FallbackPolicy

    表示動態子集路由回退策略的枚舉類型,支援的值如下:

    說明

    NO_FALLBACK

    不進行回退。

    ANY_ENDPOINT

    使用服務下任意端點。

    DEFAULT_SUBSET

    使用聲明的預設子集進行回退。

工作負載內建屬性

屬性

類型

說明

%ip%

string

工作負載的Pod地址。

相關文檔

  • 您可以啟用控制平面日誌採集和日誌警示,及時發現和解決潛在的風險。具體操作,請參見啟用控制平面日誌採集和日誌警示

  • 您可以安裝診斷工具asmctl,檢測ASM存在的配置問題。具體操作,請參見安裝和使用診斷工具asmctl

  • 您可以為ASM資源(VirtualService、DestinationRule等)的變更行為添加審計警示能力,在重要資源變動時及時發出警示通知到警示連絡人。具體操作,請參見為網格資源操作配置審計警示

  • 您可以基於VirtualService和DestinationRule等流量規則實現流量泳道,同時通過配置流量降級,在某個版本(或者其他特徵)的應用不可用時,將流量發往一個指定的降級版本(或其他特徵)的應用。具體操作,請參見基於流量規則配置實現流量泳道和流量降級

  • 如果您需要在用戶端對一個目標服務的訪問過程中,使流量儘可能的在同一個可用性區域內流轉,以保證服務間的調用延遲最低,可以使用同可用性區域優先路由功能。具體操作,請參見使用網格拓撲觀測同可用性區域優先路由