多可用性區域均衡是資料類型業務在高可用情境下常用的部署方式。當業務壓力增大時,有多可用性區域均衡調度策略的應用希望可以自動擴容出多個可用性區域的執行個體來滿足叢集的調度水位。本文介紹如何在多可用性區域實現快速彈性擴容。
前提條件
已選擇多個可用性區域,並在每個可用性區域建立好vSwitch,需要在哪些可用性區域彈出就需要這些可用性區域至少有一個vSwitch。關於建立可用性區域和vSwtich的具體操作,請參見建立ACK託管叢集。
背景資訊
Container Service節點自動調整組件可通過預調度判斷服務能否部署在某個伸縮組上,然後將擴容執行個體個數的請求發給指定伸縮組,最終由ESS伸縮組產生執行個體。但這種在一個伸縮組上配置多個可用性區域vSwtich的模型特點,會導致以下問題:
當多可用性區域業務Pod因叢集資源不足無法調度時,節點自動調整服務會觸發該伸縮組擴容,但是無法將需要擴容的可用性區域與執行個體的關係傳遞到Auto Scaling組,因此可能會連續彈出某一個地區的多個執行個體,而非在多個vSwitch同時彈出,這樣無法滿足在多可用性區域同時擴容的需求。
解決方案
為瞭解決同時擴容多可用性區域節點的問題,Container ServiceACK引入了ack-autoscaling-placeholder組件,通過少量的資源冗餘方式,將多可用性區域的Auto Scaling問題轉變為並發節點池的定向伸縮問題。具體操作,請參見基於ack-autoscaling-placeholder實現容器秒級伸縮。
具體原理如下:
首先為每個可用性區域建立一個節點池,並分別在各個節點池打上可用性區域的標籤。
通過配置可用性區域標籤nodeSelector的方式,使用ack-autoscaling-placeholder為每個可用性區域建立佔位Pod,預設的佔位Pod具有比較低權重的PriorityClass,應用Pod的優先順序高於佔位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
虛擬交換器
選擇可用性區域I的虛擬交換器。
自動調整
開啟自動Auto Scaling。
節點標籤
設定節點標籤的鍵為avaliable_zone,值為i。
單擊確認配置。
在節點池頁面,查看節點池列表。當auto-zone-I節點池狀態為已啟用,表示該節點池建立成功。
重複以上步驟,在每個需要擴容的可用性區域建立開啟自動調整的節點池。
步驟二:部署Placeholder及佔位Deployment
在控制台左側導覽列,選擇 。
在應用目錄頁簽,搜尋ack-autoscaling-placeholder,然後單擊ack-autoscaling-placeholder。
在ack-autoscaling-placeholder頁面,單擊一鍵部署。
在建立面板,選擇叢集和命名空間,然後單擊下一步。選擇Chart版本,編輯參數,然後單擊確定。
建立成功後,在
頁面,可查看到該應用狀態為已部署。在叢集管理頁左側導覽列,選擇 。
在Helm頁面,單擊ack-autoscaling-placeholder-defalut操作列的更新。
在更新發布面板中,更新YAML,然後單擊確定。將每個可用性區域都設定Placeholder,每個地區需要定義一個佔位Deployment。
本文以在可用性區域I、K、H建立佔位Deployment為例,YAML樣本如下:
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: {"avaliable_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: {"avaliable_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: {"avaliable_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."
如果不希望在負載上配置PriorityClass,可以通過設定全域PriorityClass的方式進行全域預設設定,這樣只需要下發預設配置後,搶佔能力便自動生效。
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: # 節點選擇。 avaliable_zone: "i" priorityClassName: high-priority # 這裡寫入步驟三配置的PriorityClass名稱,如果全域配置開啟,則為非必填。 containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 resources: requests: cpu: 3 # 實際負載的資源需求。 memory: 5
執行以下命令,部署實際的工作負載。
kubectl apply -f workload.yaml
預期輸出:
deployment.apps/placeholder-test created
結果驗證
部署後在
頁面可以發現,由於實際負載的PriorityClass比佔位Pod的要高,在彈出節點上,搶佔的佔位Pod會運行起來。而被搶佔的佔位Pod會觸發節點自動調整組件的並發擴容,為下次實際負載擴容做準備。在
頁面,可以看到負載Pod運行在之前佔位Pod的節點上。