作者
呂亞霖,2019年加入作業幫,作業幫架構研發負責人,在作業幫期間主導了雲原生架構演進、推動實施容器化改造、服務治理、GO微服務框架、DevOps的落地實踐。
簡介
排程系統的本質是為計算服務/任務匹配合適的資源,使其能夠穩定高效地執行,以及在此的基礎上進一步提高資源使用密度,而影響應用執行的因素非常多,比如 CPU、記憶體、IO、差異化的資源裝置等等一系列因素都會影響應用執行的表現。同時,單獨和整體的資源請求、硬體/軟體/策略限制、 親和性要求、資料區域、負載間的干擾等因素以及週期性流量場景、計算密集場景、在離線混合等不同的應用場景的交織也帶來了決策上的多變。
排程器的目標則是快速準確地實現這一能力,但快速和準確這兩個目標在資源有限的場景下會往往產生產生矛盾,需要在二者間權衡。
排程器原理和設計
K8s 預設排程器的整體工作框架,可以簡單用下圖概括:
兩個控制迴圈
- 第一個控制迴圈,稱為 Informer Path。它的主要工作,是啟動一系列的 Informer,用來監聽(Watch)叢集中 Pod、Node、Service 等與排程相關的 API 物件的變化。比如,當一個待排程 Pod 被建立出來之後,排程器就會通過 Pod Informer 的 Handler,將這個待排程 Pod 新增進排程佇列;同時,排程器還要負責對排程器快取 Scheduler Cache 進行更新,並以這個 cache 為參考資訊,來提高整個排程流程的效能。
- 第二個控制迴圈,即為對 pod 進行排程的主迴圈,稱為 Scheduling Path。這一迴圈的工作流程是不斷地從排程佇列中取出待排程的 pod,執行2個步驟的演算法,來選出最優 node。
- 在叢集的所有節點中,選出所有“可以”執行該 pod 的節點,這一步被稱為 Predicates;
- 在上一步選出的節點中,根據一些列優選演算法對節點就行打分,選出“最優”即得分最高的節點,這一步被稱為 Priorities。
排程完成之後,排程器就會為 pod 的 spec.NodeName 賦值這個節點,這一步稱為 Bind。而為了不在主流程路徑中訪問 Api Server 影響效能,排程器只會更新 Scheduler Cache 中的相關 pod 和 node 資訊:這種基於樂觀的假設的 Api 物件更新方式,在 K8s 中稱為 Assume。之後才會建立一個 goroutine 來非同步地向 Api Server 發起更新 Bind 操作,這一步就算失敗了也沒有關係,Scheduler Cache 更新後就會一切正常。
大規模叢集排程帶來的問題和挑戰
K8s 預設排程器策略在小規模叢集下有著優異的表現,但是隨著業務量級的增加以及業務種類的多樣性變化,預設排程策略則逐漸顯露出了局限性:排程維度較少,無併發,存在效能瓶頸,以及排程器越來越複雜。
迄今為止,我們當前單個叢集規模節點量千級,pod 量級則在 10w 以上,整體資源分配率超過60%,其中更是包含了 gpu,在離線混合部署等複雜場景;在這個過程中,我們遇到了不少排程方面的問題。
問題1:高峰期的節點負載不均勻
預設排程器,參考的是 workload 的 request 值,如果我們針對 request 設定的過高,會帶來資源的浪費;過低則有可能帶來高峰期 CPU 不均衡差異嚴重的情況;使用親和策略雖然可以一定程度避免這種,但是需要頻繁填充大量的策略,維護成本就會非常大。而且服務的 request 往往不能體現服務真實的負載,帶來差異誤差。而這種差異誤差,會在高峰時體現到節點負載不均上。
實時排程器,在排程的時候獲取各節點實時資料來參與節點打分,但是實際上實時排程在很多場景並不適用,尤其是對於具備明顯規律性的業務來說;比如我們大部分服務晚高峰流量是平時流量的幾十倍,高低峰資源使用差距劇大,而業務發版一般選擇低峰發版,採用實時排程器,往往發版的時候比較均衡,到晚高峰就出現節點間巨大差異,很多實時排程器,往往在出現巨大差異的時候會使用再平衡策略來重新排程,高峰時段對服務 POD 進行遷移,服務高可用角度來考慮是不現實的。顯然實時排程是遠遠無法滿足業務場景的。
我們的方案:高峰預測時排程
所以針對這種情況,需要預測性排程,根據以往高峰時候 CPU、IO、網路、日誌等資源的使用量,通過對服務在節點上進行最優排列組合迴歸測算,得到各個服務和資源的權重係數,基於資源的權重打分擴充套件,也就是使用過去高峰資料來預測未來高峰節點服務使用量,從而干預排程節點打分結果。
問題2:排程維度多樣化
隨著業務越來越多樣性,需要加入更多的排程維度,比如日誌。由於採集器不可能無限速率採集日誌且日誌採集是基於節點維度。需要將平衡日誌採集速率,不能各個節點差異過大。部分服務 CPU 使用量一般但是日誌輸出量很大;而日誌並不屬於預設排程器決策的一環,所以當這些日誌量很大的服務多個服務的 pod 在同一個節點上的時候,該機器上的日誌上報就有可能出現部分延遲。
我們的方案:補全排程決策因子
該問題顯然需要我們對排程決策補全,我們擴充套件了預測排程打分策略,新增了日誌的決策因子,將日誌也作為節點的一種資源,並根據歷史監控獲取到服務對應的日誌使用量來計算分數。
問題3:大批量服務擴縮導帶來的排程時延
隨著業務的複雜度進一步上升,在高峰時段出現,會有大量定時任務和集中大量彈性擴縮,大批量(上千 POD)同時排程導致排程時延的上漲,這兩者對排程時間比較敏感,尤其對於定時任務來說,排程延時的上漲會被明顯感知到。原因是 K8s 排程 pod 本身是對叢集資源的分配,反應在排程流程上則是預選和打分階段是順序進行的;如此一來,當叢集規模大到一定程度的時候,大批量更新就會出現可感知到的 pod 排程延遲。
我們的方案:拆分任務排程器,加大併發排程域、批量排程
解決吞吐能力低下的最直接的方法就是序列改並行,對於資源搶佔場景,儘量細化資源域,資源域之間並行。給予以上策略,我們拆分出了獨立的 job 排程器,同時使用了 serverless 作為 job 執行的底層資源。K8s serverless 為每一個 JOB POD,單獨申請了獨立的 POD 執行 sanbox,也就是任務排程器,是完整並行。以下對比圖:
原生排程器在晚高峰下節點 CPU 使用率
優化後排程器在晚高峰下節點 CPU 使用率
總結
work 節點資源、GPU 資源、serverless 資源這就是我們叢集異構資源分屬於這三類資源域,這三種資源上執行的服務存在天然的差異性,我們使用 forecast-scheduler、gpu-scheduler、job-schedule 三個排程器來管理這三種資源域上的 pod 的排程情況。
預測排程器管理大部分線上業務,其中擴充套件了資源維度,新增了預測打分策略。
GPU 排程器管理 GPU 資源機器的分配,執行線上推理和離線訓練,兩者的比例處於長期波動中,高峰期間離線訓練縮容、線上推理擴容;非高峰期間離線訓練擴容、線上推理縮容;同時處理一些離線圖片處理任務來複用 GPU 機器上比較空閒的 CPU 等資源
Job 排程器負責管理我們定時任務的排程,定時任務量大且建立銷燬頻繁,資源使用非常碎片化,而且對實效性要求更高;所以我們將任務儘量排程到 Serverless 服務上,壓縮叢集中為了能容納大量的任務而冗餘的機器資源,提升資源利用率。
未來的演進探討
更細粒度的資源域劃分
將資源域劃分至節點級別,節點級別加鎖來進行。
資源搶佔和重排程
正常場景下,當一個 pod 排程失敗的時候,這個 pod 會保持在 pending 的狀態,等待 pod 更新或者叢集資源發生變化進行重新排程,但是 K8s 排程器依然存在一個搶佔功能,可以使得高優先順序 pod 在排程失敗的時候,擠走某個節點上的部分低優先順序 pod 以保證高優 pod 的正常,迄今為止我們並沒有使用排程器的搶佔能力,即使我們通過以上多種策略來加強排程的準確性,但依然無法避免部分場景下由於業務帶來的不均衡情況,這種非正常場景中,重排程的能力就有了用武之地,也許重排程將會成為日後針對異常場景的一種自動修復的方式。
關於我們
更多關於雲原生的案例和知識,可關注同名【騰訊雲原生】公眾號~
福利:
①公眾號後臺回覆【手冊】,可獲得《騰訊雲原生路線圖手冊》&《騰訊雲原生最佳實踐》~
②公眾號後臺回覆【系列】,可獲得《15個系列100+篇超實用雲原生原創乾貨合集》,包含Kubernetes 降本增效、K8s 效能優化實踐、最佳實踐等系列。
③公眾號後臺回覆【白皮書】,可獲得《騰訊雲容器安全白皮書》&《降本之源-雲原生成本管理白皮書v1.0》
【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多幹貨!!