基於istio實現單集羣地域故障轉移

本文分享自華爲雲社區《基於istio實現單集羣地域故障轉移》,作者:可以交個朋友。

一 背景

隨着應用程序的增長並變得更加複雜,微服務的數量也會增加,失敗的可能性也會增加。微服務的故障可能多種原因造成,例如硬件問題、網絡延遲、軟件錯誤,甚至人爲錯誤。故障轉移Failover 是系統韌性設計中的一個基礎能力,它們可以確保系統在出現故障時能夠繼續運行,並且能夠在最小化的影響下進行恢復,減少或者消除對使用方或最終用戶的影響,從而提高整個系統對外的可用性。

二 簡介

雲原生K8s、istio默認使用node上特定label作爲地域信息:

  • 地區:代表較大的地理區域,例如 us-east。一個地區通常包含許多可用區。 在 Kubernetes 中,標籤topology.kubernetes.io/region 決定了節點所在的地區。
  • 區域:區域內的一組計算資源。通過在區域內的多個區域中運行服務,可以在區域內的區域之間進行故障轉移, 同時保持最終用戶的數據地域性。在 Kubernetes 中,標籤topology.kubernetes.io/zone決定了節點所在的區域。
  • 分區:允許管理員進一步細分區域,以實現更細粒度的控制,例如“相同機架”。 Kubernetes 中不存在分區的概念。所以 Istio 引入了自定義節點標籤 topology.istio.io/subzone 來定義分區。

kubectl describe node xxx |grep topo

cke_114.png

如下圖所示演示環境,helloworld作爲服務端有多個實例分別部署在不同zone中(不同zone節點topology.kubernetes.io/zone的label不同)。通過istio的destinationrule中localityLbSetting.failover(故障轉移策略)和outlierDetection(故障異常點檢測),可以實現客戶端業務訪問helloworld服務時候,優先訪問與客戶端同可用區的服務端,當同可用區的helloworld服務端全部故障後,再訪問指定可用區的服務端,實現故障轉移。

cke_115.png

三 實戰演練

事先準備好kubernetes+istio作爲操作環境。可用華爲雲CCE和ASM服務進行操作。

3.1 部署服務端

1.創建sample 命名空間,並設置istio-proxy sidecar自動注入

apiVersion: v1
kind: Namespace
metadata:
  name: sample
  labels:
    istio-injection: enabled

2.部署helloworld服務 作爲服務端

將根據以下腳本生成對yaml配置清單

 

#!/bin/bash

set -euo pipefail

display_usage() {
    echo
    echo "USAGE: ./gen-helloworld.sh [--version] [--includeService value] [--includeDeployment value]"
    echo "    -h|--help: Prints usage information"
    echo "    --version: Specifies the version that will be returned by the helloworld service, default: 'v1'"
    echo "    --includeService: If 'true' the service will be included in the YAML, default: 'true'"
    echo "    --includeDeployment: If 'true' the deployment will be included in the YAML, default: 'true'"
}

INCLUDE_SERVICE=${INCLUDE_SERVICE:-"true"}
INCLUDE_DEPLOYMENT=${INCLUDE_DEPLOYMENT:-"true"}
SERVICE_VERSION=${SERVICE_VERSION:-"v1"}
while (( "$#" )); do
  case "$1" in
    -h|--help)
      display_usage
      exit 0
      ;;

    --version)
      SERVICE_VERSION=$2
      shift 2
      ;;

    --includeService)
      INCLUDE_SERVICE=$2
      shift 2
      ;;

    --includeDeployment)
      INCLUDE_DEPLOYMENT=$2
      shift 2
      ;;

    *)
      echo "Error: Unsupported flag $1" >&2
      display_usage
      exit 1
      ;;
  esac
done

SERVICE_YAML=$(cat <<EOF
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
    service: helloworld
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
EOF
)

DEPLOYMENT_YAML=$(cat <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-${SERVICE_VERSION}
  labels:
    app: helloworld
    version: ${SERVICE_VERSION}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
      version: ${SERVICE_VERSION}
  template:
    metadata:
      labels:
        app: helloworld
        version: ${SERVICE_VERSION}
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: topology.istio.io/subzone
                operator: In
                values:
                - ${SERVICE_VERSION}            

      containers:
      - name: helloworld
        env:
        - name: SERVICE_VERSION
          value: ${SERVICE_VERSION}
        image: docker.io/istio/examples-helloworld-v1
        resources:
          requests:
            cpu: "100m"
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5000
EOF
)

OUT=""

# Add the service to the output.
if [[ "$INCLUDE_SERVICE" == "true" ]]; then
  OUT="${SERVICE_YAML}"
fi

