全部產品
Search
文件中心

Alibaba Cloud Service Mesh:使用服務網格應對可用性區域級故障容災

更新時間:May 19, 2026

可用性區域級故障是雲上業務可能遭遇的一種較為極端的故障類型。當可用性區域故障發生時,發生故障的可用性區域內的工作負載都將面臨不可用、不可訪問或資料錯誤等風險。本文介紹通過結合阿里雲Container Service for Kubernetes (ACK)與阿里雲Service Mesh (ASM),應對可用性區域級故障容災。

背景資訊

阿里雲託管組件多可用性區域高可用

ACK叢集與ASM所有託管組件均嚴格採用多副本、多可用性區域均衡打散的部署策略,確保在單個可用性區域發生故障時,叢集和服務網格控制面仍然能夠正常地提供服務。同時,叢集內的WorkerNode、Elastic Container Instance也同樣被打散在不同可用性區域。在發生可用性區域層級故障時(例如因不可控因素導致的機房斷電、斷網),健康的可用性區域仍然能夠正常提供服務。

使用叢集高可用配置和服務網格來應對應用的可用性區域級故障

為了應對可用性區域層級的故障,第一步需要將應用的工作負載均勻地部署在不同的可用性區域。

ACK支援多可用性區域(AZ)節點池。在節點池的建立和運行過程中,推薦您為節點池選擇多個不同AZ的vSwitch,並在配置擴縮容策略時選擇均衡分布策略,允許在伸縮組指定的多可用性區域(即指定多個專用網路交換器)之間均勻分配ECS執行個體。進一步,基於節點的Auto Scaling、部署集、多AZ分布等手段,結合K8s調度中的拓撲分布約束(Topology Spread Constraints),可以實現將工作負載均勻部署在不同的可用性區域。具體細節可參考叢集高可用架構推薦配置

在應用實現多可用性區域部署後,需要能夠在運行時及時有效地瞭解Kubernetes 應用程式和叢集的健康情況,以發現可用性區域故障的情況,並能快速響應恢複。

  • 服務網格技術可以協助提高系統的網路可觀測性,通過服務網格的資料平面代理可以暴露與網路請求和應用服務互動相關的關鍵計量,這些指標可以發出各種問題的訊號,也包括了特定可用性區域的監控指標內容等。這些可觀測性資訊結合可以協助發現可用性區域故障的情況。

  • 在可用性區域級故障發生時,服務網格ASM還支援使用可用性區域流量轉移能力結合負載平衡的可用性區域流量轉移能力來進行容災:當可用性區域處於不可用狀態時,可以在收到相關警示資訊後配置可用性區域轉移,暫時將叢集內網路流量從受影響的可用性區域重新導向。當可用性區域恢複健康狀態時,還可以恢複該可用性區域的流量管理。

容災架構

當應對可用性區域層級的故障時,往往涉及到一系列上下相承的實踐措施,包括:

  • 在多個可用性區域中的均勻部署,並在每個可用性區域進行。

  • 觀測服務的相關指標並發現故障。

  • 當可用性區域發生故障時,快速將流量從受影響可用性區域切走(通過手動或自動手段),以完成故障恢複,這主要涉及以下方面:

    • 入口切流:確保作為流量入口的負載平衡不再向受影響可用性區域發送流量

    • 叢集內流量轉移:確保叢集內服務調用不會再將流量發送到受影響可用性區域

如下圖,為應對多可用性區域層級的故障,需要準備一個具有多可用性區域worker節點的ACK叢集,並在多可用性區域中均衡地部署工作負載。同時,可以將ACK叢集和服務網格ASM執行個體的控制面日誌、叢集事件收集至Log ServiceSLS,並通過ACK叢集和服務網格ASM將節點、容器和服務相關指標收集至阿里雲可觀測監控Prometheus版,以及時觀測到不同層級的故障事件、並可以通過日誌及Grafana儀錶盤查看到叢集內的服務狀況。同時在使用叢集流量入口時,建議使用具有多可用性區域多活機制的負載平衡作為入口流量承接(如NLB和ALB)。

ASM支援對運行於叢集中的服務所發出以及接受的所有請求進行訪問日誌以及請求指標的持續上報,能夠匯總請求相關的響應碼、延遲和請求尺寸等相關資訊,當可用性區域中發生灰色故障模式的故障時,服務網格提供的請求相關指標以及相關警示配置對確定故障範圍以及故障表現可以形成有意義的參考。本樣本將介紹如何查看服務網格上報的服務流量相關指標,有關叢集工作負載相關指標及相關警示配置,請參見接入與配置阿里雲Prometheus監控Container Service警示管理事件監控

