http://dongxicheng.org/mapreduce-nextgen/yarn-mesos-borg/
细数国内外互联网巨头,他们都有自己的资源管理系统,比如Google的Borg,Twitter的Mesos,阿里巴巴的Fuxi,微软的Apollo等。本文涉及到的Google Borg相关内容,主要参考了论文“Large-scale cluster management at Google with Borg”
Google Borg采用了集中式master/slave架构 (Borgmaster和Borglet),其中Borgmaster负责集群资源管理和调度,Borglet负责执行实际的任务,Borgmaster通常有5个实例,通过Paxos协议组成一个高可用分布式集群;Borgmaster会周期性地主动poll各个Borglet以获取其状态和资源使用情况,一旦发现其出现问题,会触发容错,之所以采用poll,而不是像YARN/meso采用心跳机制,主要是考虑到这种方式能够更容易地控制网络通信开销,避免active Borgmaster选举时出现网络风暴等。前面提到Borg中存在5个Borgmaste,他们每个会分管一部分Borglet的状态获取和探测,并将收到的信息压缩和求差后,交给active Borgmaster,以分摊active Borgmaster压力。从这种细粒度的设计可以初探端倪:Borg尽管采用了集中式架构,但扩展性仍很好,对应Borg论文中这句话“We are not sure where the ultimate scalability limit to Borg’s centralized architecture will come from; so far, every time we have approached a limit, we’ve managed to eliminate it.”
开源系统YARN和Mesos均采用了双层调度架构,需要注意的是,google在论文omega和borg中均认为YARN是集中式架构,而把Mesos划归为双层调度架构,个人认为这是不准确的,YARN跟Mesos类似,ResourceManager负责集群调度,ApplicationMaster负责framework和应用程序内部调度,相比Mesos,由于YARN的ResourceManager要维护各个分配出去的Container的状态和位置等信息,因此,要比Mesos Master重一些。YARN和Mesos的Master均采用Zookeeper解决高可用问题,其中active master进行资源管理和调度,而backup master仅仅是backup作用,不会协助active master做任何事情;Slave会主动通过周期性心跳向Master汇报状态信息。
1.1扩展性
Borg对所管理的所有borglet进行了水平分片,让每个 Borgmaster分管一部分,这些borgmaster共享集群元信息,每个Borgmaster均可以为应用程序分配资源,但backup Borgmaster需要将分配信息发送给active Borgmaster进行审批,这个地方与Google Omega中的share state是一致的。在这方面,YARN和Mesos均是做不到,他们只有一个master进行资源管理和调度,在超大集群中,master可能会成为瓶颈,这是YARN和Mesos需要改进的方向。
1.2高可用
应用程序方面:Borg内置了各种错误重试机制,确保在机器故障,网络故障等情况下,应用程序不会失败。
服务方面:Borgmaster和Borglet挂掉后,其上正在运行的应用程序和任务不会受影响,这一点,目前Mesos和YARN已经做到。
2.对批处理作业和长服务的支持
为了简化应用程序分类,Borg把应用程序分成两类,批处理作业和长服务,批处理作业是类似于MapReduce和Spark的作业,在一定时间内会运行结束,长服务则类似于web service,HDFS service等,可能会永久运行下去,永不停止。批处理作业和长服务是绝配,混合部署它们对提高集群利用率是非常有帮助的,长服务占用大块资源,而批处理作业穿插到长服务未使用的小块资源中,当高优先级的应用需要资源时,可直接杀死抢占批处理作业。根据borg和omega论文的描述,在google集群中,长服务占用了集群中70%~80%的资源。
Mesos和YARN均对批处理作业有良好的支持,这类应用的支持也是最简单的,而难点在于长服务,一旦引入长服务,会带来以下问题:
(1)资源竞争与饿死问题。 当一个集群中只存在批处理作业时,资源调度是很容易做的,因为资源释放和申请是不断在进行中的,任何资源的申请都可以得到满足。但存在长服务后则不同,因为目前主流的调度器均采用资源预留的调度机器,比如一个作业需要10G内存,目前没有任何节点存在10GB内存呢,为了避免永远拿不到资源,调度器会找一个存在部分资源的节点,比如node1存在6GB内存,则会为该作业预留着,一直等到再次释放出4GB ,则将node1上的10GB内存分配给该作业,整个过程在批处理场景中能自然的发生,一旦加入长服务后,则可能产生饿死现象,也就是说node1上的4GB内存可能永远等不到,因为可能被长服务占着。在这方面,Borg做得很好,但mesos和YARN均存在饿死问题,目前无法解决。
(2)服务高可用问题。 资源管理系统一旦支持长服务后,应保证系统服务出现故障时,上层的长服务不会受到应用,比如在YARN中,ResourceManager或者NodeManager出现故障后,其上运行的长服务,比如MySQL,不应受到影响,到恢复后,重新接管这些服务。这一点,Borg/Mesos/YARN(本文指的是Hadoop 2.6.0之后的版本)均已经支持。
(3)日志收集和滚动。 长服务永不停止,为了方面排错和监控,长服务的日志收集也是需要解决的,Borg/Mesos/YARN在这一块均有很好地支持,其中mesos/YARN可通过上层框架解决,比如Mesos中的 aurora和Marathon(apache顶级项目),YARN中的Twill和Slider(Apache二级项目)。
(4)服务发现。长服务部署到资源管系统中后,可能被调度运行到任意一个节点上,为了让外界的访问者(客户端)发现自己的位置,需要有一个服务注册组件登记这些长服务,Borg/Mesos/YARN均对服务注册有支持,Mesos可通过上层框架,比如Aurora,YARN内核内置了对服务注册的支持。
(5)资源伸缩。长服务运行一段时间后,由于访问量的增加或减少,可能需要动态调整所使用的资源量。资源伸缩有两个维度:一个是横向的,即增加实例数目;另一方面是纵向的,即原地增加正在运行实例的资源量。这方面Borg/Mesos/YARN均已经支持,其中横向支持是通过上层框架实现的,而纵向支持是通过资源管理系统内核支持的(https://issues.apache.org/jira/browse/YARN-1197 )。
(6)服务配置更新和在线升级。 这一块均与资源管理系统无直接关系,一般通过上层的框架实现。
3.其他实现机制
(1)资源预申请(在borg论文中,称之为“alloc”)。应用程序可以预申请一些资源,可用于快速启动一些低延迟task,动态扩容等方面使用。 这一块mesos和yarn没有直接支持。在mesos和yarn中,应用框架申请到资源后,必须在一定时间内使用,如果未使用,mesos和yarn会进行回收。 实际上,mesos和yarn可以在上层框架层面解决这一问题,比如hive on Tez则实现了资源预申请,具体可参考我的这篇文章:“Tez:运行在YARN上的DAG计算框架”:http://dongxicheng.org/mapreduce-nextgen/tez-yarn-dag/。
(2)作业优先级与资源抢占。在一个混合应用部署的集群中,抢占是必须要支持的,为了支持抢占,必须提前合理规划好应用程序的优先级,以便于在一些情况下,为高优先级的作业抢占低优先级作业的资源。 资源抢占在YARN中以及得到了支持,但没能够用在批处理和长服务混合资源调度中。实际上,在YARN中,资源抢占仅仅用于队列回收资源的场景中。
(3)份额限制(Quota)和进入控制(admission control)
为了防止应用程序无限制申请资源,满足长服务的SLA,需要由一套完善的admission control机制,在开源实现中,YARN在2.6.0开始加入了这一套机制,具体参考:https://issues.apache.org/jira/browse/YARN-1051 。
4.总结
目前看来,Mesos/YARN的架构和设计上,与Google Borg仍有一定的差距,但需要注意的是,很多细节之处,都是tradeoff的结果,很难说哪种机制更适合我们的场景,对于搭建中小型的集群和数据中心,Mesos/YARN已经绰绰有余了。
5.参考资料
(1)google paper:Large-scale cluster management at Google with Borg
(2)google paper:Omega: flexible, scalable schedulers for large compute clusters
(3)YARN paper:Apache Hadoop YARN: Yet Another Resource Negotiator
(5)Apache slider:http://slider.incubator.apache.org/
(6)Apache Twill:http://twill.incubator.apache.org/
(7)Apache Aurora:http://aurora.apache.org/
(8)Apache marathon:https://mesosphere.github.io/marathon/
http://allenlsy.com/google-production-environment
Cluster 需要高效地管理里面的服务器资源。 Google 内部,有 job 的概念。每个 job 有很多子 task(关于 job 和 task 的概念,可以参考之前 Netflix Titus 架构 部分的内容)。job 还定义了期望使用多少资源。
工程师们使用内部软件运行 job。job 被发送给 Borg (Borg 是 Google 内部的容器管理工具,暂时未开源)。Borg master 询问 scheduler 应该由哪几台机器来运行 job。得到回应后,将请求发给指定的机器 Borglet,开始运行 job。如果job 失败,应该会被自动重新运行。
Google 使用 Borg name service (BNS)来定位服务器上的 task。格式是
/bns/<cluster>/<user>/<job name>/<task number>
。
BNS 地址需要映射到 IP:port 地址,并且保证同步。
Lock service
这个 BNS 映射到 IP 的信息,存储在另一个内部服务 Chubby 中。Chubby 是一个分布式系统中的锁服务,它下面还提供一个可以用类似 API 方式操作的文件系统,并使用 Paxos 算法来实现各个服务器之间的异步一致 (asynchronous consensus)。这个映射信息就是在 Chubby 里面。
- HDD + SSD:在存储系统的最底层,是机械硬盘和固态硬盘。
- D:意思是 disk,用来管理 HDD + SSD。D 提供的服务是存储临时数据,主要是给运行中的 job 使用。
- Colossus 是基于 google file system 开发的分布式文件系统,运行在 cluster 之内,支持永久保存数据,支持复制、加密等等。
- Bigtable 是一个NoSQL 数据库。Bigtable 可以保存有序的数据。在 cluster 之间进行复制时,bigtable 保证 eventually consistent。
- Spanner 是一个 NewSQL 数据库。旨在实现具有 NoSQL 一般可扩展性的关系型数据库。
通过 Borg, Google 将服务器集群实现的和单独一台电脑一样,可以运行 job 也可以存储数据。但是分布式系统会面临机器损坏的情况。举个例子,如果一个job 运行到一半,机器坏了,怎么办?
Monitoring
上面那个问题是通过监控的办法解决的。 Borgmon 是 Borg 的监控工具。图中, Borgmon 存在于很多层级。Borgmon 获取各个 task 的运行状态信息,然后最后向上汇总到 global borgmon。而 cluster borgmon 除了把信息汇总给 global borgmon,还发送给 google 的 time series database (时序数据库)以及 alert manager 警报系统。例如当某个 cluster 的错误率异常高的时候,会触发警报。
还有一个辅助工具 Prober ,负责给 task 所在服务器发送请求,并监控响应时间。这是从另一个角度观察服务器的健康状况。Prober 也将信息汇总给 borgmon。
Inter-task communication 任务之间的通讯
task 和其他 task 之间进行通讯使用 Stubby。Stubby 是一个 RPC(远程进程调用) 服务,基于 http,使用 protobuf 协议。或者可以简单的说,task 之间使用 protobuf 进行 RPC。
代码提交成功后,Blaze (Google 的 build tool,它的开源版是 Bazel)将代码编译成二进制文件。Blaze 的使用,需要工程师指定 build 的 output 输出,dependencies 依赖等等。
另一个工具 Continuous testing,从 repo 获得最新代码后开始运行自动化测试。通过后,一个叫 Rapid 的工具会调用 Blaze 来生成 MPM (Midas Package Manager)package。MPM 给软件加上名字、版本号,以及签名以确保软件的 authenticity。最后 MPM package 被 Sisyphus 部署到 production。Sisyphus 是 google 的 deploy 工具,可以做 deploy 过程中很多复杂而又繁琐的工作。并且可以指定 deploy 的方式,比如是立即通过 canary 的方式 deploy,还是指定未来某个时刻 deploy。
http://allenlsy.com/john-wikes-google-borg
在 Borg 的内部,每一台机器上都运行了一个 Borglet 的客户端,负责收集机器的状态信息,并反馈给 BorgMaster。以上面图为例,当收到这个 job 之后,BorgMaster 与 scheduler 合作,计划将在什么时候,在哪些机器上运行这10000个 replica。
对于 Replica,我们并不关心它是在 VM 还是在 container 里运行。 Borg 会根据当前 cell 里面机器剩余资源的情况决定在哪台机器上运行多少个 replica。之后,就能在 Borg 的 UI 界面上看到,多少个 replica 正在运行等等状态了。
Google 内部 replica 运行在 container 里面,而 GCP 上的 replica 运行在 VM 里面,因为需要更高的安全策略。
根据 job 的优先度,可以被分为 prod 和 non-prod 两类。prod 会被优先运行。而 non-prod 通常是一些后台任务或者不太紧急的任务。当机器资源不足时,non-prod 要为 prod 让位(preemption)。让位的方式就是, non-prod job (很可能以容器的形式在运行)会被 kill,然后在另一台机器上重新启动运行。上面这幅图里展示了,prod 和 non-prod job 在一整周的运行时间里,所遇到的问题的平均次数。灰色部分表示 preemption 的情况。蓝色部分表示例行服务器系统升级。可以看到,prod 和 non-prod 同样时间里为系统升级被 kill 的次数差不多,但 non-prod 的 preemption 次数就多很多了。
另外,在 Google 的生产环境中,每天都有很多机器故障停机。此时 Borg 会在其他机器上重新运行被停掉的程序。
但是,很多时候人们在描述 job 的时候会要求比 job 所需更多的资源。在 Borg 里,如果出现这种情况,那么 Borg 会把超出合理范围的预留资源分配给其他 job,连 prod 的高优先级 job 也不能例外。
那么,下一个问题就是,多少预留资源是合理的?
经过观察发现,大部分用都只使用了声明资源的 1/3 左右。当然这是为突发状况准备的。上图是一段时间内一台服务器的资源使用情况。最顶上灰色是 job 声明需要的资源,红色线是实际使用率。Google 做了一个算法,计算出了一个合理的预留资源数量,在图中用蓝色线表示。红蓝线之间黄色区域是给 job 的实际的预留资源。而绿色区域是可以用作其他 job 的资源。这条蓝色的线会根据 job 使用资源的变化而调整。我们可以看出,当流量猛增的时候,黄色区域变小,Borg 会自动 scale。
绿色区域经常被用作 non-prod 的 job 使用。要记得,当 prod 需要更多资源时, 这些 non-prod job 会被清掉,在另一个 cell 上重启。所以这个操作当然是越少越好。
当工程师在开发一个产品的时候,在服务器端,他们如果只关心他们 app 的服务器,只关注他们的业务逻辑,那么对他们是最有利的。这个 app 服务器在整个 app 所需的服务器里面只是很小的一部分。还需要有其他服务器来做辅助工作,比如存储、保存配置信息、监测、计算服务器使用账单、系统升级等等。而很多 app 可以共用这些辅助服务器。
在提供了这些配套服务以后,这样就有了面向 Google 以外的 Google Cloud Platform。而一个轻量版的 Borg ,结合 Docker 作为容器打包工具,就成为了 Kubernetes。