# Add the deployment to the output.
if [[ "$INCLUDE_DEPLOYMENT" == "true" ]]; then
  # Add a separator
  if [[ -n "$OUT" ]]; then
    OUT+="
---
"
  fi
  OUT+="${DEPLOYMENT_YAML}"
fi

echo "$OUT"

執行腳本: for LOC in "beijing" "tianjin" "shenyang"; do ./genHelloWorld.sh --version "$LOC" > "helloworld-${LOC}.yaml"; done 將會生成yaml配置清單,應用到集羣即可。

kubectl apply -f helloworld-xxx.yaml -n sample

cke_116.png

3.2 部署客戶端

kubectl apply -f sleep.yaml -n sample

# Sleep service
##################################################################################################
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sleep
---
apiVersion: v1
kind: Service
metadata:
  name: sleep
  labels:
    app: sleep
    service: sleep
spec:
  ports:
  - port: 80
    name: http
  selector:
    app: 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
---

cke_117.png

查看客戶端中的存儲的cluster信息

kubectl exec -it sleep-xxx -c istio-proxy -n sample -- curl localhost:15000/clusters可以看到cluster信息中包含了實例的PodIP和位置信息

cke_118.png

3.3 配置服務端地域故障轉移規則

istio的流量治理一般都是通過virtualservice、destinationrule 、envoyfilter等來實現,其中地域故障轉移是通過destinationrule配置實現的。因爲在destinationrule中可以配置outerlineDecetion進行異常點檢測,只有檢測到異常後,纔會進行故障轉移。kubectl apply -f xxx.yaml

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: helloworld
  namespace: sample
spec:
  host: helloworld.sample.svc.cluster.local
  trafficPolicy:
    connectionPool:
      http:
        maxRequestsPerConnection: 1
    loadBalancer:
      simple: ROUND_ROBIN
      localityLbSetting:  #開啓地域負載均衡
        enabled: true
        failover:         #配置故障轉移策略,failover主要控制Region等上層位置的切換
          - from: cn-north-4
            to: cn-south-1
    outlierDetection:    #異常點檢測
      consecutive5xxErrors: 1
      interval: 1s
      baseEjectionTime: 1m

以上治理策略表示:

  • 異常點檢測:當某個客戶端訪問helloworld服務時,客戶端對應的envoy會根據本次訪問HTTP狀態碼對轉發的服務端進行故障檢測,故障檢測條件爲當發生1次5xx錯誤時實例就會被隔離1m。
  • 故障隔離:當指定region的所有後端實例均不正常,觸發故障轉移到下一個地域,確保了超出地區邊界的故障轉移將具有可預測的行爲。如果位於cn-north-4 region的實例異常,流量就會發往cn-south-1 region 的實例。

3.4 驗證地域負載均衡

通過位於cn-north-4/cn-north-4b/tianjin 的Sleep Pod 多次調用 HelloWorld 服務,均訪問成功。

cke_119.png

同時可以發現服務端響應的Pod總是同一個

cke_120.png

查看sleep實例的proxy日誌,通過日誌中的%UPSTREAM_HOST%字段(紅框標準)172.16.0.136,可以看到5個請求均被髮送到相同的子區域helloworld實例(Pod IP爲172.16.0.136)。

cke_121.png

這是因爲istio考慮到網絡開銷,部署在region1/zone1上的sleep實例 大多時候只會訪問部署在同Region同Zone的helloworld實例。

3.5 驗證地域故障轉移

首先模擬故障,通過下述命令 向 enovy 的 admin port 發送請求,關閉envoy的 listener 。enovy 收到請求後,會取消端口監聽,不再接收新的連接和請求。

kubectl exec helloworld-tianjin-xxx -n sample -c istio-proxy -- curl -sSL -X POST 127.0.0.1:15000/drain_listeners

cke_122.png

再次通過位於cn-north-4/cn-north-4b/tianjin 的Sleep Pod 多次調用 HelloWorld 服務。

cke_123.png

可以發現4個請求被以輪詢的方式發往cn-north-4/cn-north-4b/beijing 和cn-north-4/cn-north-4b/shenyang的 helloworld實例。以上結果說明,在一個區域的服務實例發生故障時,可根據配置,將請求路由到其它地域的服務實例進行處理,增強服務的可靠性。在實踐中可通過From、To 配置region地區信息,控制在不同地區的實例上進行故障轉移。

四 備註

關於地域負載均衡的配置failover主要控制的是跨region的場景,因爲位於region內的zone或者subzone 上的實例默認就可以切換流量。本文檔的實踐主要是在region內進行操作的,所以不能演示完整的跨地域故障轉移。一般也多用在多集羣的治理環境中。

cke_124.png

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章