谷歌超大规模集群管理系统Borg详细解读

注:转载来自于该篇博客:谷歌超大规模集群管理系统Borg详细解读

文本根据谷歌15年发表的论文整理得到,论文连接:http://research.google.com/pubs/pub43438.html

  • Borg是谷歌内部的大规模集群管理系统,在谷歌内部经历数十年的打磨,应该是与谷歌三驾马车(MR,GFS,BigTable)的同时代产物,直到今天才发表论文公布其中的细节。
  • 谷歌其实很早就宣称可以在同一个集群中同时调度在线任务和离线任务,足可见谷歌在科技方面要领先行业一大步。
  • Kubernetes被任务时Borg的开源版本(研发团队有重合、功能简化聚焦、架构类似),很多工程师现在投身K8S的系统研究,推进K8S的发展。就在2019年5月4日,Twitter宣布弃用使用了长达数十年的Mesos系统,全面拥抱K8S,足可见K8S在目前云计算生态和环境下的重要地位。越来越多的服务以docker的方式进行运行通过K8S进行编排。

1. Abstract&Introduction

Borg是一个集群管理系统,上面运行着十万级的任务,数千个不同的应用,管理着数万台机器。其通过权限管理、资源共享、性能隔离等来达到高资源利用率。它能够支持高可用应用,并通过调度策略减少出现故障的概率,提供了任务描述语言、实时任务监控、分析工具等。

Borg主要有三大优势:

  • 隐藏了资源管理和错误处理的细节,让用户关注应用的开发;
  • 具备高可用性和高可靠性,并支持应用的高可靠和高可用;
  • 能够在数万个节点上高效运行任务;
  • 解读:易用性、可用性、可靠性、可扩展性、高利用率是所有云平台都需要解决的问题,Borg在这几个方面做得更好。

2. 用户视角

Borg的用户主要是谷歌的开发人员和管理人员,主要运行谷歌内部的应用和服务。用户以job的方式提交作业,一个job可以包含一个或多个task。每个作业运行在同一个cell中,cell是由一组机器构成的一个处理单元。

2.1 工作负载

  • borg中运行的异构工作负载主要分为两类:长服务类型和批处理任务。长服务几乎不会停止,处理短期且延迟敏感的请求,例如gmail、web search、Google docs、bigtable等。批处理任务运行时间在几秒到几天不等,对短期的性能波动不敏感。这些工作负载混合部署在集群中。
  • 很多应用框架已经运行在Borg上多年,包括内部的MapReduce、FlumeJava、Millwhell、Pregel。Google的分布式存储系统也都运行在Borg之上,包括GFS、BigTable、Megastore等。
  • 论文将高优先级的作业称为“生产型作业”(prod),将其他作业称为“非生产型作业”(non-prod)。大部分长时间运行的服务都是生产型作业,生产型作业被分配了大约70%的CPU资源并占用了大约60%的利用率;同时被分配了大约55%的内存资源并占用了大约85%的内存利用率。
  • 个人解读:谷歌通过prod和non-prod来对在线任务和离线任务进行区分,以达到混合调度的目标。

2.2 集群和单元

在谷歌集群中,通常包含一个大规模的cell和若干个用于测试或者特殊用途的小cell。cell的规模大约是10K个机器,其中的机器在资源大小、处理器型号、性能、容量等方面异构。

  • 个人解读:cell是集群和机器的中间层,这种方式在实践中被广泛使用,通常一个集群中都会预留一部分资源作为测试集群。

2.3 job&task

一个Borg作业Job具有name、owner、task数量等属性。Job具有约束(constraint)来强制其任务运行在具有特定属性的机器上,例如处理器架构、OS版本、IP地址等。约束可以分为硬约束和软约束,前者是需求,必须满足;后者是偏好,尽量满足。每个任务,可以映射为一个container中的一组Linux程序。Borg绝大部分工作负载不运行在虚拟机中,这主要是由于虚拟机性能开销较大,此外一些硬件不支持虚拟化。

