什么是批处理任务
深度学习中经常会出现多机多卡的任务,也就是同事会起多个pod,但是这多个pod属于同一个任务。
这样就会有一个问题
一个任务要起100个pod,每个pod需要一张卡,总共需要100张GPU卡,而集群中只有99张空闲的GPU卡,这样默认的k8s调度器会如何处理?
因为默认调度器是一个一个pod调度的,只会检查单个pod资源够不够,这样前99个都能成功,最后一个pod调度失败。
这样非常有可能造成
- 任务跑不了
- 前99个占着GPU不释放,新的任务无法调度
- 严重时整个集群死锁,都“占着茅坑不拉屎”
所以需要在调度时对整个task所需所有资源进行检查,当集群总体资源不够时,一个pod都得不到调度。
社区提供了一个能支持这种特性的调度器
但是这个调度器是没办法和原生调度器很好的配合工作的
- 最大的问题在于两个调度器都有cache,这样cache的内容会冲突,导致调度混乱
- 这个调度器没法和原生调度器同时起作用,这样用了这个batch调度器后就没法用亲和性什么的特性了
所以我们做的事是将两者特性融合,选择的方法是定制化开发kube-scheduler
其实scheduler是可以通过extender扩展的,但是extender还是太弱了,它仅能在预选和优选过程中加入自己的过滤策略,而这对于批处理任务远远不够。
实现难点
需要优选时加batch任务检查
拿到一个pod —> 如果是一个batchpod —> 查询集群资源是否满足batch任务—>否调度失败
需要保障batch任务中其它pod能得到调度
如果集群资源能满足这个batch任务直接去bind有个问题:
假设调度队列是这样,假设集群中有三个GPU,而batch任务需要三个GPU:
A batch pod -> | pod -> | pod -> | A batch pod -> | A batch pod |
---|---|---|---|---|
集群资源够 调度成功 | 调度了别的pod | 调度了别的pod | GPU被别的pod占用 GPU不够 失败 | GPU不够 失败 |
所以最终结果是A批任务占用了一个GPU但是整个任务是调度失败的,那一个GPU还得不到释放
所以需要修改pod调度队列里的顺序?让A batch pod连续调度? 没这么简单,
pod调度是创建协程并发调度的,这样即便去调整任务队列里pod的顺序也不一定能保证batch任务其它pod能得到优先调度。
go wait.Until(sched.scheduleOne, 0, sched.config.StopEverything)
只要batch pod走到Bind逻辑了就没有回头路了
batch任务中所有pod先进行assume调度,其中任意一个失败就清理掉其它已经bind但是还没实际进行调度的pod。 并把所有pod扔回队列,或者直接返回调度失败清理改任务的pod,让上层重新触发?
scheduler流程 scheduler/sheduler.go scheduleOne逻辑:
选节点->cache assume pod on node-> 创建协程bind
所以在assume时去检查,不满足退还已经调度的pod是不可行的,因为之前batch任务中的pod可能已经bind过了, 所以只能batch任务中最后一个pod得到确认才能去bind前面的pod