集群调度框架的架构演进

2023-10-01 17:45
本博客是大规模集群任务调度系列文章的第一篇。资源调度在亚马逊、谷歌、Facebook、微软或雅虎已经得到很好的实现,其他地方的需求也在增长。 本博客是有关大规模集群任务调度的系列文章中的第一篇。资源调度在亚马逊、谷歌、Facebook、微软或雅虎已经得到很好的实现,其他地方的需求也在增长。调度是一个非常重要的话题,因为它直接关系到运行集群的投资:一个糟糕的调度器会导致利用率低下,并且浪费昂贵的硬件资源投资。它本身无法实现高利用率,因为必须仔细配置和正确调度与资源利用率冲突的负载。 架构演变 本博客讨论了近年来调度架构的演变及其原因。图 1 演示了不同的方法:灰色方块对应于设备,不同颜色的圆圈对应于任务,带有“S”的方块代表调度程序。箭头代表调度器的调度决策;三种颜色代表不同的工作负载(例如网站服务、批量分析和机器学习) 图1:不同的调度架构,灰框代表集群设备,圆圈代表任务,Si代表调度器。 (a) 单调度器 (b) 两级调度 (c) 共享状态调度 (d) 分布式调度 (e) 混合调度 很多集群架构,比如大量的高性能计算(HPC),都使用Borg调度器,它与Hadoop调度器和Kubernetes调度器完全不同,是一个单体调度器。 整体调度 单个调度进程运行在一台物理机上(如Hadoop V1中的JobTacker、Kubernetes中的kube-scheduler),并将任务分配给集群中的其他物理机。所有负载都服从调度程序,并且所有任务都通过此调度逻辑运行(参见图 1a)。这种架构具有最简单的格式并且是独一无二的。在此基础上,开发了很多负载调度器,例如Paragon和Quasar调度器,它们利用机器学习的方法来避免不同负载之间的资源竞争。 如今的集群正在运行不同类型的应用程序(对应于早期的MapReduce作业场景)。然而,由于以下几个原因,使用单个调度程序来处理如此复杂和异构的工作负载可能会很棘手: 调度程序必须以不同于批处理分析作业的方式处理长时间运行的服务作业,这是一个合理的要求。 由于不同的应用有不同的需求,所以在调度器中增加了更多的功能,并增加了业务逻辑和部署方式。调度程序处理任务的顺序成为一个问题:如果调度程序设计不仔细,排队效应(例如队头阻塞)和回滚可能会成为问题。 总而言之,这对工程师来说听起来像是一场噩梦,调度程序维护人员面临的无穷无尽的功能请求也证实了这一点。 二次调度 二次调度通过将资源调度和任务调度分离来解决这个问题,使得任务调度逻辑不仅可以根据不同的应用需求进行定制,而且还保留了集群之间共享资源的可能性。虽然侧重点不同,但Mesos和YARN集群管理都采用这种方式:在Mesos中,资源是主动提供(offer)给应用层调度器的,而YARN则允许应用层调度器请求资源(并随后接受所请求的资源) 。分配资源)。图 1b 显示了这个概念。作业负责调度(S0-S2)并与资源管理器交互。资源管理器为每个作业分配动态资源。该解决方案使客户能够灵活安排工作策略。 但通过两级调度解决问题也存在问题:应用层调度隐藏了资源的全局调度,即无法再看到全局可选的资源配置。相反,你只能看到资源管理器主动向应用程序提供(offer,对应Mesos)或请求/分配(对应YARN)的资源。这带来了一些问题: 可重入优先级(即高优先级将淘汰低优先级任务)变得非常难以实现。在offer-based模式下,运行任务占用的资源对于上层调度器是不可见的;在基于请求的模式下,底层资源管理器必须了解重入策略(与应用程序相关)。 调度器无法干预正在运行的服务,这可能会降低资源使用效率(例如“饥饿的邻居”占用IO带宽),因为调度器看不到它们。 应用相关的调度器更关注底层资源使用的不同情况,但它们选择资源的唯一方式是资源管理器提供的Offer/request接口。这个界面很容易变得非常复杂。 共享状态架构 共享状态架构通过采用半分布式模型解决了这个问题。在此架构中,集群状态的多个副本由应用程序级调度程序独立更新,如图 1C 所示。一旦本地有更新,调度程序就会发出并发事务来更新所有共享集群状态。有时事务更新可能会失败,因为另一个调度程序发出了冲突的事务。 共享状态架构最重要的例子是谷歌的Omega系统,以及微软的Apollo和Hashicorp的Nomad容器调度程序。在这些例子中,共享集群状态架构是通过一个模块来实现的,即Omega中的“cell state”、Apollo中的“resource Monitor”和Nomad中的“planqueue”。 Apollo与其他两者的不同之处在于共享状态是只读的,调度事务直接提交到集群设备;设备本身将检查冲突并决定接受或拒绝更新,即使共享状态暂时不可用,Apollo 也可用。继续执行。 从逻辑上讲,共享状态设计并不一定需要将完整状态分布在其他地方。这样(有点像 Apollo)每个物理设备都会维护自己的状态并将更新发送到其他感兴趣的代理,例如调度程序、设备健康监控和资源监控系统。每个物理设备的本地状态成为全局共享状态的“碎片”。 然而,共享状态架构也有一些缺点。它们必须在稳定(过时)的信息上运行(与集中式调度程序不同),并且可能会在高争用情况下导致调度程序性能下降(尽管对于其他体系结构也是如此)。这种可能性)。 全分布式架构 看起来这种架构更加分散:调度器之间没有协调,使用许多独立的调度器来响应不同的负载,如图1d所示。每个调度程序都根据自己的本地(部分或经常陈旧)集群状态信息进行操作。通常,作业可以提交给任何调度器,调度器可以将作业发布到任何集群节点来执行。与两级调度器不同的是,每个调度器没有负责的分区。全局调度和资源划分具有统计显着性和随机分布。这有点像共享状态架构,但没有中央控制。 虽然去中心化的底层概念(去中心化随机选择)在1996年就已经出现,但现代意义上的分布式调度应该是从Sparrow论文开始的。当时有一个讨论:细粒度的任务很多。优点,Sparrow论文的关键假设是集群上的任务周期可以变得很短;接下来,作者假设大量的任务意味着调度器必须支持非常高的决策吞吐量,而单个调度器无法支持如此大量的决策。 (假设每秒有数百万个任务),Sparrow 将此负载分散到许多调度程序中。 这个实现很重要:去中心化理论上意味着更多的仲裁,但这非常适合某些类型的工作负载,我们将在后面的系列中讨论。现在,有足够的理由证明,由于分布式调度是不协调的,因此它比复杂的单片调度、两级调度或分布式状态时间调度更适合简单的逻辑。例如: 分布式调度基于简单的“时隙”概念,将每个设备划分为n个标准时隙,同时运行n个并发任务,尽管这种简化忽略了任务资源需求不同的事实。 在任务端(worker端),使用遵守简单服务规则的队列方法(例如Sparrow中的FIFO)。这样,调度器的灵活性受到限制,调度器只需要决定在哪个设备上将任务入队即可。 由于没有中央控制,分布式调度器在设置全局变量时存在一定的困难(例如,公平策略或严格的优先级优先级等)。 由于分布式调度旨在基于最少的知识做出快速决策,因此无法支持或承担复杂的应用相关调度策略,因此避免任务之间的干扰对于完全分布式调度来说是困难的。 混合架构 混合架构是最近(起源于学术界)为了解决全分布式架构的缺点而提出的解决方案。它结合了单一或共享状态设计。这种方法如Tarcil、Mercury、Hawk等,一般有两种调度路径:一种是针对部分负载(例如短期任务或低优先级批量负载)设计的分布式路径,另一种是集中式路径处理剩余负载的调度。降低负载,如图 1e 所示。对于所描述的工作负载,在混合架构中生效的调度程序是独特的。事实上,据我所知,目前还没有真正的混合架构部署在生产系统中。 实际意义 除了许多关于不同调度架构的相对价值的研究论文外,讨论并不局限于学术界。从行业角度对Borg、Mesos和Omega论文进行深入讨论,请参见Andrew Wang的专业博客。然而,上面讨论的许多系统已经部署在大型企业生产系统中(例如,微软的 Apollo、谷歌的 Borg、苹果的 Mesos),而这些系统反过来又启发了其他可用的开源项目。 如今,许多集群系统都运行容器化的工作负载,因此出现了一系列面向容器的“Orchestration Framworks”。它们类似于谷歌和其他所谓的“集群管理系统”。然而,对这些调度框架和设计原则的讨论却很少,更多地关注面向用户的调度 API(例如,Armand Grillet 的这篇报告比较了 Docker Swarm、Mesos/Marathon 和 Kubernetes 的默认调度器)。然而,许多客户既不了解不同调度架构之间的差异,也不知道哪一种更适合他们的应用。 图2展示了开源编排框架的部分架构以及调度器支持的功能。图表底部还包括谷歌和微软的闭源系统进行比较。资源粒度一栏显示调度器是将任务分配到固定大小的时隙还是根据多维度需求(如CPU、内存、磁盘IO、网络带宽等)分配资源。 图2:常用开源编排框架的分类和功能比较,以及与闭源系统的比较。 确定适当的调度架构的一个主要因素是您的集群是否运行异构(或混合)工作负载。例如,前端服务(例如负载均衡Web服务和memcached)和批量数据分析(例如MapReduce或Spark)混合在一起。这种组合对于提高系统利用率是有意义的,但不同的应用需要不同的调度方法。在混合环境中,单片调度很可能导致任务分配不理想,因为单片调度逻辑无法根据应用需求进行多样化,此时辅助或共享状态调度可能更合适。 许多为用户服务负载运行的资源通常是为了满足容器的峰值需求而设计的,但实际上资源是过度分配的。在这种情况下,有机会减少对低优先级工作负载的资源过度分配是高效集​​群的关键。虽然 Kubernetes 已经有比较成熟的解决方案,但 Mesos 是目前唯一支持这种超分配策略的开源系统。这个功能未来应该还有更大的改进空间,因为根据Google borg集群来看,很多集群的利用率还不到60-70%。在后续博客中,我们将讨论资源估算、过度分配和有效设备利用等方面。 ***,特殊分析和 OLAP 应用程序(例如 Dremel 或 SparkSQL)非常适合完全分布式调度。然而,完全分布式调度(例如Sparrow)具有相对严格的内置功能集,因此当负载均匀(即所有任务同时运行)且设置时间(set-up times)较短时(即任务调度时间较长)时间运行,例如运行在YARN上的MapReduce应用任务,在任务吞吐量(churn)较高时非常适合(即很多调度决策必须在很长时间内做出)短时间)。我们将在下一篇博客中详细讨论这些条件,以及为什么完全分布式调度(以及混合架构中的分布式模块)仅对这种应用场景有效。 现在,我们可以证明分布式调度比其他调度架构更简单,并且不支持其他资源维度、过度分配或重新调度。 总而言之,图 2 中的表格表明,相对于更先进但闭源的系统,开源框架仍然有很大的改进空间。可以从以下几个方面采取行动:缺少功能、使用不良、任务性能不可预测、邻居噪音降低效率以及微调调度程序以支持某些客户的特殊需求。 然而,也有很多好消息:尽管如今许多集群仍然使用整体调度,但许多集群已经开始迁移到更灵活的架构。如今 Kubernetes 已经可以支持可插拔的调度器(kube-scheduler pod 可以被其他 API 兼容的调度 pod 替代),并且从 1.2 版本开始,更多的调度器将支持“扩展器”以提供定制策略。据我了解,Docker Swarm 未来还将支持可插拔的调度程序。 下一步 下一篇博客将讨论全分布式架构是否是可扩展集群调度的关键技术创新(反对者说:没有必要)。然后,我们将讨论资源适配策略(以提高利用率),最后讨论我们的Firmament调度平台如何结合和共享状态架构和单片调度质量,以及全分布式调度器性能问题。