容災配置實踐

本樣本將通過建立一個包含多個可用性區域的叢集來示範可用性區域層級故障的容災動作。

步驟一:環境準備

  1. 建立一個ACK託管叢集。在建立叢集時,選擇來自兩個可用性區域的交換器以支援叢集的多可用性區域。其它配置均保持預設即可,節點池將預設使用均衡分布的擴縮容策略。具體操作,請參見建立ACK託管叢集

  2. 建立與ACK叢集相同可用性區域配置的ASM執行個體。具體操作,請參見建立ASM執行個體

  3. 部署網關及樣本應用。

    1. 在ASM執行個體中建立ASM網關、並綁定一個網路型負載平衡NLB。NLB可用性區域選擇與ASM執行個體和ACK叢集同樣的兩個可用性區域(本例中是cn-hangzhou-k和cn-hangzhou-h)。具體操作,請參見在ASM入口網關中使用網路型負載平衡NLB

      說明

      本例中採用關連網絡型負載平衡NLB的ASM網關作為流量入口。您也可以使用應用型負載平衡ALB作為應用的流量入口,ALB同樣具備多可用性區域容災以及DNS摘除能力。

    2. 關閉NLB的跨AZ轉寄能力,使得每個可用性區域的NLB都只向自身可用性區域內的後端進行轉寄。

    3. 為命名空間default開啟Sidecar自動注入。具體操作參考管理全域命名空間

    4. 部署樣本應用。

      展開查看代碼內容

      kubectl apply -f- <<EOF
      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-cn-hangzhou-h
        labels:
          app: mocka
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: mocka
        template:
          metadata:
            labels:
              app: mocka
              locality: cn-hangzhou-h
          spec:
            nodeSelector:      
              topology.kubernetes.io/zone: cn-hangzhou-h  
            containers:
            - name: default
              image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/go-http-sample:tracing
              imagePullPolicy: IfNotPresent
              env:
              - name: version
                value: cn-hangzhou-h 
              - name: app
                value: mocka
              - name: upstream_url
                value: "http://mockb:8000/"
              ports:
              - containerPort: 8000
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: mocka-cn-hangzhou-k
        labels:
          app: mocka
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: mocka
        template:
          metadata:
            labels:
              app: mocka
              locality: cn-hangzhou-k
          spec:
            nodeSelector:      
              topology.kubernetes.io/zone: cn-hangzhou-k
            containers:
            - name: default
              image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/go-http-sample:tracing
              imagePullPolicy: IfNotPresent
              env:
              - name: version
                value: cn-hangzhou-k
              - 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-cn-hangzhou-h
        labels:
          app: mockb
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: mockb
        template:
          metadata:
            labels:
              app: mockb
              locality: cn-hangzhou-h
          spec:
            nodeSelector:      
              topology.kubernetes.io/zone: cn-hangzhou-h
            containers:
            - name: default
              image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/go-http-sample:tracing
              imagePullPolicy: IfNotPresent
              env:
              - name: version
                value: cn-hangzhou-h
              - name: app
                value: mockb
              - name: upstream_url
                value: "http://mockc:8000/"
              ports:
              - containerPort: 8000
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: mockb-cn-hangzhou-k
        labels:
          app: mockb
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: mockb
        template:
          metadata:
            labels:
              app: mockb
              locality: cn-hangzhou-k
          spec:
            nodeSelector:      
              topology.kubernetes.io/zone: cn-hangzhou-k
            containers:
            - name: default
              image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/go-http-sample:tracing
              imagePullPolicy: IfNotPresent
              env:
              - name: version
                value: cn-hangzhou-k
              - 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-cn-hangzhou-h
        labels:
          app: mockc
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: mockc
        template:
          metadata:
            labels:
              app: mockc
              locality: cn-hangzhou-h
          spec:
            nodeSelector:      
              topology.kubernetes.io/zone: cn-hangzhou-h
            containers:
            - name: default
              image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/go-http-sample:tracing
              imagePullPolicy: IfNotPresent
              env:
              - name: version
                value: cn-hangzhou-h
              - name: app
                value: mockc
              ports:
              - containerPort: 8000
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: mockc-cn-hangzhou-k
        labels:
          app: mockc
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: mockc
        template:
          metadata:
            labels:
              app: mockc
              locality: cn-hangzhou-k
          spec:
            nodeSelector:      
              topology.kubernetes.io/zone: cn-hangzhou-k
            containers:
            - name: default
              image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/go-http-sample:tracing
              imagePullPolicy: IfNotPresent
              env:
              - name: version
                value: cn-hangzhou-k
              - name: app
                value: mockc
              ports:
              - containerPort: 8000
      ---
      apiVersion: networking.istio.io/v1beta1
      kind: Gateway
      metadata:
        name: mocka
        namespace: default
      spec:
        selector:
          istio: ingressgateway
        servers:
          - hosts:
              - '*'
            port:
              name: test
              number: 80
              protocol: HTTP
      ---
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: demoapp-vs
        namespace: default
      spec:
        gateways:
          - mocka
        hosts:
          - '*'
        http:
          - name: test
            route:
              - destination:
                  host: mocka
                  port:
                    number: 8000
      EOF

      以上命令將部署一個包含mocka、mockb、mockc服務的應用,每個服務下包含兩個只有1副本的無狀態部署,分別通過不同的nodeSelector分布到不同可用性區域的節點上,並通過配置環境變數來返回自己所在的可用性區域。

      說明

      出於樣本示範直觀的目的,本樣本中直接使用pod的nodeSelector欄位來手動選擇pod所在的可用性區域。在實際高可用環境的構建中,您應該配置拓撲分布約束,來保證pod儘可能分布在不同的可用性區域中。具體配置方法,請參見工作負載高可用配置

