支持批任务的Coscheduling/Gang scheduling
作者:王慶璨 張凱
進擊的Kubernetes調度系統(一):Scheduling Framework
進擊的Kubernetes調度系統(二):支持批任務的Coscheduling/Gang scheduling
前言
首先我們來了解一下什么是Coscheduling和Gang scheduling。Wikipedia對?Coscheduling的定義是“在并發系統中將多個相關聯的進程調度到不同處理器上同時運行的策略”。在Coscheduling的場景中,最主要的原則是保證所有相關聯的進程能夠同時啟動。防止部分進程的異常,導致整個關聯進程組的阻塞。這種導致阻塞的部分異常進程,稱之為“碎片(fragement)”。
在Coscheduling的具體實現過程中,根據是否允許“碎片”存在,可以細分為Explicit Coscheduling,Local Coscheduling和Implicit Coscheduling。 其中Explicit Coscheduling就是大家常聽到的Gang Scheduling。Gang Scheduling要求完全不允許有“碎片”存在, 也就是“All or Nothing”。
我們將上述定義的概念對應到Kubernetes中,就可以理解Kubernetes調度系統支持批任務Coscheduling的含義了。 一個批任務(關聯進程組)包括了N個Pod(進程),Kubernetes調度器負責將這N個Pod調度到M個節點(處理器)上同時運行。如果這個批任務需要部分Pod同時啟動即可運行,我們稱需啟動Pod的最小數量為min-available。特別地,當min-available=N時,批任務要求滿足Gang Scheduling。
為什么Kubernetes調度系統需要Coscheduling?
Kubernetes目前已經廣泛的應用于在線服務編排,為了提升集群的的利用率和運行效率,我們希望將Kubernetes作為一個統一的管理平臺來管理在線服務和離線作業。默認的調度器是以Pod為調度單元進行依次調度,不會考慮Pod之間的相互關系。但是很多數據計算類的離線作業具有組合調度的特點,即要求所有的子任務都能夠成功創建后,整個作業才能正常運行。如果只有部分子任務啟動的話,啟動的子任務將持續等待剩余的子任務被調度。這正是Gang Scheduling的場景。
如下圖所示,JobA需要4個Pod同時啟動,才能正常運行。Kube-scheduler依次調度3個Pod并創建成功。到第4個Pod時,集群資源不足,則JobA的3個Pod處于空等的狀態。但是它們已經占用了部分資源,如果第4個Pod不能及時啟動的話,整個JobA無法成功運行,更糟糕的是導致集群資源浪費。
?
如果出現更壞的情況的話,如下圖所示,集群其他的資源剛好被JobB的3個Pod所占用,同時在等待JobB的第4個Pod創建,此時整個集群就出現了死鎖。
?
社區相關的方案
社區目前有Kube-batch以及基于Kube-batch衍生的Volcano 2個項目來解決上文中提到的痛點。實現的方式是通過開發新的調度器將Scheduler中的調度單元從Pod修改為PodGroup,以組的形式進行調度。使用方式是如果需要Coscheduling功能的Pod走新的調度器,其他的例如在線服務的Pod走Kube-scheduler進行調度。
這些方案雖然能夠解決Coscheduling的問題,但是同樣引入了新的問題。如大家所知,對于同一集群資源,調度器需要中心化。但如果同時存在兩個調度器的話,有可能會出現決策沖突,例如分別將同一塊資源分配給兩個不同的Pod,導致某個Pod調度到節點后因為資源不足,導致無法創建的問題。解決的方式只能是通過標簽的形式將節點強行的劃分開來,或者部署多個集群。這種方式通過同一個Kubernetes集群來同時運行在線服務和離線作業,勢必會導致整體集群資源的浪費以及運維成本的增加。再者,Volcano運行需要啟動定制的MutatingAdmissionWebhook和ValidatingAdmissionWebhook。這些Webhooks本身存在單點風險,一旦出現故障,將影響集群內所有pod的創建。另外,多運行一套調度器,本身也會帶來維護上的復雜性,以及與上游Kube-scheduler接口兼容上的不確定性。
基于Scheduling Framework的方案
本系列第一篇《進擊的Kubernetes調度系統 (一):Scheduling Framework》介紹了Kubernetes Scheduling Framework的架構原理和開發方法。在此基礎上,我們擴展實現了Coscheduling調度插件,幫助Kubernetes原生調度器支持批作業調度,同時避免上述方案存在的問題。Scheduling framework的內容在前一篇文章詳細介紹,歡迎大家翻閱。
Kubernetes負責Kube-scheduler的小組sig-scheduler為了更好的管理調度相關的Plugin,新建了項目scheduler-plugins管理不同場景的Plugin。我們基于scheduling framework實現的Coscheduling Plugin成為該項目的第一個官方插件,下面我將詳細的介紹Coscheduling Plugin的實現和使用方式。
技術方案
總體架構
PodGroup
我們通過label的形式來定義PodGroup的概念,擁有同樣label的Pod同屬于一個PodGroup。min-available是用來標識該PodGroup的作業能夠正式運行時所需要的最小副本數。
labels:pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpupod-group.scheduling.sigs.k8s.io/min-available: "2"備注: 要求屬于同一個PodGroup的Pod必須保持相同的優先級
Permit
Framework的Permit插件提供了延遲綁定的功能,即Pod進入到Permit階段時,用戶可以自定義條件來允許Pod通過、拒絕Pod通過以及讓Pod等待狀態(可設置超時時間)。Permit的延遲綁定的功能,剛好可以讓屬于同一個PodGruop的Pod調度到這個節點時,進行等待,等待積累的Pod數目滿足足夠的數目時,再統一運行同一個PodGruop的所有Pod進行綁定并創建。
舉個實際的例子,當JobA調度時,需要4個Pod同時啟動,才能正常運行。但此時集群僅能滿足3個Pod創建,此時與Default Scheduler不同的是,并不是直接將3個Pod調度并創建。而是通過Framework的Permit機制進行等待。
此時當集群中有空閑資源被釋放后,JobA的中Pod所需要的資源均可以滿足。
則JobA的4個Pod被一起調度創建出來,正常運行任務。
QueueSort
由于Default Scheduler的隊列并不能感知PodGroup的信息,所以Pod在出隊時處于無序性(針對PodGroup而言)。如下圖所示,a和b表示兩個不同的PodGroup,兩個PodGroup的Pod在進入隊列時,由于創建的時間交錯導致在隊列中以交錯的順序排列。
當一個新的Pod創建后,入隊后,無法跟與其相同的PodGroup的Pod排列在一起,只能繼續以混亂的形式交錯排列。
這種無序性就會導致如果PodGroupA在Permit階段處于等待狀態,此時PodGroupB的Pod調度完成后也處于等待狀態,相互占有資源使得PodGroupA和PodGroupB均無法正常調度。這種情況即是把死鎖現象出現的位置從Node節點移動到Permit階段,無法解決前文提到的問題。
針對如上所示的問題,我們通過實現QueueSort插件, 保證在隊列中屬于同一個PodGroup的Pod能夠排列在一起。我們通過定義QueueSort所用的Less方法,作用于Pod在入隊后排隊的順序:
func Less(podA *PodInfo, podB *PodInfo) bool首先,繼承了默認的基于優先級的比較方式,高優先級的Pod會排在低優先級的Pod之前。
然后,如果兩個Pod的優先級相同,我們定義了新的排隊邏輯來支持PodGroup的排序。
通過如上的排隊策略,我們實現屬于同一個PodGroup的Pod能夠同一個PodGroup的Pod能夠排列在一起。
當一個新的Pod創建后,入隊后,會跟與其相同的PodGroup的Pod排列在一起。
Prefilter
為了減少無效的調度操作,提升調度的性能,我們在Prefilter階段增加一個過濾條件,當一個Pod調度時,會計算該Pod所屬PodGroup的Pod的Sum(包括Running狀態的),如果Sum小于min-available時,則肯定無法滿足min-available的要求,則直接在Prefilter階段拒絕掉,不再進入調度的主流程。
UnReserve
如果某個Pod在Permit階段等待超時了,則會進入到UnReserve階段,我們會直接拒絕掉所有跟Pod屬于同一個PodGroup的Pod,避免剩余的Pod進行長時間的無效等待。
Coscheduling試用
安裝部署
用戶既可以在自己搭建的Kubernetes集群中,也可以在任一個公有云提供的標準Kubernetes服務中來試用Coscheduling。需要注意的是集群版本1.16+, 以及擁有更新集群master的權限。
本文將使用?阿里云容器服務 ACK?提供的Kubernetes集群來進行測試。
前提條件
部署Coscheduling
我們已經將Coscheduling插件和原生調度器代碼統一構建成新的容器鏡像。并提供了一個helm chart包 ack-coscheduling來自動安裝。它會啟動一個任務,自動用Coscheduling scheduler替換集群默認安裝的原生scheduler,并且會修改scheduler的相關Config文件,使scheduling framework正確地加載Coscheduling插件。完成試用后,用戶可通過下文提示的卸載功能恢復集群默認scheduler及相關配置。
下載helm chart包,執行命令安裝
$ wget http://kubeflow.oss-cn-beijing.aliyuncs.com/ack-coscheduling.tar.gz $ tar zxvf ack-coscheduling.tar.gz $ helm install ack-coscheduling -n kube-system ./ack-coscheduling NAME: ack-coscheduling LAST DEPLOYED: Mon Apr 13 16:03:57 2020 NAMESPACE: kube-system STATUS: deployed REVISION: 1 TEST SUITE: None驗證Coscheduling
在Master節點上,使用helm命令驗證是否安裝成功。
$ helm get manifest ack-coscheduling -n kube-system | kubectl get -n kube-system -f - NAME COMPLETIONS DURATION AGE scheduler-update-clusterrole 1/1 8s 35s scheduler-update 3/1 of 3 8s 35s### 卸載Coscheduling
通過helm卸載,將kube-scheduler的版本及配置回滾到集群默認的狀態。
使用方式
使用Coscheduling時,只需要在創建任務的yaml描述中配置pod-group.scheduling.sigs.k8s.io/name和pod-group.scheduling.sigs.k8s.io/min-available這兩個label即可。
labels:pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpupod-group.scheduling.sigs.k8s.io/min-available: "3"pod-group.scheduling.sigs.k8s.io/name:用于表示PodGroup的Name
pod-group.scheduling.sigs.k8s.io/min-available: 用于表示當前集群資源至少滿足min-available個pod啟動時,才能整體調度該任務
備注: 屬于同一個PodGroup的Pod必須保持相同的優先級
Demo展示
接下來我們通過運行Tensorflow的分布式訓練作業來演示Coscheduling的效果。當前測試集群有4個GPU卡
Arena是基于Kubernetes的機器學習系統開源社區Kubeflow中的子項目之一。Arena用命令行和SDK的形式支持了機器學習任務的主要生命周期管理(包括環境安裝,數據準備,到模型開發,模型訓練,模型預測等),有效提升了數據科學家工作效率。
git clone https://github.com/kubeflow/arena.git kubectl create ns arena-system kubectl create -f arena/kubernetes-artifacts/jobmon/jobmon-role.yaml kubectl create -f arena/kubernetes-artifacts/tf-operator/tf-crd.yaml kubectl create -f arena/kubernetes-artifacts/tf-operator/tf-operator.yaml檢查是否部署成功
$ kubectl get pods -n arena-system NAME READY STATUS RESTARTS AGE tf-job-dashboard-56cf48874f-gwlhv 1/1 Running 0 54s tf-job-operator-66494d88fd-snm9m 1/1 Running 0 54s刪除上述TFJob yaml中的pod-group.scheduling.sigs.k8s.io/name和pod-group.scheduling.sigs.k8s.io/min-available標簽,表示該任務不使用Coscheduling。創建任務后,集群資源只能滿足2個Worker啟動,剩余兩個處于Pending狀態。
$ kubectl get pods NAME READY STATUS RESTARTS AGE tf-smoke-gpu-ps-0 1/1 Running 0 6m43s tf-smoke-gpu-worker-0 1/1 Running 0 6m43s tf-smoke-gpu-worker-1 1/1 Running 0 6m43s tf-smoke-gpu-worker-2 0/1 Pending 0 6m43s tf-smoke-gpu-worker-3 0/1 Pending 0 6m43s查看其中正在運行的Worker的日志,都處于等待剩余那兩個Worker啟動的狀態。此時,4個GPU都被占用卻沒有實際執行任務。
$ kubectl logs -f tf-smoke-gpu-worker-0 INFO|2020-05-19T07:02:18|/opt/launcher.py|27| 2020-05-19 07:02:18.199696: I tensorflow/core/distributed_runtime/master.cc:221] CreateSession still waiting for response from worker: /job:worker/replica:0/task:3 INFO|2020-05-19T07:02:28|/opt/launcher.py|27| 2020-05-19 07:02:28.199798: I tensorflow/core/distributed_runtime/master.cc:221] CreateSession still waiting for response from worker: /job:worker/replica:0/task:2添加pod-group相關標簽后創建任務,因為集群的資源無法滿足用戶設定的min-available要求,則PodGroup無法正常調度,所有的Pod一直處于Pending狀態。
$ kubectl get pods NAME READY STATUS RESTARTS AGE tf-smoke-gpu-ps-0 0/1 Pending 0 43s tf-smoke-gpu-worker-0 0/1 Pending 0 43s tf-smoke-gpu-worker-1 0/1 Pending 0 43s tf-smoke-gpu-worker-2 0/1 Pending 0 43s tf-smoke-gpu-worker-3 0/1 Pending 0 43s此時,如果通過集群擴容,新增4個GPU卡,資源能滿足用戶設定的min-available要求,則PodGroup正常調度,4個Worker開始運行
$ kubectl get pods NAME READY STATUS RESTARTS AGE tf-smoke-gpu-ps-0 1/1 Running 0 3m16s tf-smoke-gpu-worker-0 1/1 Running 0 3m16s tf-smoke-gpu-worker-1 1/1 Running 0 3m16s tf-smoke-gpu-worker-2 1/1 Running 0 3m16s tf-smoke-gpu-worker-3 1/1 Running 0 3m16s查看其中一個Worker的日志,顯示訓練任務已經開始
$ kubectl logs -f tf-smoke-gpu-worker-0 INFO|2020-05-19T07:15:24|/opt/launcher.py|27| Running warm up INFO|2020-05-19T07:21:04|/opt/launcher.py|27| Done warm up INFO|2020-05-19T07:21:04|/opt/launcher.py|27| Step Img/sec loss INFO|2020-05-19T07:21:05|/opt/launcher.py|27| 1 images/sec: 31.6 +/- 0.0 (jitter = 0.0) 8.318 INFO|2020-05-19T07:21:15|/opt/launcher.py|27| 10 images/sec: 31.1 +/- 0.4 (jitter = 0.7) 8.343 INFO|2020-05-19T07:21:25|/opt/launcher.py|27| 20 images/sec: 31.5 +/- 0.3 (jitter = 0.7) 8.142后續工作
利用Kubernetes Scheduling Framework的機制實現了Coscheduling,解決了AI、數據計算類的批任務需要組合調度,同時減少資源浪費的問題。從而提升集群整體資源利用率。
我們將在本系列接下來的文章中詳細介紹更多針對批任務的調度策略,如Capacity Scheduling,多隊列管理等特性,以及在Scheduling Framework中的設計與實現。敬請期待。
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的支持批任务的Coscheduling/Gang scheduling的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云 EMR Delta Lake 在
- 下一篇: 【产品动态】一文详细解读智能数据构建产品