支持批任务的Coscheduling/Gang scheduling
作者:王慶璨 張凱
進擊的Kubernetes調(diào)度系統(tǒng)(一):Scheduling Framework
進擊的Kubernetes調(diào)度系統(tǒng)(二):支持批任務(wù)的Coscheduling/Gang scheduling
前言
首先我們來了解一下什么是Coscheduling和Gang scheduling。Wikipedia對?Coscheduling的定義是“在并發(fā)系統(tǒng)中將多個相關(guān)聯(lián)的進程調(diào)度到不同處理器上同時運行的策略”。在Coscheduling的場景中,最主要的原則是保證所有相關(guān)聯(lián)的進程能夠同時啟動。防止部分進程的異常,導(dǎo)致整個關(guān)聯(lián)進程組的阻塞。這種導(dǎo)致阻塞的部分異常進程,稱之為“碎片(fragement)”。
在Coscheduling的具體實現(xiàn)過程中,根據(jù)是否允許“碎片”存在,可以細(xì)分為Explicit Coscheduling,Local Coscheduling和Implicit Coscheduling。 其中Explicit Coscheduling就是大家常聽到的Gang Scheduling。Gang Scheduling要求完全不允許有“碎片”存在, 也就是“All or Nothing”。
我們將上述定義的概念對應(yīng)到Kubernetes中,就可以理解Kubernetes調(diào)度系統(tǒng)支持批任務(wù)Coscheduling的含義了。 一個批任務(wù)(關(guān)聯(lián)進程組)包括了N個Pod(進程),Kubernetes調(diào)度器負(fù)責(zé)將這N個Pod調(diào)度到M個節(jié)點(處理器)上同時運行。如果這個批任務(wù)需要部分Pod同時啟動即可運行,我們稱需啟動Pod的最小數(shù)量為min-available。特別地,當(dāng)min-available=N時,批任務(wù)要求滿足Gang Scheduling。
為什么Kubernetes調(diào)度系統(tǒng)需要Coscheduling?
Kubernetes目前已經(jīng)廣泛的應(yīng)用于在線服務(wù)編排,為了提升集群的的利用率和運行效率,我們希望將Kubernetes作為一個統(tǒng)一的管理平臺來管理在線服務(wù)和離線作業(yè)。默認(rèn)的調(diào)度器是以Pod為調(diào)度單元進行依次調(diào)度,不會考慮Pod之間的相互關(guān)系。但是很多數(shù)據(jù)計算類的離線作業(yè)具有組合調(diào)度的特點,即要求所有的子任務(wù)都能夠成功創(chuàng)建后,整個作業(yè)才能正常運行。如果只有部分子任務(wù)啟動的話,啟動的子任務(wù)將持續(xù)等待剩余的子任務(wù)被調(diào)度。這正是Gang Scheduling的場景。
如下圖所示,JobA需要4個Pod同時啟動,才能正常運行。Kube-scheduler依次調(diào)度3個Pod并創(chuàng)建成功。到第4個Pod時,集群資源不足,則JobA的3個Pod處于空等的狀態(tài)。但是它們已經(jīng)占用了部分資源,如果第4個Pod不能及時啟動的話,整個JobA無法成功運行,更糟糕的是導(dǎo)致集群資源浪費。
?
如果出現(xiàn)更壞的情況的話,如下圖所示,集群其他的資源剛好被JobB的3個Pod所占用,同時在等待JobB的第4個Pod創(chuàng)建,此時整個集群就出現(xiàn)了死鎖。
?
社區(qū)相關(guān)的方案
社區(qū)目前有Kube-batch以及基于Kube-batch衍生的Volcano 2個項目來解決上文中提到的痛點。實現(xiàn)的方式是通過開發(fā)新的調(diào)度器將Scheduler中的調(diào)度單元從Pod修改為PodGroup,以組的形式進行調(diào)度。使用方式是如果需要Coscheduling功能的Pod走新的調(diào)度器,其他的例如在線服務(wù)的Pod走Kube-scheduler進行調(diào)度。
這些方案雖然能夠解決Coscheduling的問題,但是同樣引入了新的問題。如大家所知,對于同一集群資源,調(diào)度器需要中心化。但如果同時存在兩個調(diào)度器的話,有可能會出現(xiàn)決策沖突,例如分別將同一塊資源分配給兩個不同的Pod,導(dǎo)致某個Pod調(diào)度到節(jié)點后因為資源不足,導(dǎo)致無法創(chuàng)建的問題。解決的方式只能是通過標(biāo)簽的形式將節(jié)點強行的劃分開來,或者部署多個集群。這種方式通過同一個Kubernetes集群來同時運行在線服務(wù)和離線作業(yè),勢必會導(dǎo)致整體集群資源的浪費以及運維成本的增加。再者,Volcano運行需要啟動定制的MutatingAdmissionWebhook和ValidatingAdmissionWebhook。這些Webhooks本身存在單點風(fēng)險,一旦出現(xiàn)故障,將影響集群內(nèi)所有pod的創(chuàng)建。另外,多運行一套調(diào)度器,本身也會帶來維護上的復(fù)雜性,以及與上游Kube-scheduler接口兼容上的不確定性。
基于Scheduling Framework的方案
本系列第一篇《進擊的Kubernetes調(diào)度系統(tǒng) (一):Scheduling Framework》介紹了Kubernetes Scheduling Framework的架構(gòu)原理和開發(fā)方法。在此基礎(chǔ)上,我們擴展實現(xiàn)了Coscheduling調(diào)度插件,幫助Kubernetes原生調(diào)度器支持批作業(yè)調(diào)度,同時避免上述方案存在的問題。Scheduling framework的內(nèi)容在前一篇文章詳細(xì)介紹,歡迎大家翻閱。
Kubernetes負(fù)責(zé)Kube-scheduler的小組sig-scheduler為了更好的管理調(diào)度相關(guān)的Plugin,新建了項目scheduler-plugins管理不同場景的Plugin。我們基于scheduling framework實現(xiàn)的Coscheduling Plugin成為該項目的第一個官方插件,下面我將詳細(xì)的介紹Coscheduling Plugin的實現(xiàn)和使用方式。
技術(shù)方案
總體架構(gòu)
PodGroup
我們通過label的形式來定義PodGroup的概念,擁有同樣label的Pod同屬于一個PodGroup。min-available是用來標(biāo)識該PodGroup的作業(yè)能夠正式運行時所需要的最小副本數(shù)。
labels:pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpupod-group.scheduling.sigs.k8s.io/min-available: "2"備注: 要求屬于同一個PodGroup的Pod必須保持相同的優(yōu)先級
Permit
Framework的Permit插件提供了延遲綁定的功能,即Pod進入到Permit階段時,用戶可以自定義條件來允許Pod通過、拒絕Pod通過以及讓Pod等待狀態(tài)(可設(shè)置超時時間)。Permit的延遲綁定的功能,剛好可以讓屬于同一個PodGruop的Pod調(diào)度到這個節(jié)點時,進行等待,等待積累的Pod數(shù)目滿足足夠的數(shù)目時,再統(tǒng)一運行同一個PodGruop的所有Pod進行綁定并創(chuàng)建。
舉個實際的例子,當(dāng)JobA調(diào)度時,需要4個Pod同時啟動,才能正常運行。但此時集群僅能滿足3個Pod創(chuàng)建,此時與Default Scheduler不同的是,并不是直接將3個Pod調(diào)度并創(chuàng)建。而是通過Framework的Permit機制進行等待。
此時當(dāng)集群中有空閑資源被釋放后,JobA的中Pod所需要的資源均可以滿足。
則JobA的4個Pod被一起調(diào)度創(chuàng)建出來,正常運行任務(wù)。
QueueSort
由于Default Scheduler的隊列并不能感知PodGroup的信息,所以Pod在出隊時處于無序性(針對PodGroup而言)。如下圖所示,a和b表示兩個不同的PodGroup,兩個PodGroup的Pod在進入隊列時,由于創(chuàng)建的時間交錯導(dǎo)致在隊列中以交錯的順序排列。
當(dāng)一個新的Pod創(chuàng)建后,入隊后,無法跟與其相同的PodGroup的Pod排列在一起,只能繼續(xù)以混亂的形式交錯排列。
這種無序性就會導(dǎo)致如果PodGroupA在Permit階段處于等待狀態(tài),此時PodGroupB的Pod調(diào)度完成后也處于等待狀態(tài),相互占有資源使得PodGroupA和PodGroupB均無法正常調(diào)度。這種情況即是把死鎖現(xiàn)象出現(xiàn)的位置從Node節(jié)點移動到Permit階段,無法解決前文提到的問題。
針對如上所示的問題,我們通過實現(xiàn)QueueSort插件, 保證在隊列中屬于同一個PodGroup的Pod能夠排列在一起。我們通過定義QueueSort所用的Less方法,作用于Pod在入隊后排隊的順序:
func Less(podA *PodInfo, podB *PodInfo) bool首先,繼承了默認(rèn)的基于優(yōu)先級的比較方式,高優(yōu)先級的Pod會排在低優(yōu)先級的Pod之前。
然后,如果兩個Pod的優(yōu)先級相同,我們定義了新的排隊邏輯來支持PodGroup的排序。
通過如上的排隊策略,我們實現(xiàn)屬于同一個PodGroup的Pod能夠同一個PodGroup的Pod能夠排列在一起。
當(dāng)一個新的Pod創(chuàng)建后,入隊后,會跟與其相同的PodGroup的Pod排列在一起。
Prefilter
為了減少無效的調(diào)度操作,提升調(diào)度的性能,我們在Prefilter階段增加一個過濾條件,當(dāng)一個Pod調(diào)度時,會計算該Pod所屬PodGroup的Pod的Sum(包括Running狀態(tài)的),如果Sum小于min-available時,則肯定無法滿足min-available的要求,則直接在Prefilter階段拒絕掉,不再進入調(diào)度的主流程。
UnReserve
如果某個Pod在Permit階段等待超時了,則會進入到UnReserve階段,我們會直接拒絕掉所有跟Pod屬于同一個PodGroup的Pod,避免剩余的Pod進行長時間的無效等待。
Coscheduling試用
安裝部署
用戶既可以在自己搭建的Kubernetes集群中,也可以在任一個公有云提供的標(biāo)準(zhǔn)Kubernetes服務(wù)中來試用Coscheduling。需要注意的是集群版本1.16+, 以及擁有更新集群master的權(quán)限。
本文將使用?阿里云容器服務(wù) ACK?提供的Kubernetes集群來進行測試。
前提條件
部署Coscheduling
我們已經(jīng)將Coscheduling插件和原生調(diào)度器代碼統(tǒng)一構(gòu)建成新的容器鏡像。并提供了一個helm chart包 ack-coscheduling來自動安裝。它會啟動一個任務(wù),自動用Coscheduling scheduler替換集群默認(rèn)安裝的原生scheduler,并且會修改scheduler的相關(guān)Config文件,使scheduling framework正確地加載Coscheduling插件。完成試用后,用戶可通過下文提示的卸載功能恢復(fù)集群默認(rèn)scheduler及相關(guān)配置。
下載helm chart包,執(zhí)行命令安裝
$ 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節(jié)點上,使用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的版本及配置回滾到集群默認(rèn)的狀態(tài)。
使用方式
使用Coscheduling時,只需要在創(chuàng)建任務(wù)的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: 用于表示當(dāng)前集群資源至少滿足min-available個pod啟動時,才能整體調(diào)度該任務(wù)
備注: 屬于同一個PodGroup的Pod必須保持相同的優(yōu)先級
Demo展示
接下來我們通過運行Tensorflow的分布式訓(xùn)練作業(yè)來演示Coscheduling的效果。當(dāng)前測試集群有4個GPU卡
Arena是基于Kubernetes的機器學(xué)習(xí)系統(tǒng)開源社區(qū)Kubeflow中的子項目之一。Arena用命令行和SDK的形式支持了機器學(xué)習(xí)任務(wù)的主要生命周期管理(包括環(huán)境安裝,數(shù)據(jù)準(zhǔn)備,到模型開發(fā),模型訓(xùn)練,模型預(yù)測等),有效提升了數(shù)據(jù)科學(xué)家工作效率。
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標(biāo)簽,表示該任務(wù)不使用Coscheduling。創(chuàng)建任務(wù)后,集群資源只能滿足2個Worker啟動,剩余兩個處于Pending狀態(tài)。
$ 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啟動的狀態(tài)。此時,4個GPU都被占用卻沒有實際執(zhí)行任務(wù)。
$ 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相關(guān)標(biāo)簽后創(chuàng)建任務(wù),因為集群的資源無法滿足用戶設(shè)定的min-available要求,則PodGroup無法正常調(diào)度,所有的Pod一直處于Pending狀態(tài)。
$ 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卡,資源能滿足用戶設(shè)定的min-available要求,則PodGroup正常調(diào)度,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的日志,顯示訓(xùn)練任務(wù)已經(jīng)開始
$ 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后續(xù)工作
利用Kubernetes Scheduling Framework的機制實現(xiàn)了Coscheduling,解決了AI、數(shù)據(jù)計算類的批任務(wù)需要組合調(diào)度,同時減少資源浪費的問題。從而提升集群整體資源利用率。
我們將在本系列接下來的文章中詳細(xì)介紹更多針對批任務(wù)的調(diào)度策略,如Capacity Scheduling,多隊列管理等特性,以及在Scheduling Framework中的設(shè)計與實現(xiàn)。敬請期待。
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的支持批任务的Coscheduling/Gang scheduling的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云 EMR Delta Lake 在
- 下一篇: 【产品动态】一文详细解读智能数据构建产品