调度框架学习笔记(2)—— 调度系统架构
Google
经历的三代资源调度器的架构,分别是中央式调度器架构(类似于 Hadoop JobTracker
,但是支持多种类型作业调度)、双层调度器架构(类似于 Apache Mesos 和 Hadoop YARN )和共享状态架构(就是Omega
)。
2.1 中央式调度(Monolithic schedulers)
在同一个代码模块中实现调度策略,单个实例,没有并行。Hadoop1,MapReduce 的宏调度架构如下图所示。
常见于HPC(high-performancecomputing)世界中。【不了解 HPC】
master进程
的 JobTracker
是一个所有 MapReduce
任务的中心调度器。每一个 node
上面都运行一个 TaskTracker
进程来管理各自 node
上的任务进入。TaskTracker
要和 master
上 JobTracker
通信并接受 JobTracker
的控制。
以 JobTracker
为例,资源的调度和作业的管理功能全部放到一个进程中完成。这种设计方式的缺点是扩展性差:
- 首先,集群规模受限
- 其次,新的调度策略难以融入现有代码中,比如之前仅支持
MapReduce
作业,现在要支持流式作业,而将流式作业的调度策略嵌入到中央式调度器中是一项很难的工作
Omega
论文中提到了一种对中央式调度器的优化方案:将每种调度策略放到单独一个路径(模块)中,不同的作业由不同的调度策略进行调度。这种方案在作业量和集群规模比较小时,能大大缩短作业相应时间,但由于所有调度策略仍在一个集中式的组件中,整个系统扩展性没有变得更好。
2.1.1 静态分区(Statically partitioned schedulers)
中央式调度是静态资源分区调度方式。也被成为云计算中的调度,资源集合的全面控制。部署在专门的,静态划分的集群的一个子集上。或者把集群划分为不同的部分,分别支持不同的行为。
静态资源划分
优点:简单,固定的硬件资源给固定的计算框架使用,各个框架各行其是,互不干扰。
缺点:整体资源利用率不高,经常出现集群计算系统资源不足。
2.1.2 动态资源管理抽象模型
资源管理与调度系统:YARN
、Mesos
、Corona
、Quincy
,从上述系统抽象两个模型:
动态资源管理与调度的概念模型
概念模型 三要素:
- 资源组织模型:组织起来方便分配,如
all resource->group->pool
三级队列,或平级多队列或单队列等 - 调度算法:熟知的
FIFO
、公平调度、能力调度、延迟调度等,说白了就是按照什么方式分配资源 - 任务组织:
job
分配,比如全局队列、机架队列、节点队列等。说白了就是如何把任务组织起来
动态资源管理通用架构
- 每台机器都有节点管理器,负责收集它所在机器的资源使用情况,分配的任务放到不同容器执行,彼此隔离开,避免
job
彼此干扰。在这里它要给 资源收集器 汇报任务,这个 收集器 在把相关的信息反应给 资源池,资源池列出目前可用的资源。 - 通用调度器构成:资源收集器、资源调度策略,资源池,工作队列。
- 调度策略:FIFO、公平调度、能力调度等,在这里系统应用者可根据具体情况设定符合业务状况的调度策略,当用户新提交作业时,其进入工作队列,等着分配使其可启动的资源。在这里系统应用者可根据具体情况设定符合业务状况的调度策略,当用户新提交作业时,其进入工作队列,等着分配使其可启动的资源。
动态与静态对比
- 动态会根据任务即时需要分配资源,不会出现资源闲置且不可用,也不会出现任务忙且不可得资源的尴尬局面。总之增加资源利用率,降低硬件成本。
- 增加数据共享能力,共用的资源存储一份就行啦,
- 说白了就是支持多类型计算框架和多版本计算框架,使用资源管理与调度平台可以实现两者平滑切换,给运营带来便利。
2.2 两层调度(Two-level scheduling)
为了解决中央式调度器的不足,双层调度器是一种很容易想到的解决之道(实际上是分而治之策略或者是策略下放机制)。双层调度器仍保留一个经简化的中央式调度器,但调度策略下放到各个应用程序调度器完成。这种调度器的典型代表是 Apache Mesos 和 Hadoop YARN。
各个框架调度器并不知道整个集群资源使用情况,只是被动的接收资源。Master
仅将可用的资源推送给各个框架,而框架自己选择使用还是拒绝这些资源。一旦框架(比如 JobTracker
)接收到新资源后,再进一步将资源分配给其内部的各个应用程序(各个 MapReduce
作业),进而实现双层调度。
两层调度器有两个缺点:
-
各个框架无法知道整个集群的实时资源使用情况;
很多框架不需要知道整个集群的实时资源使用情况就可以运行的很顺畅,但是对于其他一些应用,为之提供实时资源使用情况可以为之提供潜在的优化空间,比如,当集群非常繁忙时,一个服务失败了,是选择换一个节点重新运行它呢,还是继续在这个节点上运行?通常而言,换一个节点可能会更有利,但是,如果此时集群非常繁忙,所有节点只剩下小于
5GB
的内存,而这个服务需要10GB
内存,那么换一个节点可能意味着长时间等待资源释放,而这个等待时间是无法确定的。 -
采用悲观锁,并发粒度小。
在数据库领域,悲观锁与乐观锁争论一直不休,悲观锁通常采用锁机制控制并发,这会大大降低性能,而乐观锁则采用多版本并发控制(
MVCC
,Multi-Version Concurrency Control
),典型代表是MySQL innoDB
,这种机制通过多版本方式控制并发,可大大提升性能。例如,在Mesos
中,在任意一个时刻,Mesos
资源调度器只会将所有资源推送给任意一个框架,等到该框架返回资源使用情况后,才能够将资源推动给其他框架,因此,Mesos
资源调度器中实际上有一个全局锁,这大大限制了系统并发性。
备注:
悲观锁,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如
Java
里面的同步原语synchronized
关键字的实现也是悲观锁。乐观锁,顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于
write_condition
机制,其实都是提供的乐观锁。
2.2.1 YARN
背景
YARN
被称之为 Apache Hadoop Next Generation Compute Platform
,是 hadoop1
和 hadoop2
之间最大的区别:
设计思想
Hadoop2
(MRv2
)的基础思想就是把 JobTracker
的功能划分成两个独立的进程:全局的资源管理Resource Manager
和每个进程的监控和调度 Application Master
。 这个进程可以是 Map-Reduce
中一个任务或者是DAG
中一个任务。
Hadoop2
(MRv2
)的 API
是后向兼容的,支持 Map-Reduce
的任务只需要重新编译一下就可以运行在Hadoop2
(MRv2
)上。
架构说明
在 YARN
的设计中, 集群中可以有多个 Application Masters
,每一个 ApplicationMasters
可以有多个Containers
(例如,图中有两个 Application Masters
,红色和蓝色。红色的有三个 containers
,蓝色的有一个 container
)。关键的一点是 Application Masters
不是 Resource Manager
的部分,这就减轻了中心调度器的压力,并且,每一个 Application Masters
都可以动态的调整自己控制的 container
。
而 Resource Manager
是一个纯粹的调度器(不监控和追踪进程的执行状态,也不负责重启故障的进程),它唯一的目的就是在多个应用之间管理可用的资源(以 Containers
的粒度)。Resource Manager
是资源分配的终极权威。如果说 Resource Manager
是 master
,NodeManager
就是其 slave
。Resource Manager
并且支持调度策略的插件化:Capacity Scheduler
和 Fair Scheduler
就是这样的插件。
Application Master
负责任务提交,通过协商和谈判从 Resource Manager
那里以 Containers
的形式获得资源(负责谈判获得适合其应用需要的 Containers
)。然后就 track
进程的运行状态。Application Masters
是特定于具体的应用,可以根据不同的应用来编写不同的 Application Masters
。另外,Application Master
提供自动重启的服务。Application Master
可以理解为应用程序可以自己实现的接口库。
Application Masters
请求和管理 Containers
。Containers
指定了一个应用在某一台主机上可以使用多少资源(包括 memory
,CPU
等)。Application Master
一旦从 Resource Manager
那里获得资源,它就会联系 Node Manager
来启动某个特定的任务。例如如果使用 MapReduce
框架,这些任务可能就是 mapper
和 reducer
进程。不同的框架会有不同的进程。
Node Manager
是每一个机器上框架代理,负责该机上的 Containers
,并且监控可用的资源(CPU
,memory
,disk
,network
)。并且资源状态报告给 Resource Manager
。
小结
YARN
是一个两层调度,在 YARN
中,资源请求从 application masters
发出到一个中心的全局调度器上,中心调度器根据应用的需要在集群中的多个节点上分配资源。截止目前,YARN
只支持 CPU
和内存的调度。
2.2.2 Mesos
背景
大量分布式计算框架的出现:Hadoop
,Giraph
,MPI
,etc
。每一个计算框架管理自己的计算集群。这些应用框架往往把任务分割成很多小任务从而可以提高集群的利用率,让计算靠近数据。但是这些框架都是独立开发的,不可能在应用框架之间共享资源。形象的表示如下:
我们希望在同一个集群上可以运行多个应用框架。Mesos
是通过提供一个通用资源共享层,多个不同的应用框架可以运行在这个资源共享层之上。形象的表示如下:
但我们不希望使用简单的静态分区的方法(如下左图):
优势与挑战
Mesos
好处如下:
- 最大的好处就是提高集群的利用率。
- 可以很好的隔离产品环境和实验环境,可以同时并发运行多个框架。
- 可以在多个集群之间共享数据。
- 可以降低
Maintenance
成本。
Mesos
最大挑战是如何支持大量的应用框架。因为每一个框架都有不同的调度需求:编程模型,通信范型,任务依赖和数据放置。另外 Mesos
的调度系统需要能够扩展到数千个节点,运行数百万个任务。由于集群中的所有任务都依赖于 Mesos
,调度系统必须是容错和高可用的。
设计思想
Mesos
的设计决策:不采用中心化的、设计周全的(应用需求,可用资源,组织策略),适用于所有任务的全局调度策略;而采用委派调度任务给应用框架(把调度和执行的功能交给应用框架)。Mesos
声称:这样的设计策略可能不会达到全局最优的调度,但在实际运行中出奇的好,可以使得应用框架的近乎完美的达到目标。还声称其优点有两个:
- 应用框架的演化独立。
- 保持
mesos
的简洁。
架构说明
Mesos
的主要组件包括 master daemon
,slave daemons
和在 slaves
之上运行的 mesos applications
(也被称为 frameworks
)。Master
根据相应的策略(公平调度,优先级调度等)决定给每一个应用分配多少资源。模块化架构支持多种策略。
Resource offer
是资源的抽象表示,基于该资源,应用框架可以在集群中的某个 node
上实例化分配 offer
,并运行任务。每一个 Resource offer
就是一个分布在多个 node
上的空闲资源列表。Mesos
基于一定的算法策略(如公平调度)决定有多少资源可以分配给应用框架,而应用框架决定使用(接受)哪些资源,运行哪些任务。Mesos
上运行的应用框架由两部分组成:应用调度器和 slave
上运行的代理。
应用调度器向 Mesos
注册。Master
决定向注册的框架提供多少资源,应用调度器决定 master
分配的资源中哪些来使用。调度完成之后,应用调度器把接受的资源发送给 Mesos
,从而决定了使用哪些 slave
。然后应用框架中的任务可以在 slave
上运行。当任务很小并且是短期任务(每个任务都频繁的让渡自己握着的资源的时候),Mesos
工作的很好。
在 Mesos
中,一个中央资源分配器动态的划分集群,分配资源给不同的调度框架(scheduler frameworks
)。资源可以在不同的调度框架之间以 “offers”
的形式任意分配,offers
表示了当前可用的资源。资源分配器为了避免不同调度框架对同一资源冲突申请,只允许一次只能分配给一个调度框架。在调度决策的过程中,资源分配器实质上起到了锁的作用。因此 Mesos
中的并发调度是悲观策略的。
Master
使用 resource offer
机制在多个框架之间细粒度的共享资源。每一个 resource offer
空闲资源列表,分布在多个 slave
上。Master
决定给每一个应用框架提供多少资源,依据是公平方法或者优先级方法(调度策略,可插拔的方式)。
Reject
机制:驳回 mesos
提供的资源方案。为了保持接口的简单性,Mesos
不允许应用框架指定资源需求的限制信息,而是允许应用框架拒绝 mesos
提供的资源方案。应用框架如果遇到没有满足其需求的资源提供方案,则会拒绝等待。Mesos
声称拒绝机制可以支持任意复杂的资源限制,同时保持扩展性和简单。
Reject
机制带来的一个问题是在应用框架收到一个满足其需求的方案之前可能需要等待很长时间。由于不知道应用框架的需求,mesos
可能会把同一个资源方案发给多个应用框架。因此,引入 fliter
机制:Mesos
中的一个调度框架使用 filter
来描述它期望被服务的资源类型(允许应用框架设置一个 filter
表示该应用框架会永远的拒绝某类资源)。因此,它不需要访问整个集群,它只需要访问它被 offer
的节点即可。这种策略带来的缺点是不能支持基于整个集群状态的抢占和策略:一个调度框架不知道分配给其他调度框架的资源。Mesos
提供了一种资源储存的策略来支持 gang
调度。例如,应用框架可以指定一个其可以运行 node
白名单列表。这不是动态的集群分区吗?Mesos
进一步解释 filter
机制:filter
只是一个资源分配模型的性能优化方案,应用框架有哪些任务运行在哪些 node
上最终决定权。
gang 调度:
作业的所有任务要么一起调度,要么没有,调度器必须尝试重新调度整个作业。
任务调度过程
Slave 1
向master
汇报它有4个CPUs
和4 GB
的空闲内存,master
的allocation
模块会根据相应的分配策略通知framework 1
可以使用所有可用资源。master
把slave 1
上的可用资源发送给framework 1
(以resource offer
的方式)。framework
的调度器响应master
调度器,准备在slave
上运行两个任务,使用的资源分别是:第一个任务<2 CPUs, 1 GB RAM>
,第二个任务<1 CPUs, 2 GB RAM>
。- 最后,
master
把任务发送给slave
,然后把相应的资源分配给framework
的执行器。然后执行器启动两个任务 。由于slave1
上还有1 CPU
和1 GB
的内存没有分配,分配模块可以把资源分配给framework 2
。
另外 Mesos master
的 Allocation module
是 pluggable
。使用 ZooKeeper
来实现 mesos master
的Failover
。
2.3 状态共享调度(Shared-state scheduling)
每一个调度器都可以访问整个集群状态。当多个调度器同时更新集群状态时使用乐观锁并发控制。Shared-state
调度可以解决两层调度的两个问题:悲观并发控制所带来的并行限制和调度框架对整个集群资源的可见性。
乐观并发控制所带来的问题是当乐观假设不成立时,需要重新调度。
为了克服双层调度器的以上两个缺点(Omega paper
主要关注了这个问题),Google
开发了下一代资源管理系统 Omega
,Omega
是一种基于共享状态的调度器,该调度器将双层调度器中的集中式资源调度模块简化成了一些持久化的共享数据(状态)和针对这些数据的验证代码,而这里的 共享数据
实际上就是整个集群的实时资源使用信息。一旦引入共享数据后,共享数据的并发访问方式就成为该系统设计的核心,而 Omega
则采用了传统数据库中基于多版本的并发访问控制方式(也称为 乐观锁
,MVCC
,Multi-Version ConcurrencyControl
),这大大提升了 Omega
的并发性。在 Omega
中没有中心的资源分配器,调度器自己作出资源分配的决策。
2.3.1 Omega
背景
中央式调度的缺点是难以增加调度策略和专门的实现,并且不能随着集群的扩展而扩展。两层调度确实可以提供灵活性和并行性,但是在实践中他们的资源可见性却是保守的,难以适应一些挑剔型的任务和一些需要访问整个集群资源的任务。为了克服双层调度器的缺点,Google
开发了下一代资源管理系统 Omega
设计思想
由于 Omega
不再有集中式的调度模块,因此,不能像 Mesos
或者 YARN
那样,在一个统一模块中完成以下功能:对整个集群中的所有资源分组,限制每类应用程序的资源使用量,限制每个用户的资源使用量等,这些全部由各个应用程序调度器自我管理和控制。
根据论文所述,Omega
只是将优先级这一限制放到了共享数据的验证代码中,即当同时由多个应用程序申请同一份资源时,优先级最高的那个应用程序将获得该资源,其他资源限制全部下放到各个子调度器。
引入多版本并发控制后,限制该机制性能的一个因素是资源访问冲突的次数,冲突次数越多,系统性能下降的越快,而 google
通过实际负载测试证明,这种方式的冲突次数是完全可以接受的。
Omega
论文中谈到,Omega
是从Omega
的调度器架构,我们可推测它的整体架构类似于Mesos
,这样,如果你了解Mesos
,那么可知道,我们可以通过仅修改Mesos
的Master
将之改造成一个Omega
。
架构说明
Omega
中没有中心的资源分配器,所有的资源分配决策都是由应用的调度器自己完成的。Omega
维护了一个称为 cell state
的资源分配状态信息主拷贝。每一个应用的调度器都维护了一个本地私有的,频繁更新的 cell state
拷贝,用来做调度决策。调度器可以看到全局的所有资源,并根据权限和优先级来自以为是的要求需要的资源。当调度器决定资源方案时,以原子的方式更新共享的 cell state
:大多数时候这样 commit
将会成功(这就是乐观方法)。当冲突发生时,调度决策将会以事务的方式失败。无论调度成功还是失败,调度器都会重新同步本地的 cell state
和共享的 cell state
。然后,如果需要,重启调度过程。
Omega
的调度器完全是并行的,不需要等待其他调度器。为了避免冲突造成的饥饿,omega
调度器使用增量调度 —— 除了冲突的资源,其他都接受
,这样可以避免资源囤积。如果使用 all or nothing
的策略可以使用 gang
调度。gang
调度要等待所有资源就绪,才 commit
整个任务,就造成了资源囤积。
每一个应用调度器都可以实现自己的调度策略。但是它们必须就资源分配和任务的优先级达成一致。两层调度中中心资源管理器可以轻松实现这一点。公平性不是一个关键需求,只是满足自己的业务需求。因此,需要限制每一个应用调度器资源上限和任务提交上限。
2.4 小结
集群调度的主要目标是提高集群的利用率和使用效率。
- Monolithic schedulers
为所有任务都使用一个中心调度算法。其缺点是不易增加新的调度策略,也不能随着集群的扩展而扩展。
- Two-level schedulers
使用一个动态资源管理器提供计算资源或者存储资源给到多个并行的调度框架。每一个调度框架所拥有的都是一个整个资源的一个子集。为什么是一个动态资源管理器呢?是相对于静态的集群分区来说的。我们可以静态的把集群分为几个区,分别服务于不同的应用。上面的动态资源管理器完成工作就是把静态的分区工作动态化。由于两层调度无法处理难以调度的挑剔任务,且不能根据整个集群的状态做出决策,google
引入下面的调度架构。
- Shared-state schedulers
使用无锁的乐观并发控制算法。那么对比起来,Two-level schedulers
本质上是悲观调度算法。Omega
,google
的下一代调度系统中使用了该架构。
在 omega
看来,mesos
的 offer
的机制本质上是一个动态的过滤机制,这样 mesos master
向应用框架提供的只是一个资源池的子集。当然可以把这个子集扩大为一个全集,也是 share state
的,但其接口依然是悲观策略的。
附录:参考资料
YARN中FIFO、Capacity以及Fari调度器的详细介绍