gang scheduler介紹:一個(gè)job,可能有多個(gè)tasks,這些tasks要不全部執(zhí)行,要不一個(gè)都不執(zhí)行。
上面說(shuō)到 kube-batch自定義了一個(gè)podGroup,調(diào)度時(shí)以podGroup為單位的?,F(xiàn)在說(shuō)一下調(diào)度podGroup的整個(gè)過(guò)程。
本文分為四個(gè)部分:
一:介紹kube-batch的基本概念
二:總結(jié)這些概念的關(guān)系,已經(jīng)列出可能的疑問(wèn)
三:介紹如何實(shí)現(xiàn)gang scheduler
四:總結(jié)gang scheduler的實(shí)現(xiàn)流程
第一二部分是為了幫助理解,第四部分是從代碼角度深入理解。想大概了解的,看到第三部分就可以了。
一:介紹kube-batch的基本概念:queue, podgroup, job。
(1) queue
這個(gè)一個(gè)全局的概念,意思就是沒(méi)有Namespace的這一說(shuō)法。用這個(gè)概念的目的是為了實(shí)現(xiàn)多租戶。這里一個(gè)租戶就是一個(gè)隊(duì)列。kube-batch默認(rèn)有一個(gè)default-queue。如果用戶創(chuàng)建的任務(wù)沒(méi)有指定隊(duì)列,都會(huì)放在默認(rèn)的這個(gè)隊(duì)列里。
type QueueInfo struct {
UID QueueID
Name string
Weight int32
Queue *arbcorev1.Queue
}
查看 queue 的定義,發(fā)現(xiàn)這里有個(gè) Weight的屬性。這個(gè)干什么用的呢?
繼續(xù)往下。
proportion.go:116
// Calculates the deserved of each Queue.
deserved := api.EmptyResource()
for _, attr := range pp.queueOpts {
glog.V(4).Infof("Considering Queue <%s>: weight <%d>, total weight <%d>.",
attr.name, attr.weight, totalWeight)
if _, found := meet[attr.queueID]; found {
continue
}
attr.deserved.Add(remaining.Clone().Multi(float64(attr.weight) / float64(totalWeight)))
if !attr.deserved.LessEqual(attr.request) {
attr.deserved = helpers.Min(attr.deserved, attr.request)
meet[attr.queueID] = struct{}{}
}
pp.updateShare(attr)
glog.V(4).Infof("The attributes of queue <%s> in proportion: deserved <%v>, allocate <%v>, request <%v>, share <%0.2f>",
attr.name, attr.deserved, attr.allocated, attr.request, attr.share)
deserved.Add(attr.deserved)
}
可以看出來(lái),"Weight" 是分配資源用的。一個(gè)Queue代表一個(gè)租戶,它們按照權(quán)重分配集群的資源。下面這個(gè)圖清晰明了:

(2)job
這里很容易弄混,kube-batch的job 和 k8s的job是不一樣的。
type JobInfo struct {
UID JobID
Name string
Namespace string
Queue QueueID
Priority int32
NodeSelector map[string]string
MinAvailable int32
NodesFitDelta NodeResourceMap
// All tasks of the Job.
TaskStatusIndex map[TaskStatus]tasksMap
Tasks tasksMap
Allocated *Resource
TotalRequest *Resource
CreationTimestamp metav1.Time
PodGroup *v1alpha1.PodGroup
// TODO(k82cn): keep backward compatibility, removed it when v1alpha1 finalized.
PDB *policyv1.PodDisruptionBudget
}
這是kube-batch 中job的定義,這里要理解成: kube-batch job是一堆Task的集合(1個(gè)Task是一個(gè)pod)。
這里的job和K8s中job的區(qū)別是:
如果有一個(gè)k8s的job, 假設(shè)為Job1,有2個(gè)pod, 他指定的podgroup是gp1.
同時(shí)還有一個(gè)k8s的job, 假設(shè)為Job2,有2個(gè)pod, 他指定的podgroup也是gp1.
如果同時(shí)提交這倆個(gè)k8s job。那么在kube-batch看來(lái),當(dāng)前要調(diào)度的只有一個(gè)"job"(這是kube-batch的一個(gè)作業(yè))。這個(gè)kube-batch job總共有4個(gè)Tasks(pod)需要綁定。
所以,kube-batch的job,它是指向某個(gè)podgroup 所有的k8s job的pod集合。
有點(diǎn)拗口,結(jié)合上面例子再讀讀。
(3) podgroup
我原本以為一個(gè)podgroup對(duì)應(yīng)的是一個(gè)k8s job。后面我發(fā)現(xiàn):
a. 我運(yùn)行一個(gè)job不指定podgroup也能正常運(yùn)行。
b. 我同時(shí)運(yùn)行倆個(gè)job,指定的是同一個(gè)podgroup也能正常運(yùn)行。
所以,我就非常奇怪,看了相關(guān)源碼后發(fā)現(xiàn),這里的podgroup是一個(gè)pod的集合。
如果有n個(gè)k8s job指向他,那么他就是這個(gè)n個(gè)Job的所有pod的集合。
二:總結(jié)這些概念的關(guān)系
(1)提交每個(gè)k8s job都必須指定一個(gè)podgroup
Q: 為什么創(chuàng)建job時(shí),不指定也能運(yùn)行?
A: kube-batch會(huì)為這類(lèi)job,創(chuàng)建一個(gè) shadow PodGroup
(2)一個(gè)podGroup對(duì)應(yīng)一個(gè)queue
Q:為什么創(chuàng)建queue時(shí),不指定也能運(yùn)行
A:不指定,默認(rèn)使用default-queue
(3)一個(gè)kube-batch中的job 包含podgroup中所有的pod.
(4)這個(gè)kube-batch的job使用 queue中的資源,完成task
三:介紹如何實(shí)現(xiàn)gang scheduler
結(jié)合圖片和文字一起看,能加深理解

