作者:王庆璨 张凯

进击的Kubernetes调度系统(一):Scheduling Framework
进击的Kubernetes调度系统(二):支持批任务的Coscheduling/Gang scheduling

前言

首先我们来了解一下什么是Coscheduling和Gang scheduling。Wikipedia对 Coscheduling的定义是“在并发系统中将多个相关联的进程调度到不同处理器上同时运行的策略”。在Coscheduling的场景中,最主要的原则是保证所有相关联的进程能够同时启动。防止部分进程的异常,导致整个关联进程组的阻塞。这种导致阻塞的部分异常进程,称之为“碎片(fragement)”。
在Coscheduling的具体实现过程中,根据是否允许“碎片”存在,可以细分为Explicit Coscheduling,Local Coscheduling和Implicit Coscheduling。 其中Explicit Coscheduling就是大家常听到的Gang Scheduling。Gang Scheduling要求完全不允许有“碎片”存在, 也就是“All or Nothing”。

我们将上述定义的概念对应到Kubernetes中,就可以理解Kubernetes调度系统支持批任务Coscheduling的含义了。 一个批任务(关联进程组)包括了N个Pod(进程),Kubernetes调度器负责将这N个Pod调度到M个节点(处理器)上同时运行。如果这个批任务需要部分Pod同时启动即可运行,我们称需启动Pod的最小数量为min-available。特别地,当min-available=N时,批任务要求满足Gang Scheduling。

为什么Kubernetes调度系统需要Coscheduling?

Kubernetes目前已经广泛的应用于在线服务编排,为了提升集群的的利用率和运行效率,我们希望将Kubernetes作为一个统一的管理平台来管理在线服务和离线作业。默认的调度器是以Pod为调度单元进行依次调度,不会考虑Pod之间的相互关系。但是很多数据计算类的离线作业具有组合调度的特点,即要求所有的子任务都能够成功创建后,整个作业才能正常运行。如果只有部分子任务启动的话,启动的子任务将持续等待剩余的子任务被调度。这正是Gang Scheduling的场景。

如下图所示,JobA需要4个Pod同时启动,才能正常运行。Kube-scheduler依次调度3个Pod并创建成功。到第4个Pod时,集群资源不足,则JobA的3个Pod处于空等的状态。但是它们已经占用了部分资源,如果第4个Pod不能及时启动的话,整个JobA无法成功运行,更糟糕的是导致集群资源浪费。

如果出现更坏的情况的话,如下图所示,集群其他的资源刚好被JobB的3个Pod所占用,同时在等待JobB的第4个Pod创建,此时整个集群就出现了死锁。

社区相关的方案

社区目前有Kube-batch以及基于Kube-batch衍生的Volcano 2个项目来解决上文中提到的痛点。实现的方式是通过开发新的调度器将Scheduler中的调度单元从Pod修改为PodGroup,以组的形式进行调度。使用方式是如果需要Coscheduling功能的Pod走新的调度器,其他的例如在线服务的Pod走Kube-scheduler进行调度。

这些方案虽然能够解决Coscheduling的问题,但是同样引入了新的问题。如大家所知,对于同一集群资源,调度器需要中心化。但如果同时存在两个调度器的话,有可能会出现决策冲突,例如分别将同一块资源分配给两个不同的Pod,导致某个Pod调度到节点后因为资源不足,导致无法创建的问题。解决的方式只能是通过标签的形式将节点强行的划分开来,或者部署多个集群。这种方式通过同一个Kubernetes集群来同时运行在线服务和离线作业,势必会导致整体集群资源的浪费以及运维成本的增加。再者,Volcano运行需要启动定制的MutatingAdmissionWebhook和ValidatingAdmissionWebhook。这些Webhooks本身存在单点风险,一旦出现故障,将影响集群内所有pod的创建。另外,多运行一套调度器,本身也会带来维护上的复杂性,以及与上游Kube-scheduler接口兼容上的不确定性。

基于Scheduling Framework的方案

本系列第一篇《进击的Kubernetes调度系统 (一):Scheduling Framework》介绍了Kubernetes Scheduling Framework的架构原理和开发方法。在此基础上,我们扩展实现了Coscheduling调度插件,帮助Kubernetes原生调度器支持批作业调度,同时避免上述方案存在的问题。Scheduling framework的内容在前一篇文章详细介绍,欢迎大家翻阅。

Kubernetes负责Kube-scheduler的小组sig-scheduler为了更好的管理调度相关的Plugin,新建了项目scheduler-plugins管理不同场景的Plugin。我们基于scheduling framework实现的Coscheduling Plugin成为该项目的第一个官方插件,下面我将详细的介绍Coscheduling Plugin的实现和使用方式。

技术方案

总体架构

PodGroup