一个任务也具有属性,例如资源需求和task index。Borg程序采用静态链接库,以减少对运行环境的依赖,软件包中主要包括二进制程序和数据文件。 用户向Borg发送RPC用以操作作业。用户可以提交作业、查询作业,也可以改变作业、任务属性。作业Job和任务task的状态图如下图所示。用户可以触发submit、kill和update事务。

任务Task在被抢占之前,会收到通知,二者都是通过Unix信号,抢占通过SIGKILL信号,程序直接退出;通知通过SIGTERM信号,可以被处理。这样做主要是为了让程序关闭前清理环境、保存状态、完成当前请求等。

  • 解读:硬约束主要是为了解决资源异构问题,软约束则主要是为了解决类似于数据本地行的问题;目前将任务运行在容器中是趋势,因为虚拟机的开销太大,因为docker既可以支持镜像又能够简化部署 ,所以非常风靡;以事件的方式进行任务状态管理是常用的做法,YARN中就有详细的状态机机制;对于抢占任务,云平台一般都会提供优雅的退出方式,一般情况都先通知应用由其自己决定如何处理,这样做主要是让程序在抢占前清理环境、保存状态等。

2.4 资源分配集合

一个Borg alloc是一台machine上预留资源的集合,可以用于一个或多个任务运行。Alloc被用于为未来的task预留资源,用来在停止和再次启动任务之间保持资源,或者将不同作业Job的任务task聚合在一起运行(例如一个web服务器实例和一个相关的logsaver)。
一个Alloc集合类似一个job:它是在多个机器上预留资源的一组alloc。一旦一个alloc集合被创建,一个或者多个作业Job就可以被提交在其中运行。简而言之,一个task与一个alloc对应,一个job与一个alloc集合对应。

  • 解读:alloc是资源单元,但其与YARN中的container不同,因为其中可以运行多个task,更像K8S中的pod(包含多个container)。

2.5 优先级、配额和准入控制

每个作业都有一个优先级,具体形式是一个小的正整数。Borg定义非重叠优先级,包括:monitoring, production, batch, and best effort (also known as testing or free),生产型作业(prod job)包含前两种优先级。
为了避免“抢占洪流”,Borg不允许同类型作业之间的抢占,只允许生产型作业抢占非生产型作业。
资源配额Quota是一组资源量化表达式(CPU、RAM、Disk等),它与一个优先级和一个时间段对应。如果Quota不足,作业提交会被拒绝。

  • 解读:在borg中如果配额不足会拒绝任务提交,而通常的调度系统如果资源不足会让任务排队,等到资源充足时系统再进行调度执行。这种差别可能是系统的用户群不同所导致的,borg的用户是高级工程师,而通常的调度系统使用者一般对分布式不太熟悉,所以需要将提交、调度和运行的细节封装起来。

2.6 名字服务和监控

Borg创建了一个稳定的Borg名字服务(BNS),用于为每个任务命名。任务名字包括cell name、job name和task number这个三元组。Borg将任务的主机名和端口号写入Chubby中的一致性、高可用文件。Borg也会将作业规模和任务健康信息写入Chubby,负载均衡器LoadBalancer据此来进行路由。
每个任务会构建HTTP server来发布其健康信息和性能指标,Borg根据URL来监控任务,并重启异常任务。

  • 解读:borg中不仅运行离线任务还运行在线任务,这就会导致集群中的服务相互之间可能会存在调度关系,这时候就需要名字服务。当客户端或者其他系统需要访问服务时,首先访问BNS根据任务名查找具体的访问方式进行访问;监控室每个调度系统都需要有的组件,对于一个数据节点,其需要定时的汇报自己所在节点的状态信息包括资源使用信息、将康状态信息和任务执行的汇总信息等,一般是设定好固定的时间间隔进行主动心跳上传。

3.borg架构

borg的中心控制模块叫BorgMaster,每个节点模块叫Borglet,所有组件均使用C++编写。(基础平台使用C++书写属于惯例,能够从底层提高性能,而Java语言则比较重,不过使用Hadoop生态的还需要使用Java语言)
在这里插入图片描述

3.1 borgmaster

borgmaster包含两个进程:主进程:负责处理客户端RPC调用,管理系统中所有对象的状态机,与borglet进行通信。以及一个分离的调度器。