這里以倆個(gè)k8s job為例介紹這個(gè)過(guò)程
(1)將指向某個(gè)podgroup的 所有k8s job中的pod合在一起生成一個(gè) kube-batch job. (比如上面圖中,最后kube-batch job的Tasks是 n1+n2)
(2) 開(kāi)始調(diào)度kube-batch中的job
(3) 每次完成一個(gè)task(這里是假裝綁定一個(gè)pod),當(dāng)完成的task數(shù)量 達(dá)到MIN(podgroup設(shè)定的值)時(shí),開(kāi)始真正的綁定,一次性綁定MIN個(gè)task。然后將剩下的Tasks 生成一個(gè)新的kube-batch job 再次調(diào)度。如果在沒(méi)達(dá)到MIN 個(gè)之前,已經(jīng)資源不足,那么進(jìn)入backfill 操作,釋放之前綁定的job。
所以實(shí)現(xiàn)gang scheduler的關(guān)鍵還是 podgroup,通過(guò)設(shè)置podgroup的minNumber。達(dá)到每次調(diào)度要么 執(zhí)行minNumber個(gè)tasks.要么一個(gè)都不執(zhí)行。
PS:
上面的圖片是為了解釋, 所以放了倆個(gè)k8s job.
如果想讓這倆個(gè)job進(jìn)行g(shù)ang scheduer, 直接令 minNumber = n1+n2.
這樣倆個(gè)job就會(huì)同時(shí)執(zhí)行。
四:總結(jié)gang scheduler的實(shí)現(xiàn)流程
我一開(kāi)始非常納悶為什么podgroup不是和k8s job一一對(duì)應(yīng)。后面我仔細(xì)想想,發(fā)現(xiàn)這樣設(shè)計(jì),更加靈活。
如果是k8s job和podgroup一一對(duì)應(yīng)的話。那么podgroup包含的就是這個(gè)k8s job的所有pod.這樣每次就只能對(duì)一個(gè)k8s job進(jìn)行g(shù)ang scheduler.
而他這樣設(shè)計(jì),則同時(shí)進(jìn)行一個(gè)或者多個(gè)K8s job的調(diào)度。
(通過(guò)調(diào)整n1,n2以及minNumber的值)
注意:
這樣也可能產(chǎn)生不好的影響。
例如我有兩個(gè)K8s job.描述如下:
job1 需要6個(gè)pod,1個(gè)pod需要1個(gè)cpu
job2 需要6個(gè)pod,1個(gè)pod需要100個(gè)cpu
現(xiàn)在集群總共有6個(gè)cpu.
如果我不小心,將job1,job2的podgroup設(shè)置成一樣。那么這次就會(huì)出現(xiàn)死鎖,這倆個(gè)job都不會(huì)執(zhí)行。
原因:
此時(shí)只有一個(gè)kube-batch的job,并且Tasks隊(duì)列很有可能為:
job1-pod1, job2-pod1,job1-pod1......
這樣當(dāng)創(chuàng)建到j(luò)ob2-pod1, 這個(gè)任務(wù)時(shí),已經(jīng)資源不足了。然后釋放資源,重新再來(lái)。這樣就會(huì)一直死循環(huán)。
所以,要靈活使用Podgroup.
本來(lái)想再加一個(gè)步驟:“從代碼中找出gang scheduler這個(gè)過(guò)程"
由于篇幅和時(shí)間的原因,下篇文章在介紹。
從代碼角度查找,能理解更多的細(xì)節(jié)。
比如有多個(gè)隊(duì)列,每個(gè)隊(duì)列有多個(gè)任務(wù),那么kubu-batch首先運(yùn)行哪個(gè)呢?