我们通过label的形式来定义PodGroup的概念,拥有同样label的Pod同属于一个PodGroup。min-available是用来标识该PodGroup的作业能够正式运行时所需要的最小副本数。

labels:pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpupod-group.scheduling.sigs.k8s.io/min-available: "2"

备注: 要求属于同一个PodGroup的Pod必须保持相同的优先级

Permit

Framework的Permit插件提供了延迟绑定的功能,即Pod进入到Permit阶段时,用户可以自定义条件来允许Pod通过、拒绝Pod通过以及让Pod等待状态(可设置超时时间)。Permit的延迟绑定的功能,刚好可以让属于同一个PodGruop的Pod调度到这个节点时,进行等待,等待积累的Pod数目满足足够的数目时,再统一运行同一个PodGruop的所有Pod进行绑定并创建。

举个实际的例子,当JobA调度时,需要4个Pod同时启动,才能正常运行。但此时集群仅能满足3个Pod创建,此时与Default Scheduler不同的是,并不是直接将3个Pod调度并创建。而是通过Framework的Permit机制进行等待。

此时当集群中有空闲资源被释放后,JobA的中Pod所需要的资源均可以满足。

则JobA的4个Pod被一起调度创建出来,正常运行任务。

QueueSort

由于Default Scheduler的队列并不能感知PodGroup的信息,所以Pod在出队时处于无序性(针对PodGroup而言)。如下图所示,a和b表示两个不同的PodGroup,两个PodGroup的Pod在进入队列时,由于创建的时间交错导致在队列中以交错的顺序排列。

当一个新的Pod创建后,入队后,无法跟与其相同的PodGroup的Pod排列在一起,只能继续以混乱的形式交错排列。

这种无序性就会导致如果PodGroupA在Permit阶段处于等待状态,此时PodGroupB的Pod调度完成后也处于等待状态,相互占有资源使得PodGroupA和PodGroupB均无法正常调度。这种情况即是把死锁现象出现的位置从Node节点移动到Permit阶段,无法解决前文提到的问题。

针对如上所示的问题,我们通过实现QueueSort插件, 保证在队列中属于同一个PodGroup的Pod能够排列在一起。我们通过定义QueueSort所用的Less方法,作用于Pod在入队后排队的顺序:

func  Less(podA *PodInfo, podB *PodInfo) bool

首先,继承了默认的基于优先级的比较方式,高优先级的Pod会排在低优先级的Pod之前。
然后,如果两个Pod的优先级相同,我们定义了新的排队逻辑来支持PodGroup的排序。

  1. 如果两个Pod都是regularPod(普通的Pod),则谁先创建谁在队列里边排在前边。
  2. 如果两个Pod一个是regularPod,另一个是pgPod(属于某个PodGroup的Pod), 我们比较的是regularPod的创建时间和pgPod所属PodGroup的创建时间,则谁先创建谁在队列里边排在前边。
  3. 如果两个Pod都是pgPod,我们比较两个PodGroup的创建时间,则谁先创建谁在队列里边排在前边。同时有可能两个PodGroup的创建时间相同,我们引入了自增Id,使得PodGroup的Id谁小谁排在前边(此处的目的是为了区分不同的PodGroup)。

通过如上的排队策略,我们实现属于同一个PodGroup的Pod能够同一个PodGroup的Pod能够排列在一起。

当一个新的Pod创建后,入队后,会跟与其相同的PodGroup的Pod排列在一起。

Prefilter

为了减少无效的调度操作,提升调度的性能,我们在Prefilter阶段增加一个过滤条件,当一个Pod调度时,会计算该Pod所属PodGroup的Pod的Sum(包括Running状态的),如果Sum小于min-available时,则肯定无法满足min-available的要求,则直接在Prefilter阶段拒绝掉,不再进入调度的主流程。

UnReserve

如果某个Pod在Permit阶段等待超时了,则会进入到UnReserve阶段,我们会直接拒绝掉所有跟Pod属于同一个PodGroup的Pod,避免剩余的Pod进行长时间的无效等待。

Coscheduling试用

安装部署

用户既可以在自己搭建的Kubernetes集群中,也可以在任一个公有云提供的标准Kubernetes服务中来试用Coscheduling。需要注意的是集群版本1.16+, 以及拥有更新集群master的权限。
本文将使用 阿里云容器服务 ACK 提供的Kubernetes集群来进行测试。

前提条件

  1. 支持Kubernetes 1.16以上版本
  2. 选择创建ACK提供的标准专有集群(Dedicated cluster)
  3. 保证集群节点可以访问公网
  4. helm v3:ACK在master节点上默认安装helm,请确认版本是否为helm v3,如果是helm v2请升级值helm v3,安装helm v3请参考helm v3 安装。

部署Coscheduling