borgmaster在逻辑上是一个进程,但是有5个副本,每个副本都维护cell状态的一份内存副本,cell状态同时在高可用、分布式、基于Paxos的存储系统中做本地磁盘持久化存储。一个单一的被选举的master既是Paxos leader,也是状态管理者。当cell启动或者被选举master挂掉时,系统会选举Borgmaster,选举机制按照Paxos算法流程进行。选举master的过程通常会消耗10秒,在一些规模较大的cell会消耗1分钟左右。

borgmaster的状态会定时设置checkpoint,具体形式就是在Paxos store中存储周期性的镜像snapshot和增量更改日志。其作用可以存储borgmaster的状态,修复问题,离线模拟等。

此外还提供了一个模拟器Fauxmaster,其是borgmaster的一个复制品,可以用来修复bug,模拟执行。

  • 解读:master是一个调度系统的核心,为了保证可用性通常的做法就是进行副本维护,当一个master出现故障的时候可以由新的master来替代。

3.2 scheduling

当job被提交时,borgmaster会将其记录到paxos store中,并将task增加到等待队列中,调度器通过一步的方式扫描队列将机器分配给任务,调度过程主要包括可行性检查和打分。

可行性检查主要用于解决硬约束,找到一组满足硬约束的机器;打分则是满足软约束,在可行的机器中根据用户偏好为机器打分,用户偏好主要是系统内置的标准,如:被抢占的task的优先级和数量最小化,具有任务软件包的机器、分散任务到不同的失败域中,将高优先级和低优先级的task分配到同一个node上,使得高优先的任务可以抢占低优先级的任务等。borgmaster一开始使用E-PVN进行打分,其会将任务分散到不同的机器上,称为worst fit;而best fit会尽量使用紧凑的机器以减少资源碎片。考虑到系统中不同类型任务单额资源需求,采用混合的模型进行打分。

如果对于一个新task没有充足的资源,那么系统会杀死低优先级的任务,杀死的低优先级的任务会被放置在等待队列中。任务启动延迟平均在25秒,有80%的时间花费在包安装上。因此在调度的时候尽量把任务调度在含有包的机器上,同时因为包是不变得,所以可以共享和缓存,同时还提供了一些并行分发包的协议。

3.3 borglet

borglet是运行在每台机器上的代理,负责启动和停止task,重启任务,管理本地资源,向master和监控模块汇报自己的状态。master会周期性的向borglet拉取其当前的状态,这样做能够控制通信速度避免“恢复风暴”。

为了性能扩展,master副本会运行一个无状态的link shard去处理与部分borglet的通信,但重新选举时会重新分区,master副本会聚合和压缩信息,向被选举的master报告状态机的不同部分,减少更新负担。如果borglet多轮都没有进行响应会被标记为down。运行在上面的任务会被重新调度至其他机器,如果通信恢复,master会通知borglet杀死已经重调度的任务保证一致性。

  • 解读:borglet的设计与其他调度系统相同,提供汇报功能。不同的是borg中采用拉取的方式进行,而在yarn中采取的是推的方式。另外borg中的副本会与一些borglet通信以此方式来减少更新负担。

3.4 可扩展性

虽然现在还不确定borg的中心式框架的可扩展性极限在哪里,至少现在还没有遇到极限,一个master可以管理数千台机器,每分钟处理上万个任务,一个master配置10-14核CPU和50G内存。

为了提升可扩展性,我们将调度器分割为独立进程,这样它可以与Borgmaster并行进行操作。调度器具备多个副本,每个调度器在cell状态的副本上进行操作。它重复以下操作:从被选举master中检索状态变更;更新本地状态副本;进行调度、分配任务;将分配通知被选举master。Master会检查调度器的调度操作,如果发生调度冲突则再下一轮重新调度,否则接受并应用该调度结果。这套机制与Omega一文中的乐观并发控制高度相似。此外,Borg最近支持为不同负载类型使用不同调度器。