步驟二:觀測服務指標

當故障發生時,與工作負載相關的日誌及指標等可觀測資訊以及相關的警示可以協助我們儘快發現故障事件、確定故障範圍以及初步瞭解故障影響或理解故障成因。

  1. 為請求指標添加可用性區域維度。

    網格代理可以自動檢測工作負載部署的可用性區域,並儲存在代理中繼資料中。我們可以通過編輯指標維度方法,為服務網格指標添加locality維度、並將取值設定為xds.node.locality.zone,即工作負載所在可用性區域。具體操作,請參見可觀測配置

  2. 向樣本應用發送請求,產生請求指標。

    watch -n 0.1 curl nlb-xxxxxxxxxxxxx.cn-xxxxxxx.nlb.aliyuncsslb.com/mock -v

    預期輸出:

    > GET /mock HTTP/1.1
    > Host: nlb-85h289ly4hz9qhaz58.cn-hangzhou.nlb.aliyuncsslb.com
    > User-Agent: curl/8.7.1
    > Accept: */*
    > 
    * Request completely sent off
    < HTTP/1.1 200 OK
    < date: Sun, 08 Dec 2024 11:53:26 GMT
    < content-length: 150
    < content-type: text/plain; charset=utf-8
    < x-envoy-upstream-service-time: 5
    < server: istio-envoy
    < 
    * Connection #0 to host nlb-85h289ly4hz9qhaz58.cn-hangzhou.nlb.aliyuncsslb.com left intact
    -> mocka(version: cn-hangzhou-h, ip: 192.168.122.66)-> mockb(version: cn-hangzhou-k, ip: 192.168.0.47)-> mockc(version: cn-hangzhou-h, ip: 192.168.122.44)%

    可以看到,請求會隨機到兩個不同的可用性區域,說明兩個可用性區域的工作負載都處於可用狀態。

  3. 持續發送一段時間請求後,在可觀測監控Prometheus版中進行指標探索,具體操作,參考指標探索

    1. 查看不同可用性區域的工作負載請求狀態代碼資訊。

      在Prometheus執行個體中,通過以下PromQL,可以查詢到指定可用性區域內的工作負載接收請求的速率,並按照服務名和響應碼分組:

      sum by(app, response_code) (rate(istio_requests_total{locality="cn-hangzhou-h", reporter="destination"}[$__rate_interval]))

      預期效果:

      image

      可以看到在均勻部署的情況下,兩個可用性區域接收到的請求速率基本一致。您可以通過將篩選條件變更為locality="cn-hangzhou-k"查看cn-hangzhou-k可用性區域下的請求速率及狀態代碼資訊。

    2. 查看不同可用性區域的工作負載請求延遲資訊。

      在Prometheus執行個體中,通過以下PromQL,可以查詢到指定可用性區域內的工作負載接收請求的平均延遲,並按照服務名和響應碼分組:

      sum by(app, response_code) (rate(istio_request_duration_milliseconds_sum{locality="cn-hangzhou-h", reporter="destination"}[$__rate_interval]))

      您可以將篩選條件變更為locality="cn-hangzhou-k"查看cn-hangzhou-k可用性區域下的平均延遲資訊。

  4. 基於指標資訊配置警示規則。

    您可以通過自訂PromQL的方式配置警示資訊。當業務的延遲或狀態代碼大於設定值時,為指定連絡人發送警示資訊。具體操作方式,請參見建立Prometheus警示規則

    1. 基於應用延遲設定警示規則。

      通過以下的PromQL來實現針對mockb服務的警示。

      sum by(response_code, locality) (rate(istio_request_duration_milliseconds_sum{app="mockb",reporter= "destination"}[1m])) > 3

      上述PromQL表示當mockb服務1分鐘之內的平均延遲在3ms以上時進行警示,警示針對狀態代碼和可用性區域資訊進行分組。

    2. 基於服務響應狀態代碼設定警示規則。

      通過如下的PromQL來實現針對mocka服務的警示。

      sum by (locality, response_code) (rate(istio_requests_total{app="mocka",reporter="destination",response_code!="200"}[1m])) >= 0

    您可以通過修改上述兩種警示規則的設定方式中的app="mockb"為其他應用設定警示規則。

步驟三:容災演練

當ACK叢集中的一個可用性區域不健康或受損並收到警示後,我們可以將該可用性區域內部署的服務工作負載與其它可用性區域進行隔離,在保持業務依然正常啟動並執行情況下對故障原因進行詳細調查、並在故障恢複時讓流量返回到擁有完整容量的可用性區域。

  1. 隔離受影響可用性區域中的節點。

    通過設定汙點,禁止新的Pod向可用性區域內的節點進行調度。設定後,節點將進入不可調度狀態,已有Pod仍將留存在節點上、但新的Pod不會在該可用性區域內再進行調度。具體操作,參考設定節點調度狀態。以下以隔離cn-hangzhou-h可用性區域為例,將cn-hangzhou-h可用性區域中的節點都標識為不可調度。

  2. 轉移南北向入口流量。

    由於配置了NLB的同可用性區域轉寄,當故障發生時,需要使用NLB(或ALB)的DNS摘除能力,快速在入口南北向流量層級切走流量,防止外部流量繼續流入故障可用性區域。

    1. 進入NLB控制台,單擊ASM網關綁定的NLB執行個體。

    2. 在NLB的執行個體詳情頁面,可用區頁簽下,找到受影響可用性區域,在右側操作列中單擊DNS摘除,並在彈框中單擊確定。進行DNS摘除後,受影響可用性區域的NLB公網IP將不再出現在網域名稱解析記錄中。

  3. 轉移東西向流量。

    通過使用阿里雲服務網格ASM的可用性區域轉移能力,可以快速轉移叢集中的東西向流量走向,防止流量發送到指定可用性區域的端點。

    1. 進入服務網格控制台,單擊管理叢集的服務網格執行個體。

    2. 在網格詳情頁面,單擊服務發現範圍配置。找到展開進階選項,填寫Pod所在的地區和可用性區域資訊,並單擊確定

    操作後,執行個體會進入短暫的更新狀態。更新完成後,指定可用性區域內的端點將被排除出服務發現範圍。

  4. 觀察隔離效果。

    繼續持續訪問樣本應用,可以發現響應的服務均來自cn-hangzhou-k的服務,說明流量已經完全從cn-hangzhou-h可用性區域切走。預期輸出:

    > Host: nlb-xxxxxxxxxxxxxxxxxxx8.cn-hangzhou.nlb.aliyuncsslb.com
    > User-Agent: curl/8.7.1q
    > Accept: */*
    > A
    * Request completely sent off
    < HTTP/1.1 200 OKe
    < date: Tue, 10 Dec 2024 04:03:26 GMT
    < content-length: 1500
    < content-type: text/plain; charset=utf-8
    < x-envoy-upstream-service-time: 30
    < server: istio-envoyr
    < s
    { [150 bytes data]
    {100   150  100   150    0     0   2262      0 --:--:-- --:--:-- --:--:--  2238
    * Connection #0 to host nlb-xxxxxxxxxxxxxxxxxxx8.cn-hangzhou.nlb.aliyuncsslb.com left intact
    -> mocka(version: cn-hangzhou-k, ip: 192.168.0.44)-> mockb(version: cn-hangzhou-k, ip: 192.168.0.47)-> mockc(version: cn-hangzhou-k, ip: 192.168.0.46)