我们已经将Coscheduling插件和原生调度器代码统一构建成新的容器镜像。并提供了一个helm chart包 ack-coscheduling来自动安装。它会启动一个任务,自动用Coscheduling scheduler替换集群默认安装的原生scheduler,并且会修改scheduler的相关Config文件,使scheduling framework正确地加载Coscheduling插件。完成试用后,用户可通过下文提示的卸载功能恢复集群默认scheduler及相关配置。

下载helm chart包,执行命令安装

$  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节点上,使用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的版本及配置回滚到集群默认的状态。

$ helm uninstall ack-coscheduling -n kube-system

使用方式

使用Coscheduling时,只需要在创建任务的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: 用于表示当前集群资源至少满足min-available个pod启动时,才能整体调度该任务

备注: 属于同一个PodGroup的Pod必须保持相同的优先级

Demo展示

接下来我们通过运行Tensorflow的分布式训练作业来演示Coscheduling的效果。当前测试集群有4个GPU卡

  1. 通过Kubeflow的Arena工具在已有Kubernetes集群中部署Tensorflow作业运行环境。

Arena是基于Kubernetes的机器学习系统开源社区Kubeflow中的子项目之一。Arena用命令行和SDK的形式支持了机器学习任务的主要生命周期管理(包括环境安装,数据准备,到模型开发,模型训练,模型预测等),有效提升了数据科学家工作效率。

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
  1. 用户向集群中提交Tensorflow作业(TFJob),如下示例,包含1个Parameter Server pod和4个Worker pod,每个Worker需要2个GPU。配置了pod group,需要至少5个pod启动后才能运行。
apiVersion: "kubeflow.org/v1"
kind: "TFJob"
metadata:name: "tf-smoke-gpu"
spec:tfReplicaSpecs:PS:replicas: 1template:metadata:creationTimestamp: nulllabels:pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpupod-group.scheduling.sigs.k8s.io/min-available: "5" spec:containers:- args:- python- tf_cnn_benchmarks.py- --batch_size=32- --model=resnet50- --variable_update=parameter_server- --flush_stdout=true- --num_gpus=1- --local_parameter_device=cpu- --device=cpu- --data_format=NHWCimage: registry.cn-hangzhou.aliyuncs.com/kubeflow-images-public/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3name: tensorflowports:- containerPort: 2222name: tfjob-portresources:limits:cpu: '1'workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarksrestartPolicy: OnFailureWorker:replicas: 4template:metadata:creationTimestamp: nulllabels:pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpupod-group.scheduling.sigs.k8s.io/min-available: "5"spec:containers:- args:- python- tf_cnn_benchmarks.py- --batch_size=32- --model=resnet50- --variable_update=parameter_server- --flush_stdout=true- --num_gpus=1- --local_parameter_device=cpu- --device=gpu- --data_format=NHWCimage: registry.cn-hangzhou.aliyuncs.com/kubeflow-images-public/tf-benchmarks-gpu:v20171202-bdab599-dirty-284af3name: tensorflowports:- containerPort: 2222name: tfjob-portresources:limits:nvidia.com/gpu: 2workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarksrestartPolicy: OnFailure
  1. 当用户不使用Coscheduling功能时

删除上述TFJob yaml中的pod-group.scheduling.sigs.k8s.io/name和pod-group.scheduling.sigs.k8s.io/min-available标签,表示该任务不使用Coscheduling。创建任务后,集群资源只能满足2个Worker启动,剩余两个处于Pending状态。

$ 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启动的状态。此时,4个GPU都被占用却没有实际执行任务。

$ 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
  1. 当用户使用Coscheduling功能时

添加pod-group相关标签后创建任务,因为集群的资源无法满足用户设定的min-available要求,则PodGroup无法正常调度,所有的Pod一直处于Pending状态。

$ 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卡,资源能满足用户设定的min-available要求,则PodGroup正常调度,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的日志,显示训练任务已经开始

$ 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

后续工作

利用Kubernetes Scheduling Framework的机制实现了Coscheduling,解决了AI、数据计算类的批任务需要组合调度,同时减少资源浪费的问题。从而提升集群整体资源利用率。
我们将在本系列接下来的文章中详细介绍更多针对批任务的调度策略,如Capacity Scheduling,多队列管理等特性,以及在Scheduling Framework中的设计与实现。敬请期待。

原文链接
本文为云栖社区原创内容,未经允许不得转载。

