本文分享自華為雲社群《基於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
如下圖所示演示環境,helloworld作為服務端有多個例項分別部署在不同zone中(不同zone節點topology.kubernetes.io/zone的label不同)。透過istio的destinationrule中localityLbSetting.failover(故障轉移策略)和outlierDetection(故障異常點檢測),可以實現客戶端業務訪問helloworld服務時候,優先訪問與客戶端同可用區的服務端,當同可用區的helloworld服務端全部故障後,再訪問指定可用區的服務端,實現故障轉移。
三 實戰演練
事先準備好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
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 ---
檢視客戶端中的儲存的cluster資訊
kubectl exec -it sleep-xxx -c istio-proxy -n sample -- curl localhost:15000/clusters可以看到cluster資訊中包含了例項的PodIP和位置資訊
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 服務,均訪問成功。
同時可以發現服務端響應的Pod總是同一個
檢視sleep例項的proxy日誌,透過日誌中的%UPSTREAM_HOST%欄位(紅框標準)172.16.0.136,可以看到5個請求均被髮送到相同的子區域helloworld例項(Pod IP為172.16.0.136)。
這是因為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
再次透過位於cn-north-4/cn-north-4b/tianjin 的Sleep Pod 多次呼叫 HelloWorld 服務。
可以發現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內進行操作的,所以不能演示完整的跨地域故障轉移。一般也多用在多叢集的治理環境中。
點選關注,第一時間瞭解華為雲新鮮技術~