背景

最近工作聚焦在机器学习这块,公司采用的kubeflow框架中的tfjob,利用tersorflow进行分布式训练。
在训练过程中,发现在整体资源不足的情况下,由于k8s默认调度器调度的粒度是pod,但是tfjob可能对应多个pod, 多个tfjob可能会互相争抢资源而陷入资源死锁。
于是开始调研解决方案,优先考虑开源的方案,最终发现2种解决方案:

  1. 第一种是kube-batch及从此之上衍生的volcano, 可以作为k8s第二调度器,对机器学习、大数据等job类任务批调度做了特殊优化
  2. 第二种是k8s默认调度器自1.16版本后开始支持批调度插件,原生就支持批调度

由于公司的k8s是1.13, 升级k8s版本可能会有未知的异常,决定优先调研volcano

volcano调研

项目路径:https://github.com/volcano-sh/volcano

volcano是华为云开源的针对ai/大数据场景的批调度服务,脱胎于kube-batch, kube-batch是kubeflow原生的批调度器项目,volcano在此基础上提供了增强型的volcano scheduler, 并且还添加了更多的特性,例如提供了一个contorller manager用于管理volcano自己可拓展的crd job (相当于实现了一个operator)、queue、podgroup等资源,还有vctl命令行工具。

volcano调度过程如下:

k8s 动态pvc volumeClaimTemplates k8s 动态调度工具_解决方案


volcano scheduler调度时监控的是pod group,不是自己的crd volcano job, 这样设计可以让其他类型的crd job也可以使用volcano的调度,其他类型的crd job只要创建出volcano的podgroup就可以接入volcano的调度中去。

volcano当前可以无缝对接Tensorflow、Spark、PyTorch、 MPI、kubeflow等通用领域框架。

kubeflow tfjob && volcano

kubeflow原生已经支持使用volcano scheduler, 官网文档介绍了如何使用volcano scheduler实现tfjob的批调度:

https://www.kubeflow.org/docs/components/training/job-scheduling/#

具体操作是:

1.部署volcano
2.tf-operator增加–enable-gang-scheduling=true参数即可
总体来说算是可插拔式,不需要侵入代码修改。

通过使用volcano scheduler的gang-scheduling后,一个job必须等待所有pod都有资源才能启动,否则就陷入等待,避免了多个job都只有部分pod启动陷入死锁等待中。

实现原理是:

k8s 动态pvc volumeClaimTemplates k8s 动态调度工具_解决方案_02


增加enable-gang-scheduling调度后,提交一个tfjob任务后,tf-operator会将tfjob这个crd转化成volcano的podgroup的crd,并且会在podgroup crd上添加一些调度信息。

后面podgroup会由volcano来处理,volcano scheduler会代替k8s default scheduler去监控podgroup的信息,把它的信息拉到自己的组件里,根据策略对podgroup里的pod统一进行调度,Kubelet监控到Pod被调度出去了,会拿到对应本机的Pod,并将其启动起来。

调研结果

1.volcano确实可以实现tfjob的批调度,在资源不足的时候所有pod会处于pending状态,等资源足够才会统一启动
2.当然volcano也有缺陷:

  • 如果namespace有resourcequota资源限制,volcano批调度会失去作用,因为volcano会等待所有pod创建之后统一批调度,在开始调度之前所有pod处于pending状态,但是pending的pod会占用resourcequota资源,如果同时提交两个tfjob,resourcequota不够同时运行两个tfjob, 这两个tfjob创建的pod会抢占资源并处于pending状态,但是由于资源不足,volcano检测pod没有全部创建所以不会调度,还是会造成资源等待死锁,此bug已反馈给社区
  • 当前volcano只支持调度tfjob,如果环境中有其他类型任务,volcano可能会和k8s默认调度器产生冲突