支持批任务的Coscheduling/Gang scheduling相关推荐

  1. 进击的 Kubernetes 调度系统(二):支持批任务的 Coscheduling/Gang scheduling

    作者 | 王庆璨(阿里云技术专家).张凯(阿里云高级技术专家) **导读:**阿里云容器服务团队结合多年 Kubernetes 产品与客户支持经验,对 Kube-scheduler 进行了大量优化和扩 ...

  2. Kubernetes — 调度系统

    目录 文章目录 目录 调度系统 Kubernetes 调度器的设计 Kubernetes 调度器的工作流 Kubernetes 调度系统的未来 Scheduler Extender(调度器扩展) Mu ...

  3. 进击的 Kubernetes 调度系统(一):Kubernetes scheduling framework

    作者 | 王庆璨(阿里云技术专家).张凯(阿里云高级技术专家) 导读:阿里云容器服务团队结合多年 Kubernetes 产品与客户支持经验,对 Kube-scheduler 进行了大量优化和扩展,逐步 ...

  4. 邀您参与 | 阿里巴巴如何扩展 K8s 调度器支持 AI 和大数据任务?

    简介:2020 年 7 月 15 日上午 10:00,<阿里巴巴如何扩展 K8s 调度器支持 AI 和大数据任务?>主题线上网络研讨会即将召开. 随着 Kubernetes 的广泛应用,越 ...

  5. 官宣 | Apache Flink 1.12.0 正式发布,流批一体真正统一运行!

    ‍‍ 翻译 | 付典 Review | 徐榜江.朱翥 Apache Flink 社区很荣幸地宣布 Flink 1.12.0 版本正式发布!近 300 位贡献者参与了 Flink 1.12.0 的开发, ...

  6. 支持高性能计算场景,博云容器云打造智能算力引擎

    随着 Kubernetes 作为 AI.大数据和高性能批量计算的下一代基础设施的趋势逐渐清晰,越来越多的企业对 Kubernetes 在深度学习.科学计算.高性能渲染等方面提出了更高的要求. 项目挑战 ...

  7. Flink 1.11 与 Hive 批流一体数仓实践

    导读:Flink 从 1.9.0 开始提供与 Hive 集成的功能,随着几个版本的迭代,在最新的 Flink 1.11 中,与 Hive 集成的功能进一步深化,并且开始尝试将流计算场景与Hive 进行 ...

  8. hive表ddl导出_Flink 1.11 与 Hive 批流一体数仓实践

    简介:Flink 从 1.9.0 开始提供与 Hive 集成的功能,随着几个版本的迭代,在最新的 Flink 1.11 中,与 Hive 集成的功能进一步深化,并且开始尝试将流计算场景与Hive 进行 ...

  9. flink1.12.0学习笔记第2篇-流批一体API

    flink1.12.0学习笔记第 2 篇-流批一体API flink1.12.0学习笔记第1篇-部署与入门 flink1.12.0学习笔记第2篇-流批一体API flink1.12.0学习笔记第3篇- ...

最新文章

  1. Oracle在线重定义
  2. java enum 定义属性_java enum(枚举)使用详解 + 总结
  3. 新式类和经典类的区别类的特殊方法单例模式
  4. find vba 模糊_VBA积木代码方案:实现多重查找、模糊查找、清除值
  5. python编写请求参数带文件_转载:如何编写一个带命令行参数的Python文件
  6. ubuntu 18.04可以连接内网,无法连接外网
  7. 关于oracle with as用法
  8. [MySQL]--查询性能分析工具-explain关键字
  9. springboot医院门诊挂号病历管理系统
  10. ZigBee协议栈的串口实验
  11. pe服务器注册表,注册表修复方法
  12. uni-app云打包 No enum constant com.pandora.pack.core.PackagePlatform.ANDROID_CLC
  13. python绘图 ——蜡笔小新
  14. 小菜鸟的自我激励与不服输的心
  15. 语音处理 之 libritts,AIShell
  16. AI 腾讯云人脸核身之独立H5接入
  17. Oracle数据表导入导出_数据迁移用
  18. Windows7修改文件默认图标
  19. 文件上传中国菜刀 —— 【WUST-CTF2020】CV Maker
  20. android NFC读写卡Demo

热门文章

  1. 用python将word文档导入数据库_python读取word文档,插入mysql数据库的示例代码
  2. caffe不支持relu6_国产AI框架再进化!百度Paddle Lite发布:率先支持华为NPU在线编译,全新架构更多硬件支持...
  3. simulink仿真及代码生成技术入门到精通_Simulink仿真零基础入门到精通实用教学教程 自学全套...
  4. 英语作文谈谈你对计算机的看法,英语作文:谈谈你对网络语言的看法
  5. 创建一个storageevent事件_谈谈StorageEvent
  6. mysql游标的概述_MySQL游标简介
  7. python免费网络采集_python网络数据采集7 采集一个网站所有的外链
  8. 德国大学:如何改变一个民族和整个世界的命运
  9. 学位论文是根,学术论文是叶
  10. ​【文末有福利】为何美国的科研既能得诺贝尔奖,又能产生高科技产品?