为了提高系统可扩展性,Borg调度器还作了三个方面优化:(1)得分缓存:可以将可行性检查和打分结果缓存下来;(2)等价类:同一job中的task通常具有类似的约束,因此可以将多个任务视为一个等价类;(3)随机化:大规模计算所有机器的得分代价太大,通过随机遍历机器找到足够多的满足可行性的机器,然后对这些机器进行打分。

3.5 可用性

可用性对于分布式系统非常关键,borg通过一系列的优化能够使其可用性达到99.99%。

  • 自动重现调度被驱逐的任务;
  • 将任务分散在不同的失败域中以降低相关失败;
  • 限制一个作业中任务的个数和中断率;
  • 使用声明性的期望状态表示和幂等变异操作,以便失败的客户端可以无害地重新提交任何被遗忘的请求;
  • 限制任务重调度速率,因为不能区分大规模机器故障和网络分区;
  • 避免引发错误的任务-机器配对
  • 关键数据持久化,写入磁盘;

5. 利用率

在这里插入图片描述
实验部分主要运行谷歌内部的任务,首先通过实验证明混合部署比独立部署具有更高的利用率,结果如上图所示。然后通过实验证明了几种提高集群资源利用率得方法,包括cell sharing(处理单元共享,评价指标CPI),large cell(大处理单元), fine-grained resource requests(细粒度资源请求)和resource reclamation(资源矫正后者理解为资源超售)。

对于资源超售部分说明:

  • 设计背景:对于用户来说会因为负载高峰的波动出于心理安全的原因在申请资源时申请较多的资源,但是实际情况任务偶尔会使用所有资源,其他大部分时间都使用较少的资源,这就会到这大量的资源闲置。
    在这里插入图片描述
  • 处理方式:一个job可以定义一个资源上限,其用于决定是否有足够的资源配额来提交作业,并用于决定是否具有足够的空闲资源来调度任务。因为borg中会kill掉尝试使用更多ram的task或者cpu节流,所以用户总是会多申请资源。对于可以容忍低质量资源的工作,borg会评估任务将使用的资源,并回收空闲资源,整个过程称为资源回收,评估过程称为任务预留。最初的预留值与其资源请求一致,然后300秒之后,会慢慢降低到实际使用率外加一个安全边缘。如果利用率超过资源预留值,预留值会快速增长。

6. 隔离

由于集群资源共享,所以每个机器不可避免会运行不同的任务,这就需要有一套较好的机制来确保这些任务之间的影响最小。

  • 安全隔离,使用Linux chroot来进行安全隔离;
  • 性能隔离,每个task都运行在基于cgroup的container中,并且在实际的资源使用中,prod类型的任务可以独占某些资源而non-prod的任务则不行。

Borg按照两种方法来控制overload和overcommitment,overload通过将任务分为Latency-sensitive(LS)和batch两种来控制资源;overcommitment则通过任务对资源的占用划分为compressible(例如cpu、I/O带宽)和non-compressible(例如内存、磁盘空间)。

如果机器上non-compressible的资源不够用了,那么Borglet会立刻按照优先级杀掉本机上优先级低的任务释放出资源;如compressible的资源不够用,那么Borglet会先尝试控制batch任务对cpu的使用来度过高峰,如果还不行,就会由borgmaster拿掉机器上的一个或更多的低优先级任务。

Borglet里面还有一个用户态的程序来控制分配给prod和non-prod任务的内存,如内存超过了task的限制,或本机的内存不够用了,内核中处理OOM事件的部分将按照优先级来kill task。

在CPU方面,在资源的分配上,对于LS tasks允许占用整个cpu核,而batch tasks则可以在任何核上运行,但batch tasks会在较低优先级上,Borg修改了内核的CPU调度器,允许根据每个container的load状况来动态决定是否要kill batch tasks,同时避免多个LS tasks在一个cpu上争抢,目前Borg仍然在尝试的是在cpu调度时更好的考虑线程亲和、NUMA亲和之类的。

7. 相关工作

  • 对雅虎、谷歌、Facebook的集群trace数据的分析工作
  • mesos介绍
  • YARN
  • Fuxi,其思想与YARN类似,不过采用反向调度,为最近可用的机器匹配任务
  • omega
  • quincy针对DAG任务的调度系统
  • Apollo是微软内部针对短作业的调度系统,其主要提供了一个预测矩阵
  • kubernetes

