深入分析kube-batch(4)——actions
action是真正的調(diào)度過程误墓,順序是reclaim -> allocate -> backfill -> preempt
interface
// Action is the interface of scheduler action.
type Action interface {
// The unique name of Action.
Name() string
// Initialize initializes the allocator plugins.
Initialize()
// Execute allocates the cluster's resources into each queue.
Execute(ssn *Session)
// UnIntialize un-initializes the allocator plugins.
UnInitialize()
}
重點(diǎn)關(guān)注Execute
實(shí)現(xiàn)
reclaim
kube-batch\pkg\scheduler\actions\reclaim\reclaim.go
func (alloc *reclaimAction) Execute(ssn *framework.Session) {
queues := util.NewPriorityQueue(ssn.QueueOrderFn)
preemptorsMap := map[api.QueueID]*util.PriorityQueue{}
preemptorTasks := map[api.JobID]*util.PriorityQueue{}
for _, job := range ssn.Jobs {
queues.Push(queue)
if len(job.TaskStatusIndex[api.Pending]) != 0 {
preemptorsMap[job.Queue].Push(job)
for _, task := range job.TaskStatusIndex[api.Pending] {
preemptorTasks[job.UID].Push(task)
}
}
}
- 根據(jù)優(yōu)先級排序queue
- 將待調(diào)度的task保存為搶占者
for {
if queues.Empty() {
break
}
queue := queues.Pop().(*api.QueueInfo)
jobs, found := preemptorsMap[queue.UID]
tasks, found := preemptorTasks[job.UID]
resreq := task.Resreq.Clone()
reclaimed := api.EmptyResource()
assigned := false
for _, n := range ssn.Nodes {
if err := ssn.PredicateFn(task, n); err != nil {
continue
}
var reclaimees []*api.TaskInfo
for _, task := range n.Tasks {
if task.Status != api.Running {
continue
}
reclaimees = append(reclaimees, task.Clone())
}
victims := ssn.Reclaimable(task, reclaimees)
if len(victims) == 0 {
continue
}
// If not enough resource, continue
allRes := api.EmptyResource()
for _, v := range victims {
allRes.Add(v.Resreq)
}
if allRes.Less(resreq) {
continue
}
// Reclaim victims for tasks.
for _, reclaimee := range victims {
ssn.Evict(reclaimee, "reclaim")
reclaimed.Add(reclaimee.Resreq)
if resreq.LessEqual(reclaimee.Resreq) {
break
}
resreq.Sub(reclaimee.Resreq)
}
break
}
}
- 找到優(yōu)先級最高的queue赁咙,job,task
- 遍歷node奸鬓,首先過預(yù)選函數(shù)豪硅,很奇怪拷窜,沒有
PodFitsResources
,應(yīng)該是kube-batch
自己管理資源 - 找到node上正在運(yùn)行的pod
- 找到受害者
- 如果受害者資源總量小于pod申請資源總量锡足,就跳過
- 驅(qū)逐受害者,調(diào)用刪除接口
- 如果釋放足夠的資源壳坪,就跳出驅(qū)逐
reclaim過程目前還沒遇到過舶得,回收函數(shù)也不是很理解。我覺得回收不是很必要爽蝴,驅(qū)逐邏輯不應(yīng)該在這里做沐批,kubelet已經(jīng)有了驅(qū)逐邏輯纫骑,不是很明白reclaim的必要性。而且不是每次都需要回收珠插,應(yīng)該判斷node是否自愿不足惧磺。我會(huì)在配置中移除reclaim的action,還能提高性能捻撑。
allocate
for !tasks.Empty() {
task := tasks.Pop().(*api.TaskInfo)
for _, node := range ssn.Nodes {
if err := ssn.PredicateFn(task, node); err != nil {
continue
}
// Allocate idle resource to the task.
if task.Resreq.LessEqual(node.Idle) {
ssn.Allocate(task, node.Name)
break
}
}
}
分配過程只看最核心的部分磨隘,
- 過一遍預(yù)選函數(shù),
- 比較pod資源申請和node空閑資源
- bind
這里解決了上面的疑問顾患,預(yù)選函數(shù)中沒有PodFitsResources
番捂,是因?yàn)樵谶@里實(shí)現(xiàn)了類似功能;不過又多了一個(gè)疑問江解,這里如果node滿足pod要求设预,那么就直接bind了?沒有優(yōu)選過程嗎犁河?那soft親和性怎么辦鳖枕?
backfill
func (alloc *backfillAction) Execute(ssn *framework.Session) {
for _, job := range ssn.Jobs {
for _, task := range job.TaskStatusIndex[api.Pending] {
if task.Resreq.IsEmpty() {
for _, node := range ssn.Nodes {
ssn.PredicateFn(task, node);
ssn.Allocate(task, node.Name)
break
}
}
}
}
}
backfill是為了處理BestEffort
后加的action,相關(guān)issue桨螺。
preempt
搶占邏輯一直沒有很理解宾符,這里先暫時(shí)不分析了彬碱,會(huì)另外開一篇文章專門介紹搶占或辖,不過kube-batch的搶占跟K8S的不太一樣芝加。
總結(jié)
學(xué)習(xí)了馬達(dá)老師的kube-batch為我打開了一個(gè)新思路腹缩,不過對于我的項(xiàng)目可能有點(diǎn)重遣妥,不是很需要回收和搶占邏輯五督,還有貌似也不支持優(yōu)選邏輯兴猩,沒有優(yōu)選就沒有soft親和性埂陆,這個(gè)是個(gè)非常致命的問題煌张,所以后期應(yīng)該準(zhǔn)備參考default-scheduler和kube-batch自己寫一個(gè)調(diào)度器了呐赡。