随着分布式计算集群规模的不断扩张,任务调度系统的稳定性成为了整个集群稳定的关键因素。随着容器技术的快速兴起,基于容器的计算平台被大量应用,任务调度的规模及频率快速上升,这对任务调度系统提出了更为严苛的挑战。常见的调度系统往往兼顾了准确度却牺牲了性能,容器调度的复杂性使得在准确和效率之间找到平衡点很难,尤其是在交互式调度的场景下,可取的解决方案更是捉襟见肘。本篇文章就以此为背景,介绍大规模调度场景下分布式任务调度的难点、解决策略及现有的一些方案。
\\计算任务通常分为两个大的类别,即以守护进程形式运行的长任务和以批处理形式运行的短任务,前者资源的使用率变化幅度小,而后者资源使用率变化大。本篇我们讨论的主要是针对计算密集型的场景,即以批处理形式运行的数据处理任务。
\\调度系统的核心目标: 快速准确地为任务匹配合适的计算资源。但快速(Low Placement latency)和准确(High Quality Placement
\)这两个目标会产生矛盾,即必须在二者间权衡。尤其是在交互式调度场景下,只追求准确度而忽视效率会使得调度失去意义。
调度系统的本质是为计算任务匹配合适的资源,使其能够稳定高效地运行,而影响应用运行的因素非常多,比如CPU、内存、网络、端口等等一系列因素都会影响应用运行的表现。于此同时,整个计算集群的资源使用情况是动态变化的,大量的应用被创建、销毁和迁移,调度决策的过程如果不够快,那么实际运行时面对的资源情况可能与决策时千差万别。
\\ \\计算任务的调度不仅仅要考虑资源本身的状况,而且还要结合任务本身的优先级来考虑抢占式调度的情景。比如系统出现异常,临时增加诊断性任务,就必须以高于其他任务的优先级来运行。有资源抢占就涉及到任务驱逐、重调度等情况,这里面会涉及驱逐相关的算法策略、调度流程的复用等。
\\从目前常用的任务调度框架来看,Apache Mesos是面向数据中心资源管理的典型代表。Hadoop YARN调度器是面向计算任务的,与Mesos相似之处在于也是两层调度机制,不同在于考虑了资源抢占机制,能够处理任务的优先级对调度结果的影响。这些年发展迅速的Kubernetes也包含调度器,其本质上还是面向应用运行层面,默认调度器的策略比较丰富,扩展性也比较好,但在面对大规模的调度时,吞吐性能表现不是那么完美。
\\Apache Mesos将CPU、内存、存储和其他计算资源从机器(物理或虚拟)中抽象出来,使容错和弹性分布式系统能够轻松构建并有效运行。Mesos采用的Resource Offer
使得任务框架能够自行决定是否接受Mesos Master的Offer
,在实际使用过程中,资源框架中的调度机制需要用户自行实现,还是略微有些工作量的。
上述调度过程分为四个步骤:
\\这里着重需要关注两个问题:
\\对于第一个问题,Mesos提供了\"reject offer\"的机制,允许Framework暂时拒绝不满足其资源需求的Resource Offer,具体拒绝的策略由各个框架自行决定。
\\因此在Master与Framework通信的问题上,Mesos提供了\"filter\"和\"rescinds\"机制,前者可以只接收剩余资源量大于L的Resource Offer,从而屏蔽掉部分流量,后者则允许某个Framework在一定的时间内没有为分配的资源返回对应的任务,则Mesos会回收其资源量,并将这些资源分配给其他framework。
\\Mesos调度采用Dominant Resource Fairness(DRF)策略,是一种支持多资源的max-min fair资源分配机制。在调度时先找出用户(这里指具体的Framework)的支配性资源,DRF将均衡所有用户的支配性资源,确保每个用户获取了最大比例的支配性资源。
\\Hadoop 的YARN模块也具有为计算任务分配资源的能力,相应地也具有调度能力。YARN将应用的资源请求抽象为一个容器(Container),具体的Application Master会接收容器,然后在容器中启动这个任务。对于Hadoop来说,任务注意分为两类: Map 和 Reduce。
\\注意: 这里的容器不是云计算中所说的具体容器技术,而是Hadoop YARN中一个抽象的资源的集合的概念。
\\ \\在MapReduce应用程序中,有多个map任务,每个任务都在集群中某个工作主机上的容器中运行。同样,还有多个reduce任务,每个还在运行主机上的容器中运行。
\\同时在YARN端,ResourceManager,NodeManager和ApplicationMaster一起工作以管理集群的资源,并确保任务以及相应的应用程序完整地完成。
\\ \\调度本身并不是一个新的概念,个人计算机可以有多个CPU核,每个核运行一个进程,但同时运行多达几百个进程。调度程序是操作系统的一部分,它将进程分配给CPU内核以在短时间内运行。
\\对于大规模的计算集群也一样,应用程序由群集上的多个任务(通常在不同的主机上)组成。集群调度程序基本上必须解决:
\\YARN使用队列(Queue)在多个租户之间共享资源。当应用程序提交给YARN时,调度程序会将它们分配给队列。根队列是所有队列的父级。所有其他队列都是根队列或另一个队列(也称为分层队列)的子节点。队列通常与用户,部门或优先级相对应。Application Master跟踪每个任务的资源需求并协调容器请求。这种方式允许更好的扩展,因为RM/调度程序不需要跟踪在集群上运行的所有容器。
\\在YARN支持的调度程序中,公平调度(Fair Scheduler)是一个受欢迎的方式。在最简单的形式中,它在集群上运行的所有作业中公平地共享资源。
\\ \\在实际的复杂调度场景中,还会有水平队列的形式出现,其实是上述队列的一种嵌套形式,即在队列的中还包含子队列,不同的子队列在父队列的权重的基础上有各自的权重设置,这里不再赘述。
\\Firmament 通过对调度算法的优化使得大规模计算集群的任务调度可以很好地在性能和准确之间找到平衡。Firmament 的设计出发点主要有如下两个:
\\既要保证单个决策的准确性,又要保证调度策略的灵活性,这对于调度程序的性能提出了很高的要求,而Firmament的基于流图的决策模式能够有效解决这个问题。这个调度程序的核心来自于Google的开发者,开发语言为C++,目前作为一个开源项目,大家都可以共享自己的代码。Firmament主要有以下三个特性:
\\Firmament既可以独立工作,也可以在集群管理器中工作,如Kubernetes这样的容器管理集群,开源项目Poseidon正是出于这样的目的,将Firmament引入容器管理集群。
\\ \\目前Firmament通过与容器管理集群结合,大幅度提升容器应用的调度管理能力,解决像Kubernetes原生调度器在10K节点时,性能急剧下降且对批处理作业任务支持不够完善的情况。Kubernetes原生调度器采用的基于队列的模型,需要依赖于队列的性能,而在任务调度失败后,又再次回到队列等待继续调度,在对于计算密集型的任务调度时,对优先级、资源状态的共享支持都有待提升。
\\Firmament 采用的流图的机制,综合考虑了很多种影响调度结果的因素,比如Rack,AZ,Region等,甚至包括SSD硬件属性,综合这些因素考虑最小的成本,最大的流量来决定最终的调度结果。
\\Posedion 会充当 Firmament 与 Kubernetes 之间的桥梁,即通过Kubernetes API 获取对象的事件(Event),然后结合资源使用状况,调用 Firmament 获取调度的结果,最终完成 Pod 调度的过程。
\\ \\Firmament 可以理解为独立的核心算法模块,功能与原来的默认scheduler 相同,都是根据目前资源情况,给出最佳的调度结果。Firmament 本身由一系列的复杂算法组成,但对于 Posedion 来说,这些细节可以不必关系,只需要了解输入和输出的标准数据结构就好。
\\在具体使用的时候,Firmament、Poseidon 及 heapster 等模块都是以 Deployment 形式部署在 Kubernetes 集群中的。在部署 Pod 的时候,我们知道 Kubernetes 支持多调度器机制,可以在 Pod 的定义中指定使用哪个调度器,具体示例如下:
\\\apiVersion: batch/v1\kind: Job\metadata:\ name: cpuspin5\spec:\ completions: 1\ parallelism: 1\ template:\ metadata:\ name: cpuspin5\ labels:\ schedulerName: poseidon\ spec:\ schedulerName: poseidon\ containers:\ - name: cpuspin\ image: firmament/libhdfs3\ resources:\ requests:\ memory: \"10Mi\"\ cpu: \"10m\"\ limits:\ memory: \"12Mi\"\ cpu: \"20m\"\ command: [\"/bin/sh\