8. 总结

  • 该篇论文阐述了borg系统的一些设计思想,某些设计思想有很强大引导作用。但是目前borg没有开源,还不清楚其系统内部具体是怎么实现的。不过K8S作为同类系统吸纳了很多borg的思想,学习K8S会对其系统内部设计有更深的理解。
  • borg系统其实可以看做是集中式的调度器,因为其调度的对象是task,master负责调度和通信工作;
  • 为了提高并发度,有多个调度器采用状态共享无锁乐观并发的方式进行,这可以说是开创性的,现在很多商用的云平台都采用这种方式来提高并发度;
  • 可以同时调度在线和离线任务,这就是混合调度的起源。虽然YARN和Mesos可以调度不同计算框架的任务,但是对延迟敏感和延迟非敏感的任务混合调度支持还不好。论文中说到目前很多公司都是将在线集群和离线集群分开,谷歌研究表示如果其将在离线分开则需要增加20%左右的机器,而其目前的机器体量在500万以上。目前工业界也在进行实践,例如阿里的混部系统,将Fuxi和Sigma进行混合调度;百度在其内部也实现了类似的在离线混布的功能matrix。
  • 不同类型的任务采用不同的调度器;
  • 资源超售机制的提出,这种机制的提出能够显著提高系统的资源利用率,其实对于目前来说,调度系统内部我们已经可以做的比较优了,唯一的问题就是对于一个任务我们无法预知到该任务真正需要多少资源,这就导致了在资源分配的时候造成大量资源闲置和浪费。而资源超售可以很好的解决这个问题。
  • 细粒度的任务约束性描述,包括操作系统版本、处理器架构等。

9. K8S从borg中吸取的经验和教训

  • 在Borg中,Job是用来对task进行分组的唯一方式,这种方式相当不灵活,导致用户自己发展出很多方式来实现更灵活对分组。 Kuberntes吸取了Borg的这个教训,引入了Labels,使Pod可以被灵活地分组。
  • 在Borg中,一个node上的所有task共享node的ip,这直接导致端口也成为一种资源,在调度时候需要被考虑。Kubernetes为每个Pod分配独立的IP。
  • Kubernetes借鉴了Alloc(node上的一块可以被多个task共享的资源),设计了Pod(多个容器的封装)。这种设计,可以将一个pod中的任务分拆成不同 的容器,由不同的团队开发,特别是一些辅助性的任务,例如日志采集等。
  • org中task、job的命名机制被kubernetes借鉴,提供了service等特性。
  • Borg将系统内部的事件、task的日志暴露给用户,提供了不同级别的UI和调试工具,使几千个用户能够自助地使用Borg。 Kubernetes吸收了Borg的这些特性,引入cAdvisor、Elasticsearch/Kibana、Fluentd等组件。
  • Borg采用中心式设计,管理功能集中于Master,这种设计方便了后续更多特性引入以及规模的扩展。 Kubernetes更进一步,apiserver只负责处理请求和状态维护,集群的管理逻辑被拆分到多个更精悍、内聚的controller中。

10.一些实验经验和技巧

  • 使用一个规模足够大的cell,在同等任务时,比使用多个规模较小的cell,需要的node数更少。
  • 50%的node上运行了超过9个任务7。
  • 非Leader的BorgMaster自动分管node,负载均摊。
  • BorgMaster轮询Borglet,防止出现请求风暴。
  • Borget向BorgMaster反馈的状态是全量的,但对应BorgMaster只将增量部分提交给作为Leader的BorgMaster。
  • BorgMaster宕机,node上的task保持原状。
  • Task启动平均耗时25秒,80%的时间是在读取Task的Package文件
  • node失联后,它所承担的task已经在其它node上重建,node重新上线后,依然在运行的任务在BorgMaster的指示下删除。
  • 用户向Borg发起的请求被设计成幂等的。

参考Google大规模集群管理系统Borg解读

                                
发布了114 篇原创文章 · 获赞 25 · 访问量 6万+

你可能感兴趣的:(云计算调度)