基于Chronos和Docker的任务调度系统
相关背景
Mesos
Mesos是一个开源的分布式弹性资源管理系统。目前,Twitter、Apple等公司在大量使用Mesos管理集群资源。国内也有一些公司在使用Mesos,比如豆瓣、爱奇艺等。
Mesos是仿照Google内部的资源管理系统Borg实现的。Mesos对Docker的支持较好,其采用的资源分配算法叫做DRF.
Chronos
Chronos是一个运行在Mesos之上的具有分布式容错特性的作业调度器。在Airbnb公司,它是cron的替代品。与cron相比,Chronos在很多方面具备优势。比如,它支持ISO8601标准,允许更灵活地定义调度时间;Chronos也支持任务依赖,即一个作业的开始依赖于一些任务的完成。
Docker
Docker是一个开源的应用容器引擎,允许开发者打包应用以及相关依赖到一个可移植的容器中,然后发布到任何流行的Linux机器上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在数据中心运行。
系统结构
下图是这个任务调度系统的基本框图。
调度系统框图整体上来看,该系统由业务队列、业务调度器、Chronos和Mesos组成。每个组成部分的说明如下:
- 业务队列:实际存放任务的队列,队列中的任务可以是按照优先级排序,也可以是FIFO. 多个业务队列表示不同的业务,或者同一业务的不同队列。
- 业务调度器:根据一定的调度算法从多个业务队列中选择任务来调度。业务调度器接受Chronos的资源汇报,然后给这些资源分配任务。
- Chronos:向业务调度器汇报剩余资源,接受业务调度器提交的任务然后在Mesos集群上面运行。
- Mesos:运行各个Docker容器的载体。每个任务都在Docker容器中执行。
有一点需要解释的是,在这个任务调度系统中,存在多个调度器:
- Mesos中的调度器:一种通用的集群资源分配算法,也就是DRF
- Chronos中的调度器:在我们这个场景中,不需要定时调度任务。我们只需使用Chronos的Rest API提交一个Docker任务就可以了,这个Docker任务然后由Chronos调度到Mesos集群上面运行。为什么不绕过Chronos直接提交任务到Mesos呢?Mesos本身是个两级调度架构,如果绕过Chronos,那么业务调度器就需要实现Mesos的上级调度接口,增加了复杂性。
- 业务调度器:这就是和业务联系最紧密的地方了,此处必须考虑各种业务特性,比如各个业务队列的任务总量、到达速率、优先级等等。
系统实现
系统的核心在于业务调度器。经过调研各种调度算法,觉得YARN资源管理系统中的容量调度(Capacity Scheduler)适合多业务多队列的场景。
容量调度
以两级队列为例来说明容量调度算法。下图是Chronos资源的容量配置示例:
Chronos容量分配在该示意图中,Chronos资源容量的20%分配给了业务1,80%分配给了业务2. 接着,20%容量中的40%分配给了业务队列11,60%分配给了业务队列12. 业务1和业务2是业务层次的队列,业务队列11、业务队列12和业务队列21是具体业务下的子队列。
父队列
父队列具有如下基本属性:
- 队列名称
- 队列容量:表示给这个队列分配使用的资源占总资源的百分比
- 队列最大容量:表示这个队列可以使用的最大资源占总资源的百分比。如果没有设置最大容量限制,则当其它队列空闲时,该队列可能使用过多资源。
- 子队列
- 是否启用
父队列维护如下运行时信息:
- 已使用资源量:其子队列已使用资源量的总和
- 已使用容量:已使用资源量除以资源总量
子队列
子队列具有如下基本属性:
- 队列名称
- 队列容量
- 队列最大容量
- 是否启用
子队列维护如下运行时信息:
- 正在排队的任务
- 正在运行的任务
- 已使用资源量:正在运行的任务占用的资源量
- 已使用容量:已使用资源量除以资源总量
- 绝对容量:子队列的容量乘以父队列的容量
- 绝对最大容量:子队列的最大容量乘以父队列的最大容量
- 相对资源使用率:已使用资源量除以绝对容量
调度算法
- 计算每个子队列的相对资源使用率
- 将子队列按照相对资源使用率从小到大排序
- 遍历排序后的队列,从队列中选择一个任务调度
最后一步需要考虑如下条件:
- 跳过超过最大容量限制的队列
- 跳过没有等待任务的队列
- 跳过父队列超过最大容量限制的队列
- 剩余资源满足任务对资源的需求
资源总量估计
容量调度需要知道资源总量。以Chronos为例说明如何估计Chronos的资源总容量。Chronos是运行在Mesos上的调度框架,Chronos上报资源时,会列出本次上报中一些Mesos Slave机器的空闲资源量。
假如某次上报各个Mesos Slave机器的空闲资源量如下:
- slave-1: c11 cpus, m11 mem
- slave-2: c21 cpus, m21 mem
根据这次上报,估计当前资源总容量为 ( c11 + c21 ) cpus, ( m11 + m21 ) mem. 其中,slave-1、slave-2的容量分别如上。
假如接下来上报各个Mesos Slave机器的空闲资源量如下:
- slave-2: c22 cpus, m22 mem
- slave-3: c32 cpus, m32 mem
综合此次上报以及上次上报,估计当前资源总容量为 ( c11 + max(c21, c22) + c32) cpus, ( m11 + max(m21, m22) + m32 ) mem. 其中,各个slave的容量分别如下:
- slave-1: c11 cpus, m11 mem
- slave-2: max(c21, c22) cpus, max(m21, m22) mem
- slave-3: c32 cpus, m32 mem
如此继续......以上即为资源总容量估计算法。
实际情况下,有些slave会被下线、宕机等等,这些slave的容量应当从总容量中扣除。但是业务调度器无法得知slave是否下线或宕机,所以一个解决办法是对每个slave容量设置一个失效时间,各个slave容量的失效时间最好不一样。
这种资源总容量估计算法估计出来的结果可能不是准确的。可以有一个备用方案,就是直接在配置文件中或者数据库中配置资源的总容量。