多可用性區域均衡部署是分布式系統實現高可用架構的核心策略之一。業務負載增大時,多可用性區域均衡調度策略能夠自動擴容出多個可用性區域的執行個體來滿足叢集的調度水位。
前提條件
已在待彈出執行個體的可用性區域建立至少一個交換器,請參見建立和管理交換器。建立後,您可以在建立和管理節點池時選擇對應的vSwitch。
功能介紹
節點自動調整組件可通過預調度判斷服務能否部署在某伸縮組上,並將擴容請求發送至指定ESS伸縮組完成執行個體的產生。但當前在單伸縮組配置多可用性區域vSwitch的機制存在一個問題:當多可用性區域業務Pod因叢集資源不足無法調度時,ACK雖會觸發伸縮組擴容,但由於缺乏可用性區域與執行個體的關聯資訊傳遞機制,Auto Scaling組無法識別具體需要擴容的可用性區域。這可能導致擴容執行個體集中在單個可用性區域,而非多可用性區域均衡擴充,無法滿足業務對跨可用性區域同時擴容的需求。
為此,ACK引入了ack-autoscaling-placeholder組件,通過少量的資源冗餘方式,將多可用性區域的Auto Scaling轉變為並發節點池的定向伸縮,詳情請參見基於ack-autoscaling-placeholder實現容器秒級伸縮。原理如下。
為每個可用性區域建立一個節點池,並分別在各個節點池打上可用性區域的標籤。
通過配置可用性區域標籤nodeSelector的方式,使用ack-autoscaling-placeholder為每個可用性區域建立佔位Pod。預設的佔位Pod具有比較低權重的PriorityClass,優先順序低於應用Pod。
業務應用Pod Pending後會搶佔各個可用性區域佔位Pod,帶有可用性區域nodeSelector的多可用性區域佔位Pod處於Pending後,節點自動調整組件感知到的調度策略就從多個可用性區域的antiAffinity變成了可用性區域的nodeSelector,從而處理髮出擴容地區的節點的請求。
以下以兩個可用性區域為例,介紹如何基於現有架構滿足多可用性區域的同時擴容。
ack-autoscaling-placeholder作為業務應用和節點自動調整組件之間的橋樑,為每個可用性區域建立佔位Pod。佔位Pod的調度優先順序低於實際業務應用的調度優先順序。
應用Pod Pending後會迅速搶佔佔位Pod,並部署在各個可用性區域的已有節點上,同時,被搶佔的佔位Pod會處於Pending狀態。
佔位Pod是帶有可用性區域nodeSelector調度策略的,節點自動調整組件可以並發擴容到對應的可用性區域。
步驟一:為可用性區域建立節點池並配置自訂節點標籤
登入Container Service管理主控台,在左側導覽列選擇叢集列表。
在叢集列表頁面,單擊目的地組群名稱,然後在左側導覽列,選擇。
單擊建立節點池,按照頁面提示完成節點池的配置。
本樣本以在可用性區域I建立開啟自動調整的節點池auto-zone-I為例。以下僅介紹核心配置項,詳情請參見建立和管理節點池。
配置項
說明
節點池名稱
auto-zone-I
擴縮容模式
選擇自動,開啟自動Auto Scaling。
交換器
選擇可用性區域I的交換器。
節點標籤
設定節點標籤的鍵為
available_zone,值為i。節點池列表中,當auto-zone-I節點池狀態為已啟用時,表示節點池建立成功。
重複以上步驟,在每個需要擴容的可用性區域建立開啟自動調整的節點池。

步驟二:部署Placeholder及佔位Deployment
在控制台左側導覽列,選擇。
搜尋ack-autoscaling-placeholder,單擊組件卡片,然後單擊一鍵部署。
選擇叢集和命名空間,然後單擊下一步,選擇Chart版本,編輯參數,然後單擊確定。
建立成功後,在頁面,可查看到該應用狀態為已部署。
在叢集管理頁左側導覽列,選擇。
在Helm頁面,單擊ack-autoscaling-placeholder-default操作列的更新。
在更新發布面板中,參見下方樣本更新YAML,然後單擊確定。將每個可用性區域都設定Placeholder,每個地區需要定義一個佔位Deployment。
本樣本以在可用性區域I、K、H建立佔位Deployment為例。
deployments: - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # 調度單元CPU。 memory: 6 # 調度單元記憶體。 imagePullSecrets: {} labels: {} name: ack-place-holder-I # 佔位Deployment名稱。 nodeSelector: {"available_zone":i} # 可用性區域標籤(需要與步驟一建立節點池中配置的標籤一致)。 replicaCount: 10 # 每次擴容可快速彈出Pod數量。 tolerations: [] - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # 調度單元CPU。 memory: 6 # 調度單元記憶體。 imagePullSecrets: {} labels: {} name: ack-place-holder-K # 佔位Deployment名稱。 nodeSelector: {"available_zone":k} # 可用性區域標籤(需要與步驟一建立節點池中配置的標籤一致)。 replicaCount: 10 # 每次擴容可快速彈出Pod數量。 tolerations: [] - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # 調度單元CPU。 memory: 6 # 調度單元記憶體。 imagePullSecrets: {} labels: {} name: ack-place-holder-H # 佔位Deployment名稱。 nodeSelector: {"available_zone":h} # 可用性區域標籤(需要與步驟一建立節點池中配置的標籤一致)。 replicaCount: 10 # 每次擴容可快速彈出Pod數量。 tolerations: [] fullnameOverride: "" nameOverride: "" podSecurityContext: {} priorityClassDefault: enabled: true name: default-priority-class value: -1更新成功後,各個可用性區域將建立對應的佔位Deployment。

步驟三:建立實際負載的PriorityClass
使用以下YAML樣本,建立名為priorityClass.yaml的檔案。
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 # 配置優先順序,比步驟二的工作負載預設優先順序高。 globalDefault: false description: "This priority class should be used for XYZ service pods only."若無需為Pod單獨配置PriorityClass,可通過全域PriorityClass配置全域預設設定。配置生效後,未指定PriorityClass的Pod將自動採用此優先順序值,搶佔能力自動生效。
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: global-high-priority value: 1 # 配置優先順序,比步驟二的工作負載預設優先順序高。 globalDefault: true description: "This priority class should be used for XYZ service pods only."部署工作負載的PriorityClass。
kubectl apply -f priorityClass.yaml預期輸出:
priorityclass.scheduling.k8s.io/high-priority created
步驟四:建立實際工作負載
以可用性區域I為例。
使用以下YAML樣本,建立名為workload.yaml的檔案。
apiVersion: apps/v1 kind: Deployment metadata: name: placeholder-test labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: nodeSelector: # 節點選擇。 available_zone: "i" priorityClassName: high-priority # 步驟三配置的PriorityClass名稱。如果全域配置開啟,則為非必填。 containers: - name: nginx image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6 ports: - containerPort: 80 resources: requests: cpu: '3' # 實際負載的資源需求。 memory: 5Gi部署實際的工作負載。
kubectl apply -f workload.yaml預期輸出:
deployment.apps/placeholder-test created部署後,在頁面可以發現,由於實際負載的PriorityClass比佔位Pod高,搶佔的佔位Pod會在彈出節點上運行,被搶佔的佔位Pod會觸發節點自動調整組件的並發擴容,為下次實際負載擴容做準備。

在頁面,可以看到負載Pod運行在之前佔位Pod的節點上。
