摘要
Google的Borg系统是一个集群管理器,它在许多集群中运行着成千上万的工作,这些工作来自成千上万个不同的应用程序,每个集群中最多有成千上万台计算机。
通过将准入控制,有效的任务打包,过量使用和机器共享与流程级的性能隔离相结合,可以实现高利用率。 它通过运行时功能(可最大程度地缩短故障恢复时间)和调度策略来降低相关故障的可能性,从而支持高可用性应用程序。 Borg通过提供声明性(declarative)的作业规范语言,名称服务(name service)集成,实时作业监视以及分析和模拟系统行为的工具,为用户简化了生活。
我们提供了Borg系统架构和功能的摘要,重要的设计决策,一些政策决策的定量分析以及对十年运营中获得的经验教训的定性分析经验。
1 介绍
我们在内部将群集管理系统称为Borg授予,计划,启动,重新启动和监视Google运行的所有应用程序。 本文解释了如何。
Borg提供了三个主要好处:(1)隐藏资源管理和故障处理的详细信息,以便其用户可以专注于应用程序开发; (2)具有很高的可靠性和可用性,并支持相同的应用; (3)让我们有效地在成千上万的计算机上运行工作负载。 Borg不是第一个解决这些问题的系统,但是它是这种规模的,具有如此程度的灵活性和完整性的操作系统之一。 本文围绕这些主题进行了组织,并总结了十多年来我们在生产中使用Borg进行的定性观察。
2 用户角度
Borg的用户是运行Google应用程序和服务的Google开发人员和系统管理员(站点可靠性工程师或SRE)。 用户以作业的形式将其工作提交给Borg,每个作业都包含一个或多个都运行相同程序(二进制)的任务。 每个作业都在一个Borg单元中运行,Borg单元是一组作为一个单元进行管理的计算机。 本节的其余部分描述了Borg用户视图中暴露的主要功能。
2.1 工作负载
Borg单元运行的异构工作负载包括两个主要部分:第一个是长期运行的服务,应“永不中断”,并处理短暂的对延迟敏感的请求(几微秒到几百毫秒)。 此类服务用于面向最终用户的产品(例如Gmail,Google文档和网络搜索)以及内部基础架构服务(例如BigTable)。 第二个是批处理作业,需要花费几秒钟到几天的时间才能完成; 这些对短期性能波动的敏感度要低得多。 不同单元之间的工作负载组合会有所不同,这些单元会根据其主要承租方运行不同的应用程序混合(例如,某些单元是非常繁重的批处理),并且还会随着时间而变化:批处理作业来来往往,许多最终用户 面对服务工作会看到日间使用模式。 Brog必须同样妥善处理所有这些案件。
可以从2011年5月开始的为期一个月的公开跟踪中找到具有代表性的Borg工作负载[80],该跟踪已被广泛分析(例如[68]和[1、26、27、57])。
在过去的几年中,已经在Borg之上构建了许多应用程序框架,包括我们的内部MapRe duce系统[23],FlumeJava [18],Millwheel [3]和Pregel [59]。其中大多数都有一个控制器,该控制器提交一份主工作和一个或多个工人工作。前两个与YARN的应用程序管理器[76]扮演相似的角色。我们的分布式存储系统(例如GFS [34]及其后续CFS,Bigtable [19]和Megastore [8])都在Borg上运行。
在本文中,我们将优先级较高的博格工作分类为“生产”(生产)工作,其余分类为“非生产”(非生产)工作。大多数长时间运行的服务器作业都是prod;大多数批处理作业都是非生产性的。在一个代表性的单元中,生产作业分配了大约70%的CPU资源,代表了大约60%的CPU使用率。它们大约分配了总内存的55%,约占总内存的85%总内存使用量。在§5.5中,分配和使用之间的差异将被证明很重要。
2.2 集群和单元
一个单元中的机器属于一个集群,由连接它们的高性能数据中心级网络结构定义。 群集位于单个数据中心建筑物内,建筑物的集合构成一个站点。群集通常承载一个大型单元,并可能具有一些较小规模的测试或专用单元。 我们竭力避免出现任何单点故障。
排除测试单元后,我们的平均单元大小约为10,000台机器; 有些更大。 单元中的机器在许多方面都是异构的:大小(CPU,RAM,磁盘,网络),处理器类型,性能和功能(例如外部IP地址或闪存)。 Borg通过确定在单元中的哪个位置运行任务,分配资源,停止其程序和其他依赖项,监视其运行状况并在失败时重新启动它们,从而使用户免受这些差异中的大多数的影响。
2.3 工作和任务
Borg作业的属性包括其名称,所有者和它拥有的任务数。 作业可能会受到约束,以迫使其任务在具有特定属性(例如处理器体系结构,操作系统版本或外部IP地址)的机器上运行。 后者的行为就像偏好而不是要求。 可以将工作的开始推迟到之前的工作完成为止。 作业仅在一个单元中运行。
每个任务都映射到在计算机上的容器中运行的一组Linux进程[62]。 由于我们不想支付虚拟化的成本,绝大多数Borg工作负载不在虚拟机(VM)内运行。此外,该系统是在我们对没有虚拟化的处理器进行大量投资对支持硬件的设计方案。
任务也具有属性,例如其资源需求和任务在任务中的索引。 大多数任务在所有任务中的属性都是相同的,但可以被覆盖(例如,提供特定于任务的命令行标志)。每个资源维度(CPU核心,RAM,磁盘空间,磁盘访问速率, TCP端口等)以细粒度独立指定; 我们不强加固定大小的存储桶或插槽(第5.4节)。 Borg程序是静态链接的,以减少对其运行时环境的依赖性,并构成为二进制文件和数据文件的程序包,其安装由Borg精心安排。
用户通过向Borg发出远程过程调用(RPC)(通常是从命令行工具,其他Borg作业或我们的监视系统,第2.6节)来对作业进行操作。 大多数作业描述是用声明性配置语言BCL编写的。 这是GCL的一种变体[12],它生成protobuf文件[67],并使用一些Borg特定的关键字进行了扩展。 GCL提供了lambda函数以允许计算,并且应用程序使用这些函数来调整其配置以适应其环境。 成千上万的BCL文件的长度超过1000行,而我们已经积累了数千万行的BCL。Borg作业配置与Aurora配置文件相似[6]。
图2说明了作业和任务在其生命周期内所经历的状态。
图2:作业和任务的状态图。 用户可以触发提交,取消和更新转换。用户可以通过将新作业配置推送到Borg,然后指示Borg将任务更新为新规范来更改正在运行的作业中某些或所有任务的属性。这是一种轻量级的,非原子性的事务,可以很容易地撤消,直到关闭(提交)。通常以滚动方式进行更新,并且可以限制更新导致的任务中断(重新计划或抢占)的数量;会导致更多中断的任何更改都将被跳过。
某些任务更新(例如,推送新的二进制文件)总是会要求重新启动任务;有些(例如,增加的资源需求或更改的约束)可能会使任务不再适合计算机,并导致任务被停止和重新安排;并且某些操作(例如更改优先级)始终可以在不重新启动或移动任务的情况下完成。
在SIGKILL抢占任务之前,可以要求通过Unix SIGTERM信号通知任务,因此它们有时间清理,保存状态,完成任何当前正在执行的请求并拒绝新请求。如果抢占者设置了延迟范围,则实际通知可能会少一些。实际上,通知是在大约80%的时间内发送的。
2.4 分配(Alloc)
Borg分配(分配的缩写)是一台机器上可以运行一个或多个任务的一组保留资源。 无论是否使用它们,资源都会保持分配状态。Alloc可用于为将来的任务留出资源,在停止任务和重新启动任务之间保留资源,以及将来自不同作业的任务收集到同一台计算机上–例如 ,一个Web服务器实例和一个关联的日志保护程序任务,该任务将服务器的URL日志从本地磁盘复制到分布式文件系统。 分配资源与机器资源的处理方式相似;一个内部运行的多个任务共享其资源。 如果必须将分配重定位到另一台计算机,则将其任务重新安排。
分配集就像一项工作:它是一组在多台机器上保留资源的分配。 创建分配集后,可以提交一个或多个作业以在其中运行。 为简便起见,我们通常使用“任务”来指代一个分配或顶级任务(一个在分配外的任务),而“作业”则指代一个作业或分配集。
2.5 优先级,配额和准入控制
如果出现的工作量超出了容纳的工作量,会发生什么情况? 我们为此的解决方案是优先级和配额。
每个作业都有一个优先级,一个小的正整数。 高优先级任务可以以低优先级任务为代价获得资源,即使这涉及抢占(杀死)后者。 Borg为不同用途定义了不重叠的优先级带,包括(按降序排列):监视(monitoring),生产(production),批处理(batch)和best efford(也称为测试或免费)。 在本文中,生产工作属于监视和生产带中的工作。
尽管抢占式任务通常会在单元格中的其他地方重新安排,但如果高优先级的任务碰到了优先级稍低的任务,而又抢占了另一个稍低优先级的任务,则抢占级联可能会发生,依此类推。 为了消除大多数情况,我们不允许生产优先级范围内的任务互相抢占。 细粒度的优先级在其他情况下仍然有用,例如,MapReduce主任务以比其所控制的工作人员稍高的优先级运行,以提高其可靠性。
优先级表示在单元中正在运行或正在等待运行的作业的相对重要性。 配额用于决定允许哪些作业进行调度。 配额表示为一段时间(通常为几个月)内给定优先级的资源数量(CPU,RAM,磁盘等)的向量。 数量指定了用户的作业请求一次可以请求的最大资源量(例如,“从现在到7月底,单元xx中的产品优先级为20 TiB RAM”)。 配额检查是准入控制的一部分,而不是计划的一部分:配额不足的作业在提交后将立即被拒绝。
较高优先级的配额要比较低优先级的配额花费更多。生产优先级配额仅限于单元中可用的实际资源,因此提交适合其配额的生产优先级作业的用户可以期望其运行,模态碎片化和约束。即使我们鼓励用户购买的配额不要超过需求,但许多用户还是超买,因为这可以使他们避免应用程序用户群增长时的未来短缺。我们通过降低优先级的配额超额销售来解决此问题:每个用户都有无限的购买空间优先级为零的配额,尽管由于资源超额预订,这通常很难执行。可能会接受低优先级的工作,但由于资源不足而使其处于待处理状态(计划外)。
配额分配是在Borg之外进行的,并且与我们的物理容量计划直接相关,其结果反映在不同数据中心的配额价格和可用性上。用户作业只有在具有足够优先级的配额时才被接受。配额的使用减少了对诸如主导资源公平(DRF)[29,35,36,66]之类的政策的需求。
Borg有一个功能系统,可以为某些用户提供特殊的特权。例如,允许管理员删除或修改单元中的任何作业,或者允许用户访问受限制的内核功能或Borg行为,例如禁用其作业上的资源估计(第5.5节)。
2.6 命名和监控
创建和放置任务还不够:服务的客户端和其他系统需要能够找到它们,即使将它们重新放置到新计算机上也是如此。 为此,Borg为每个任务创建一个稳定的“ Borg名称服务”(BNS)名称,其中包括单元名称,作业名称和任务编号。Borg将任务的主机名和端口写入一致且高度可用的Chubby文件中。 此名称,我们的RPC系统将使用该名称来查找任务端点。 BNS名称也是任务DNS名称的基础,因此,用户ubar在单元cc中的作业jfoo中的第五十个任务可以通过50.jfoo.ubar.cc.borg.google.com进行访问。 Borg还会在每次更改时将作业大小和任务运行状况信息写入Chubby,以便负载平衡器可以看到将请求路由到何处。
几乎在Borg下运行的每个任务都包含一个内置的HTTP服务器,该服务器发布有关任务运行状况和成千上万个性能指标(例如RPC延迟)的信息。 Borg监视运行状况检查URL,并重新启动无法及时响应或返回HTTP错误代码的任务。监视工具可跟踪其他数据,以用于仪表板并针对违反服务水平目标(SLO)的情况发出警报。
一项名为Sigma的服务提供了一个基于Web的用户界面(UI),用户可以通过该界面检查其所有作业,特定单元的状态,或者向下钻取到各个作业和任务以检查其资源行为,详细的日志,执行历史,以及最终的命运。我们的应用程序产生大量的日志信息;它们会自动轮换状态以避免磁盘空间用尽,并在任务退出后保留一段时间以帮助调试。如果作业没有运行,Borg会提供“为什么要暂挂(pending)?”注释(annotation),以及有关如何修改作业的资源请求以更好地适应单元的指南。我们发布了可能很容易安排的“整合”资源形态指南。
Borg在Infrastore中记录所有作业提交和任务事件,以及每个任务的详细资源使用信息,Infrastore是可扩展的只读数据存储,具有通过Dremel的类似SQL的交互式界面[61]。此数据用于基于使用情况的计费,调试作业和系统故障以及长期容量规划。它还提供了Google集群工作负载跟踪的数据。
所有这些功能可帮助用户了解和调试Borg的行为及其工作,并帮助我们的SRE每人管理数万台计算机。
3 Brog架构
一个Borg单元由一组计算机,一个逻辑上集中的控制器(称为Borgmaster)和一个称为Borglet的代理进程组成,该进程在该单元中的每台计算机上运行(请参见图1)。 Borg的所有组件都是用C ++编写的。
3.1 Borgmaster
每个单元的Borgmaster包含两个进程:Borgmaster主进程和一个单独的调度程序(第3.2节)。 Borgmaster主进程处理客户端RPC,这些RPC会改变状态(例如,创建作业)或提供对数据的只读访问权限(例如,查找作业)。它还为系统中的所有对象(机器,任务,分配等)管理状态机,与Borglets通信,并提供Web UI作为Sigma的备份。
从逻辑上讲,Borgmaster是单个进程,但实际上可以重复五次。每个副本都维护该单元大多数状态的内存副本,并且此状态也记录在副本本地磁盘上的高可用性,基于Paxos的分布式存储中。每个单元由一个选举产生的主力同时充当Paxos的领导者和状态改变者,处理改变单元状态的所有操作,例如提交作业或终止机器上的任务。一个主当选(使用的Paxos)当单元成长时,每当主当选失败;它获取了Chubby锁,以便其他系统可以找到它。选择一个主节点并进行故障转移通常需要大约10秒钟,但在一个大单元中可能要花费一分钟的时间,因为必须重构某些内存状态。当副本从中断中恢复时,它会与其他最新的Paxos副本动态地重新同步其状态。
Borgmaster在某个时间点的状态称为检查点,其形式为定期快照以及保存在Paxos商店中的更改日志。检查点有许多用途,包括将Borgmaster的状态恢复到过去的任意点(例如,在接受触发Borg中的软件缺陷的请求之前,可以对其进行调试);用手将其固定在末端;建立事件的持久日志以供将来查询;和离线模拟。
称为Fauxmaster的高保真Borgmaster模拟器可用于读取检查点文件,并包含生产Borgmaster代码的完整副本,以及指向Borglet的残存接口。它接受RPC进行状态机更改并执行操作,例如“计划所有未完成的任务”,并且我们通过与RPC进行交互,就好像它是一个实时的Borgmaster一样,使用它来调试故障,并且模拟的Borglet可以真实地回放。检查点文件中的交互。用户可以单步执行并观察过去实际发生的对系统状态的更改。 Fauxmaster还可以用于容量规划(“该类型可以容纳多少个新工作?”)以及在更改单元的配置之前进行完整性检查(“此更改是否可以淘汰任何重要的工作?”)。
3.2 Scheduling
提交作业后,Borgmaster将其持久地记录在Paxos商店中,并将该作业的任务添加到等待队列中。 调度程序会对此进行异步扫描,如果有足够的可用资源满足任务的约束,调度程序会将任务分配给计算机。 (调度程序主要在任务而不是作业上运行。)扫描过程从高优先级到低优先级,并由优先级内的循环机制调制,以确保整个用户之间的公平性并避免行头阻塞大型任务 。 调度算法分为两部分:可行性检查,以找到可以在其上运行任务的机器;以及评分,从中选择一台可行的机器。
在可行性检查中,调度程序会找到一组满足任务约束并且具有足够“可用”资源的机器,其中包括分配给可以撤出的低优先级任务的资源。在计分中,调度程序确定每种可行机器的“优劣”。分数考虑了用户指定的偏好,但主要受内置标准驱动,例如,最小化被抢占任务的数量和优先级,挑选已经拥有任务包副本的机器,将任务分散在各个电源上以及故障域,以及打包质量,包括将高优先级任务和低优先级任务混合到一台机器上,以允许高优先级任务在负载峰值时扩展。
Borg最初使用E-PVM [4]的一种变体进行评分,该变体跨异构资源生成单个成本值,并在放置任务时最大程度地降低了成本变化。实际上,E-PVM最终会在所有计算机上分散负载,从而为负载峰值留出了空间-但以增加碎片为代价,特别是对于需要大部分计算机的大型任务;我们有时将此称为“最不适合”。
在可行性检查中,调度程序会找到一组满足任务约束并且具有足够“可用”资源的机器,其中包括分配给可以撤出的低优先级任务的资源。在计分中,调度程序确定每种可行机器的“优劣”。分数考虑了用户指定的偏好,但主要受内置标准驱动,例如,最小化被抢占任务的数量和优先级,挑选已经拥有任务包副本的机器,将任务分散在各个电源上以及故障域,以及打包质量,包括将高优先级任务和低优先级任务混合到一台机器上,以允许高优先级任务在负载峰值时扩展。
Borg最初使用E-PVM的一种变体进行评分,该变体在异构资源中生成单个成本值,并在放置任务时最大程度地降低了成本变化。实际上,E-PVM最终会在所有计算机上分散负载,从而为负载峰值留出了空间-但以增加碎片为代价,特别是对于需要大部分计算机的大型任务;我们有时将此称为“最不适合”。
频谱的另一端是“最佳拟合”,它试图尽可能紧密地填充机器。这使一些机器没有用户作业(它们仍在运行存储服务器),因此放置大型任务很简单,但是紧凑的包装将惩罚用户或Borg对资源需求的任何错误估计。这会伤害负载激增的应用程序,并且对于指定CPU需求较低的批处理作业特别不利,因此它们可以轻松地进行调度,并尝试在未使用的资源中合理地运行:20%的非生产任务要求的CPU内核数少于0.1。
我们当前的评分模型是一种混合模型,该模型试图减少搁浅资源的数量–由于计算机上的另一资源已完全分配,因此无法使用这些资源。与最适合我们的工作负载相比,它提供了大约3–5%的打包效率(在[78]中定义)。
如果在计分阶段选择的计算机没有足够的可用资源来满足新任务的需求,那么Borg会抢先(杀死)低优先级的任务,从最低优先级到最高优先级,直到成功为止。我们将抢占式任务添加到调度程序的待处理队列中,而不是迁移或休眠它们。
任务启动延迟(从作业提交到任务运行的时间)是一个已经受到并将继续引起极大关注的领域。它变化很大,中位数通常约为25 s。软件包安装约占总数的80%:已知的瓶颈之一是争用写入软件包的本地磁盘。为了减少任务启动时间,调度程序倾向于将任务分配给已经安装了必要软件包(程序和数据)的计算机:大多数软件包是不可变的,因此可以共享和缓存。 (这是Borg调度程序支持的唯一的数据局部性形式。)此外,Borg使用树和类似torrent的协议将程序包并行分发到计算机上。
此外,调度程序使用多种技术来扩展到具有成千上万台计算机的单元(第3.4节)。
3.3 Borglet
Borglet是本地Borg代理,它存在于单元中的每台计算机上。 它开始和停止任务; 如果它们失败,则重新启动它们; 通过操纵操作系统内核设置来管理本地资源; 滚动调试日志; 并将机器的状态报告给Borgmaster和其他监视系统。
Borgmaster每隔几秒钟会对每个Borglet进行一次轮询,以检索计算机的当前状态并将其发送给任何未解决的请求。这使Borgmaster可以控制通信速率,避免了需要显式的流量控制机制,并防止了恢复风暴。
当选主负责准备的消息发送到Borglets并与他们的反应更新小区的状态。为了提高性能,每个Borgmaster副本都运行一个无状态链接碎片,以处理与某些Borglet的通信。每当发生Borgmaster选举时,都会重新计算分区。对于弹性,在Borglet始终报告它的全部状态,但链路聚合碎片和报告唯一的区别的状态机,以减少在当选主更新负载压缩该信息。
如果Borglet不响应多个轮询消息,则其计算机将标记为已关闭,并且其正在运行的所有任务都将在其他计算机上重新安排。如果恢复了通讯,则Borgmaster会通知Borglet取消已重新安排的任务,以避免重复。即使Borglet与Borgmaster失去联系,它仍会继续正常运行,因此,即使所有Borgmaster副本都失败了,当前运行的任务和服务也会保持正常运行。
3.4 Scalability(可扩展性)
我们不确定Borg集中式架构的最终可扩展性限制将来自何处; 到目前为止,每次达到极限时,我们都设法消除了极限。 单个Borgmaster可以在一个单元中管理数千个机器,并且几个单元每分钟的到达率超过10000个任务。 繁忙的Borgmaster使用10–14个CPU内核和多达50个GiB RAM。 我们使用多种技术来达到这一规模。
早期版本的Borgmaster具有一个简单的同步循环,该循环接受请求,计划任务并与Borglet通信。 为了处理更大的单元,我们将调度程序拆分为一个单独的进程,以便它可以与其他Borgmaster函数在并行处理中一起运行,以复制它们以实现容错能力。 调度程序副本对单元状态的缓存副本进行操作。它反复:从当选主(包括分配和暂挂pending工作)检索状态改变; 更新其本地副本; 进行调度以分配任务;并通知那些为signments的选举的Master。 主机将接受并应用这些分配,除非它们不合适(例如,基于过期状态),这将导致它们在调度程序的下一个传递中被重新考虑。 这在本质上与Omega [69]中使用的乐观并发控制非常相似,并且实际上,我们最近为Borg添加了针对不同工作负载类型使用不同调度程序的功能。
为了缩短响应时间,我们添加了单独的线程来与Borglet对话并响应只读RPC。为了获得更高的性能,我们将这些功能分片(划分)到五个Borgmaster副本§3.3中。它们共同使UI的99%ile响应时间低于1s,并且使Borglet轮询间隔的95%ile低于10s。
使Borg调度程序更具可伸缩性的几件事:
分数缓存:评估可行性并为机器评分是昂贵的,因此Borg会缓存分数,直到机器的属性或任务发生变化–例如,机器上的任务终止,属性被更改或任务的要求改变。忽略资源数量的细微变化可减少缓存失效。
等价类:Borg作业中的任务通常具有相同的要求和约束,因此,Borg不会对每台机器上的每个待处理任务进行挖掘可行性,并为所有可行的机器评分,Borg只对每个等价项进行可行性和评分类–一组具有相同要求的任务。
宽松的随机化:计算大型单元中所有计算机的可行性和分数是浪费的,因此调度程序以随机顺序检查计算机,直到找到“足够”可行的计算机进行评分,然后在其中选择最佳计算机放。这减少了任务进入和离开系统时所需的计分和缓存失效数量,并加快了将任务分配给计算机的速度。宽松的随机性有点类似于Sparrow [65]的批量采样,同时还处理优先级,优先级,异质性和软件包安装成本。
在我们的实验(第3节)中,从头开始计划一个单元的全部工作量通常需要几百秒,但在禁用上述技术的三天后仍未完成。通过挂起队列的传递不到半秒钟即可完成。
图3:任务逐出率以及生产和非生产工作负载的原因。 2013年8月1日的数据4 可用性
失败是大型系统中的常态[10、11、22]。 图3提供了15个样本单元中任务逐出原因的细分。 在Borg上运行的应用程序应使用复制,将持久状态存储在分布式文件系统中以及(如果适用)偶尔使用检查点等技术来处理此类事件。 即使这样,我们仍在努力减轻这些事件的影响。 例如,Brog:
•如有必要,可在新的机器上自动重新安排已撤消的任务;
通过在不同的故障域(例如机器,机架和电源域)中分散作业的任务,减少相关的故障;
•限制允许的任务中断率和作业中可以在维护活动(例如OS或机器升级)中同时关闭的任务数量;
•使用声明性的期望状态表示和等效的等效更改操作,以便失败的客户端可以无害地重新提交任何被遗忘的请求;
•对无法访问的计算机寻找新位置的速率限制,因为它无法区分大规模计算机故障和网络分区;
•避免重复任务:导致任务或机器崩溃的机器配对;和
•通过反复重新运行logsaver任务(第2.4节)来恢复写入本地磁盘的关键中间数据,即使已将附加到它的分配终止或移动到另一台计算机也是如此。用户可以设置系统尝试运行多长时间;通常几天。
Borg的一项关键设计功能是,即使Borgmaster或任务的Borglet掉线了,已经在运行的任务也可以继续运行。但是保持主机正常运行仍然很重要,因为当主机关闭时,无法提交新作业或更新现有作业,并且无法重新计划故障机器中的任务。
Borgmaster使用多种技术组合,使其在实践中可实现99.99%的可用性:复制以解决机器故障;准入控制,避免超载;使用简单的低级工具来部署实例,以最小化外部依赖关系。每个单元彼此独立,以最大程度地减少相关的操作员错误和故障传播的机会。这些目标(而不是可伸缩性限制)是反对大型单元的主要论据。
图4:压实效果。 压缩后在15个单元格中获得的原始单元格大小百分比的CDF。5 使用率
Borg的主要目标之一是有效利用Google的机器群,这是一笔可观的财务投资:将利用率提高几个百分点可以节省数百万美元。 本节讨论和评估Borg用来执行此操作的一些策略和技术。
图5:将生产和非生产工作分离到不同的单元中,将需要更多的机器。 两个图均显示如果将prod和non-prod工作负载发送到单独的单元,则需要多少额外的机器,以在单个单元中运行工作负载所需的最小机器数的百分比表示。 在此以及后续的CDF图中,每个单元的显示值均来自于我们的实验所产生的不同单元大小的90%。 误差线显示了试验值的完整范围。5.1 评估方法
我们的工作受到放置限制,需要处理罕见的工作量高峰,我们的机器是异构的,并且我们在从服务工作中回收的资源中运行批处理工作。 因此,要评估我们的政策选择,我们需要一个比“平均利用率”更复杂的指标。 经过大量实验后,我们选择了单元压缩:给定工作负载后,我们发现可以通过卸下机器来装入一个小单元格,直到不再适合工作负载为止,然后从头开始重新打包工作负载以确保我们 没有挂在一个不幸的配置上。 这提供了干净的终止条件,并有助于自动进行比较,而不会产生合成工作负荷和建模的陷阱。 评估技术的定量比较可以在以下方面找到:细节令人惊讶地微妙。
不可能在实时生产单元上进行实验,但是我们使用Fauxmaster来获得高保真模拟结果,它使用来自实际生产单元和工作负载的数据,包括所有约束,实际限制,保留和使用情况 数据(第5.5节)。 该数据来自2014年10月1日(星期三)14:00 采取的博格检查站。 (其他检查点产生的结果相似。)我们首先去除专用,测试和小型(<5000台机器)的小单元,然后选择了15个Borg单元进行报告,然后对剩余的单元进行采样,以实现在大小。
图6:隔离用户将需要更多计算机。 如果用户大于所显示的阈值,则为五个不同的小区分配了他们自己的专用小区,并需要这些小区的总数和其他机器。为了在压缩单元中保持机器异质性,我们随机选择要移除的机器。为了保持工作负载的异构性,我们保留了所有内容,但与特定计算机(例如Borglets)相关的服务器和存储任务除外。对于大于原始单元格大小一半的工作,我们将硬约束更改为软约束,并且如果它们非常“挑剔”并且只能放置在少数机器上,则允许最多0.2%的任务挂起。广泛的实验表明,这种方法产生的结果具有低方差的可重复性。如果我们需要比原始细胞更大的细胞,可以在压实之前将原始单元克隆几次。如果我们需要更多的单元格,则只需克隆原始单元格即可。
对于具有不同随机数种子的每个细胞,将每个实验重复11次。在图形中,我们使用错误栏显示所需机器的最小值和最大值,然后选择90%ile值作为“结果” –平均值或中位数不会反映系统管理员所要执行的操作如果他们想要合理地确定工作量是否合适,管理员就会这样做。我们认为单元压缩提供了一种公平,一致的方式来比较调度策略,它直接转化为成本/收益结果:更好的策略需要更少的计算机来运行相同的工作负载。
我们的实验着重于从某个时间点安排(打包)工作负载,而不是重播长期工作负载跟踪。这部分是为了避免处理开放和关闭队列模型的困难[71,79],部分是因为传统的完成时间指标不适用于我们的环境,因为它的长期服务不完整,部分是为了提供清晰的信号之所以进行比较,部分是因为我们认为结果不会有明显的不同,部分是实际的问题:我们发现自己一次消耗了20万个Borg CPU内核用于我们的实验,即使在Google的规模上,这也是不平凡的投资。
在生产中,我们故意为工作量增长,偶尔的“黑天鹅”事件,负载峰值,机器故障,硬件升级以及大规模局部故障(例如电源母线槽)留有很大的余量。图4显示了如果我们对它们应用单元压缩,则实际单元将缩小多少。下图中的基线使用这些压缩的大小。
5.2 Cell sharing(单元共享)
我们几乎所有的机器都同时执行生产任务和非生产任务:共享的Borg单元中有98%的计算机,由Borg管理的整套计算机中有83%。 (我们有一些专用单元用于特殊用途。)
由于许多其他组织在单独的群集中运行面向用户和批处理的作业,因此我们研究了如果执行相同的操作会发生什么情况。图5显示,将生产工作和非生产工作分开需要在中位单元中增加20–30%的计算机来运行我们的工作负载。这是因为生产作业通常会保留资源以处理罕见的工作量高峰,但大多数时候不会使用这些资源。 Borg回收了未使用的资源(第5.5节)来运行许多非生产性工作,因此我们总体上需要的机器更少。
大多数Borg单元由成千上万的用户共享。图6显示了原因。在此测试中,如果用户消耗了至少10 TiB的内存(或100 TiB),我们会将用户的工作量划分为一个新的单元。我们现有的策略看起来不错:即使阈值较大,我们也需要2–16倍的单元格,以及20–150%的额外计算机。再一次,合并资源将大大降低成本。
但是也许将不相关的用户和作业类型打包到同一台计算机上会导致CPU干扰,因此我们需要更多的计算机来进行补偿吗? 为了评估这一点,我们研究了在以相同机器类型和相同时钟速度运行的不同环境中,任务的CPI(每条指令的周期数)如何变化。 在这些条件下,CPI值是可比较的,并且可以用作性能干扰的代理,因为CPI的两倍会使CPU绑定程序的运行时间加倍。 数据是在一周内从大约12000个随机选择的产品任务中收集的,使用[83]中所述的硬件配置基础结构在5分钟的间隔内对周期和指令进行计数,并对样本进行加权,以便计算每秒的CPU时间 一样。 结果不明确。
(1)我们发现CPI与同一时间间隔内的两次测量值呈正相关:计算机上的总体CPU使用率和(主要独立地)计算机上的任务数量;向计算机添加任务将其他任务的CPI提高0.3%(使用适合数据的线性模型);将机器CPU使用率提高10%,将CPI降低不到2%。但是,即使相关性具有统计学意义,它们也只能解释我们在CPI测量中看到的5%的变化;其他因素占主导地位,例如应用中的固有差异和特定的干扰模式[24、83]。
(2)将我们从共享单元中采样的CPI与少数应用较少的专用单元中的CPI进行比较,我们发现共享单元中的CPI平均为1.58(σ= 0.35),平均值为1.53(σ= 0.32)在专用单元中–即,共享单元中的CPU性能大约降低3%。
(3)为了解决不同小区中的应用程序可能具有不同的工作负载甚至遭受选择偏见(可能对干扰更敏感的程序已移至专用小区)的担忧,我们研究了运行Borglet的CPI。在两种类型的单元中的所有机器上。我们发现专用单元的CPI为1.20(σ= 0.29),共享单元的CPI为1.43(σ= 0.45),这表明它在专用单元中的运行速度是共享单元的1.19倍,尽管这种超重轻载机器的效果,将结果稍微偏向于专用单元。
这些实验证实,在仓库规模上进行性能比较是很棘手的,这增强了[51]中的观察,并且还表明共享并不会显着增加运行程序的成本。
但是即使假设我们的结果是最不利的,共享仍然是一个胜利:在几种不同的分区方案下,所需的计算机数量减少了,CPU的速度下降却无法弥补,共享优势也适用于包括内存在内的所有资源 和磁盘,而不仅仅是CPU。
5.3 Large cells(大单元)
Google建立大型单元,既可以运行大型计算,又可以减少资源碎片。 我们通过将一个单元的工作负载划分为多个较小的单元来测试后者的效果–首先随机排列作业,然后以循环方式在分区之间分配它们。 图7确认使用较小的单元将需要更多的计算机。
图7:将单元细分为较小的单元将需要更多的计算机。 如果我们将这些特定的单元划分为不同数量的较小单元,则需要额外的机器(占单元格的百分比)。5.4 Fine-grained resource requests(细粒度的资源请求)
Borg用户以毫核心为单位请求CPU,以字节为单位请求内存和磁盘空间。 (一个内核是处理器超线程,针对各种机器类型的性能进行了标准化。)图8显示了它们利用了这种粒度:请求的内存或CPU内核数量几乎没有明显的“最佳结合点”,并且它们之间几乎没有明显的关联 这些资源。 这些分布与[68]中介绍的分布非常相似,不同之处在于,在90%或更高的位置上,我们看到的存储请求稍大一些。
图8:没有集群尺寸适合大多数任务。 样本单元中请求的CPU和内存请求的CDF。 尽管几个整数CPU内核大小在某种程度上更受欢迎,但没有哪个值能脱颖而出。提供一组固定大小的容器或虚拟机,尽管在IaaS(基础架构即服务)提供商中很常见[7,33],但这并不能很好地满足我们的需求。 为了说明这一点,我们通过将求出作业和分配的值四舍五入到每个资源维度中的下一个最接近的幂次幂(“ 2.4”)来“破坏” CPU内核和内存资源的限制,从CPU的0.5个内核和RAM的1 GiB开始 。 图9显示,在中位数情况下,这样做将需要增加30%到50%的资源。 上限来自将整台计算机分配给在压缩开始之前将原始单元格增加四倍后无法满足的大型任务; 允许这些任务挂起的下限。 (这少于[37]中报告的大约100%的开销,因为我们支持4个以上的存储桶,并允许CPU和RAM的容量独立扩展。)
图9:“打包”资源需求将需要更多计算机。 CDF中的额外开销,这是由于将CPU和内存请求四舍五入到15个单元中的下一个最接近的2的幂而产生的。 上下限跨越实际值(请参见文本)。5.5 Resource reclamation(资源回收)
作业可以指定资源限制-每个任务应被授予的资源上限。 Borg使用该限制来确定用户是否有足够的配额来接受作业,并确定特定的计算机是否具有足够的可用资源来安排任务。就像有些用户购买的配额超出所需数量一样,有些用户请求的资源超出其任务使用的数量,因为Borg通常会终止一个任务,该任务尝试使用比其请求更多的RAM或磁盘空间,或者限制CPU使用它要求什么。另外,某些任务有时需要使用其所有资源(例如,在一天中的高峰时间或在应对拒绝服务攻击时),但大多数时候不需要。
我们不是浪费当前分配的资源,而不会浪费掉当前消耗的资源,而是估计任务将使用多少资源,并将剩余资源回收用于可以忍受质量较低的资源的工作,例如批处理作业。整个过程称为资源回收。估算值称为任务的预留量,它由Borgmaster每隔几秒钟使用Borglet捕获的细粒度使用情况(资源消耗)信息进行计算。初始预留被设置为等于资源请求(限制); 300秒后,为了允许启动瞬态,它会逐渐衰减到实际使用量并增加安全裕度。如果使用量超过预定量,则预定量会迅速增加。
Borg调度程序使用限制来计算生产任务的可行性(第3.2节),因此它们从不依赖回收的资源,也不会受到资源的过度订购;对于非生产性任务,它使用现有任务的保留,以便可以将新任务调度到回收的资源中。
如果预留(预测)有误,则计算机可能会在运行时用尽资源,即使所有任务使用的资源少于其限制。如果发生这种情况,我们将杀死或限制非生产性任务,而不是非生产性任务。
图10显示,如果没有资源回收,将需要更多的机器。大约20%的工作负载(第6.2节)在中位单元中的回收资源中运行。
我们可以在图11中看到更多详细信息,该图显示了预留量和使用量与限制的比率。如果需要资源,则无论其优先级如何,超过其内存限制的任务将首先被抢占,因此很少有任务超过其内存限制。另一方面,CPU很容易受到限制,因此短期峰值可能会无害地使使用率超出预留量。
图11表明资源回收可能不一定是保守的:在预留和使用线之间有很大的区域。为了对此进行测试,我们选择了一个实时生产单元,并通过降低安全裕度将其资源估算算法的参数调整为激进设置一周,然后将其设置为介于基线和激进设置之间的中间设置接下来的一周,然后恢复到基准。
图10:资源回收非常有效。 如果我们为15个代表性单元禁用它,则需要其他计算机的CDF。 图11:资源估计成功地识别了未使用的资源。 虚线显示了15个单元中CPU和内存使用率与任务请求(限制)之比的CDF。 大多数任务使用的内存远远少于其限制,尽管有些任务使用的CPU数量超过了请求的数量。 实线表示CDF,即CPU和内存预留的比率与限制之比。 这些接近100%。 直线是资源估计过程的工件。图12显示了发生的情况。 预订明显在第二周接近使用,而在第三周则有所减少,在基准周(第一和第四周)显示出最大的差距。 如预期的那样,在第2周和第3.5周,内存不足(OOM)事件的发生率略有增加。在审查了这些结果之后,我们认为净收益抵消了负面影响,并将中型资源回收参数部署到了其他单元中。
图12:更积极的资源估计可以回收更多资源,而对内存不足事件(OOM)的影响很小。 一个时间轴(从2013-11-11开始),用于一个生产单元的使用,预留和限制,平均在5分钟的窗口内累积累积的内存不足事件; 后者的斜率是OOM的总比率。 竖线以不同的资源估计设置分隔周。 图13:调度延迟与负载的关系。 根据计算机的繁忙程度,可运行线程多久必须等待1毫秒以上才能访问CPU的图。 在每对条形图中,延迟敏感的任务在左侧,批处理任务在右侧。 线程只需要等待百分之五的时间就可以等待5毫秒以上的时间来访问CPU(白条)。 他们几乎不必再等待更长的时间了(深色的条形)。 来自代表性单元格的2013年12月数据; 误差线显示每日变化。6 隔离(Isolation)
我们有50%的计算机运行9个或更多任务; 一台90%的计算机可执行约25个任务,并将运行约4500个线程。 尽管在应用程序之间共享计算机可以提高利用率,但是它还需要良好的机制来防止任务相互干扰。 这适用于安全性和性能。
6.1 安全隔离
我们使用Linux chroot监狱作为同一台计算机上多个任务之间的主要安全隔离机制。 为了允许进行远程调试,我们习惯于自动分发(和撤消)ssh密钥,以使用户仅在计算机正在为用户运行任务时才对其进行访问。 对于大多数用户而言,它已被Borg ssh命令所代替,该命令与Borglet协作以建立到与该任务在同一chroot和cgroup中运行的shell的ssh连接,从而更紧密地锁定访问。
VM和安全沙箱技术被Google的AppEngine(GAE)和Google Compute Engine(GCE)用于运行外部软件。 我们在作为Borg任务运行的KVM进程中运行每个托管的VM。
6.2 性能隔离
早期版本的Borglet具有相对原始的资源隔离实施:对内存,磁盘空间和CPU周期进行事后使用检查,结合使用过多内存或磁盘的任务的终止以及积极使用Linux的CPU优先级来控制任务那用了太多的CPU。但是,流氓任务仍然很容易影响计算机上其他任务的性能,因此一些用户夸大了他们的资源请求,以减少Borg可以与他们共同调度的任务数量,从而降低了利用率。由于涉及的安全边际,资源回收可以收回部分盈余,但不是全部。在最极端的情况下,用户请愿使用专用的机器或单元。
现在,所有Borg任务都在基于Linux cgroup的资源容器[17、58、62]中运行,并且Borglet操纵容器设置,由于OS内核处于循环状态,因此大大提高了控制能力。即使这样,偶尔也会发生低级资源干扰(例如,内存带宽或L3缓存污染),如[60,83]中所示。
为了帮助进行过载和超额使用,Borg任务具有一个应用程序类或appclass。最重要的区别是延迟敏感型(LS)应用程序类与其余类之间的区别,在本文中我们将其称为批处理。 LS任务用于需要快速响应请求的面向用户的应用程序和共享基础结构服务。高优先级LS任务得到最好的处理,并且能够一次使批处理任务饿死几秒钟。
第二个划分是基于速率的可压缩资源(例如,CPU周期,磁盘I / O带宽)之间的划分,可以通过降低其服务质量而不杀死它来将其从任务中回收;以及不可压缩的资源(例如内存,磁盘空间),这些资源通常在不终止任务的情况下无法收回。如果计算机用尽了不可压缩的资源,则Borglet会立即终止从最低优先级到最高优先级的任务,直到可以满足剩余的保留。如果计算机用尽了可压缩资源,则Borglet会限制使用(有利于LS任务),以便可以处理短暂的负载尖峰而不会杀死任何任务。如果情况没有改善,Borgmaster将从计算机中删除一项或多项任务。
Borglet中的用户空间控制循环根据预测的未来使用情况(针对生产任务)或内存压力(针对非生产任务)将容器分配给内存;处理来自内核的内存不足(OOM)事件;当他们尝试分配超出其内存限制的内存时,或者当过量使用的计算机实际上耗尽内存时,它们就会杀死这些任务。 Linux急切的文件缓存大大简化了实现过程,因为它需要精确的
内存会计。
为了改善性能隔离,LS任务可以保留整个物理CPU内核,这将阻止其他LS任务使用它们。批处理任务可以在任何内核上运行,但是相对于LS任务,它们具有很小的调度程序份额。 Borglet动态调整贪婪LS任务的资源上限,以确保它们不会在数分钟内使批处理任务饿死,并在需要时有选择地应用CFS带宽控制[75];份额不足,因为我们有多个优先级。
像Leverich [56]一样,我们发现标准的Linux CPU调度程序(CFS)需要大量调整才能支持低延迟和高利用率。为了减少调度延迟,我们的CFS版本使用了扩展的每组负载历史记录[16],允许LS任务抢占批处理任务,并在CPU上运行多个LS任务时减少了调度时间。幸运的是,我们的许多应用程序都使用了“每个请求线程”模型,从而减轻了
持续的负载失衡的影响。我们很少使用cpusets将CPU内核分配给延迟要求特别严格的应用程序。这些努力的一些结果如图13所示。在这一领域中,工作继续进行,增加了线程放置和支持NUMA,超线程和功耗感知的CPU管理(例如[81]),并提高了Borg的let控制保真度。
允许任务消耗最大资源。 允许它们中的大多数超出诸如CPU之类的可压缩资源的使用范围,以利用未使用的(松弛)资源。 大概只有5%的LS任务禁用了此功能,大概是为了获得更好的可预测性。 不到批处理任务的1%。 默认情况下,使用闲置内存是禁用的,因为它会增加任务被杀死的机会,但是即使如此,仍有10%的LS任务会覆盖它,而79%的批处理任务会这样做,这是因为这是默认设置 MapReduce框架。 这补充了回收资源的结果(第5.5节)。 批处理任务愿意利用未使用的以及回收的内存:在大多数情况下,这是可行的,尽管当LS任务急需资源时偶尔的批处理任务也会被牺牲掉。
7 相关工作
资源调度已经研究了数十年,涉及范围广泛,例如广域HPC超级计算网格,工作站网络和大型服务器集群。 在这里,我们仅关注与大型服务器集群有关的最相关的工作。
最近的几项研究分析了来自Yahoo!,Google和Facebook的集群跟踪[20、52、63、68、70、80、82],并说明了在这些现代数据中心和工作负载中规模和异构性所面临的挑战。 [69]包含集群管理器体系结构的分类法。
Apache Mesos [45]使用基于报价的机制在中央资源管理器(类似于Borgmaster减去其调度程序)和多个“框架”(例如Hadoop [41]和Spark [73])之间划分资源管理和放置功能。 Borg主要使用可扩展性很好的基于请求的机制来集中这些功能。 DRF [29、35、36、66]最初是为Mesos开发的; Borg改用优先级和入场配额。 Mesos开发人员已经宣布了雄心壮志,将Mesos扩展到包括投机性资源分配和收回,并解决[69]中确定的一些问题。
YARN [76]是一个以Hadoop为中心的集群管理器。每个应用程序都有一个管理器,该管理器与中央资源管理器协商所需资源。自从大约2008年以来,Google MapReduce作业就一直使用该方案来从博格(Borg)获取资源。YARN的资源管理器直到最近才变得容错。一项相关的开源工作是Hadoop Capacity Scheduler [42],它提供了多租户支持,并具有容量保证,分层队列,弹性共享和公平性。 YARN最近已扩展为支持多种资源类型,优先级,抢占和高级准入控制[21]。俄罗斯方块的研究原型[40]支持可识别makepan的作业打包。
Facebook的Tupperware [64]是一种类似于Borg的系统,用于在群集上调度cgroup容器;尽管似乎提供了一种资源回收的形式,但仅公开了一些细节。 Twitter已开源Aurora [5],这是一种类似于Borg的调度程序,用于在Mesos上运行的长期运行的服务,其配置语言和状态机与Borg相似。
微软的自动驾驶系统[48]提供了“自动软件的配置和部署;自动的软件配置和部署”。系统监控;并针对Microsoft群集执行修复措施以处理错误的软件和硬件”。博格(Borg)生态系统提供了类似的功能,但这里不进行讨论。 Isaard [48]概述了我们也遵循的许多最佳实践。
Quincy [49]使用网络流模型为数百个节点的群集上的数据处理DAG提供公平性和数据局部性感知调度。 Borg使用配额和优先级在用户之间共享资源,并扩展到成千上万台计算机。 Quincy直接在Borg之上构建执行图,而直接处理执行图。
Cosmos [44]专注于批处理,重点在于确保其用户能够公平地访问他们捐赠给集群的资源。它使用每个职位的经理来获取资源;公开的细节很少。
微软的Apollo系统[13]使用每个作业的调度程序来处理短暂的批处理作业,以在集群规模似乎与Borg单元相当的集群上实现高吞吐量。 Apollo利用机会优先执行较低优先级的后台工作,以(有时)数天的排队延迟为代价将利用率提高到较高水平。 Apollo节点提供了任务开始时间的预测矩阵,该矩阵是两个资源维度上大小的函数,调度程序将其与启动成本和远程数据访问的估计结合起来以做出放置决策,并通过随机延迟进行调制以减少冲突。 Borg使用一个中央调度程序来根据有关先前分配的状态来进行放置决策,可以处理更多的资源维度,并专注于高可用性,长期运行的应用程序的需求;阿波罗可能可以处理更高的任务到达率。
阿里巴巴的Fuxi [84]支持数据分析工作负载;它自2009年以来一直在运行。像Borgmaster一样,中央FuxiMaster(为容错而复制)从节点收集资源可用性信息,接受来自应用程序的请求,然后将它们相互匹配。 Fuxi增量调度策略是Borg等价类的反函数:Fuxi不会将每个任务匹配到一组合适的计算机上,而是将新可用的资源与待处理工作的积压进行匹配。与Mesos一样,Fuxi允许定义“虚拟资源”类型。只有合成工作负载结果可公开获得。
Omega [69]支持多个并行的,专门的“垂直”,每个垂直近似于一个Borgmaster减去其持久性存储和链接碎片。 Omega调度程序使用乐观并发控制来操作存储在中央持久性存储中的所需和观察到的单元状态的共享表示,并通过单独的链接组件与Borglets同步。 Omega架构的设计旨在支持多个不同的工作负载,这些工作负载具有自己的特定于应用程序的RPC接口,状态机和调度策略(例如,长时间运行的服务器,来自各种框架的批处理作业,基础设施服务(如集群存储系统) ,来自Google Cloud Platform的虚拟机)。另一方面,Borg提供了“千篇一律”的RPC接口,状态机语义和调度器策略,由于需要支持许多不同的工作负载,它们的大小和复杂性随着时间的推移而增长。尚未成为问题(第3.4节)。
Google的开源Kubernetes系统[53]将应用程序放置在Docker容器[28]中的多个主机节点上。它既可以在裸机(例如Borg)上运行,也可以在各种云托管提供商(例如Google Compute Engine)上运行。许多建造Borg的工程师都在积极开发它。 Google提供了一个称为Google Container Engine [39]的托管版本。在下一节中,我们将讨论如何将来自Borg的课程应用于Kubernetes。
高性能计算社区在这一领域具有悠久的工作传统(例如,毛伊岛,摩押市,LSF平台[2,47,50]);但是规模,工作量和容错性的要求与Google单元的要求不同。通常,此类系统通过处理大量未完成的工作积压(队列)来实现高利用率。
虚拟化提供程序(例如VMware [77])和数据中心解决方案提供程序(例如HP和IBM [46])提供的群集管理解决方案通常可扩展到O(1000)台计算机。另外,一些研究小组拥有原型系统,可以通过某些方式(例如[25、40、72、74])提高调度决策的质量。
最后,正如我们已经指出的那样,管理大型集群的另一个重要部分是自动化和“操作员扩展”。 [43]描述了如何计划故障,多租户,运行状况检查,准入控制和可重启性,以使每个操作员拥有大量机器。博格(Borg)的设计理念是相似的,它使我们能够为每个操作员(SRE)提供数万台机器。
8 经验教训和未来工作
在本节中,我们讲述了从十多年来在生产中使用Borg所学到的定性课程,并描述了如何在设计Kubernetes时利用这些观察结果[53]。
8.1 经验教训:不好
我们从用作警告故事的Borg的一些功能入手,并在Kubernetes中提供了明智的替代设计。
作业是唯一限制任务的分组机制。 Borg没有一流的方法来将整个多职位服务作为单个实体进行管理,也无法引用服务的相关实例(例如,金丝雀和生产轨道)。作为一种黑客,用户将其服务拓扑编码在作业名称中,并构建更高级别的管理工具来解析这些名称。另一方面,不可能引用作业的任意子集,这会导致诸如滚动更新和调整作业大小的语义不灵活等问题。
为了避免此类困难,Kubernetes拒绝任务指示,而是使用标签(用户可以将其附加到系统中的任何对象的标签/值对)来组织其调度单位(pod)。可以通过将job:jobname标签附加到一组pod来实现等效的Borg作业,但是也可以表示任何其他有用的分组,例如服务,层或发布类型(例如,生产,标记) ing,测试)。 Kubernetes中的操作通过标签查询来标识其目标,标签查询选择操作应应用于的对象。这种方法比作业的单个固定分组具有更大的灵活性。
每台机器一个IP地址会使事情变得复杂。在Borg中,计算机上的所有任务都使用其主机的单个IP地址,从而共享主机的端口空间。这带来了许多困难:Borg必须安排端口作为资源;任务必须预先声明它们需要多少个端口,并愿意在启动时被告知要使用哪些端口;
Borglet必须强制执行端口隔离;并且命名和RPC系统必须处理端口以及IP地址。
由于Linux名称空间,VM,IPv6和软件定义的网络的出现,Kubernetes可以采用一种更加用户友好的方法来消除这些复杂性:每个pod和服务都有其自己的IP地址,从而允许开发人员选择端口而不是选择端口。要求其软件适应基础架构选择的软件,并消除管理端口的基础架构复杂性。
针对高级用户进行优化,以牺牲临时用户为代价。 Borg提供了一系列针对“高级用户”的功能,因此他们可以微调程序的运行方式(BCL规范列出了约230个参数):最初的重点是支持Google的最大资源消费者谁的效率提高是最重要的。不幸的是,该API的丰富性使“休闲”用户的工作变得更加困难,并限制了它的发展。我们的解决方案是构建在Borg之上运行的自动化工具和服务,并根据实验确定适当的设置。这些功能得益于容错应用程序提供的自由试验:如果自动化出错,那就是麻烦,而不是灾难。
8.2 经验教训:有益
另一方面,博格(Borg)的许多设计功能非常有益,并且经受了时间的考验。
分配是有用的。 Borg分配抽象产生了广泛使用的logaver模式(第2.4节),而另一个流行的模式是简单的数据加载器任务定期更新Web服务器使用的数据。 分配和程序包允许单独的团队开发此类帮助服务。 Kubernetes相当于alloc的是pod,pod是一个或多个容器的资源包络,这些容器总是被调度到同一台机器上并且可以共享资源。 Kubernetes在同一个pod中使用辅助容器而不是在alloc中使用任务,但是想法是相同的。
集群管理不仅仅是任务管理。尽管Borg的主要角色是管理任务和机器的生命周期,但是在Borg上运行的应用程序还可以从许多其他群集服务中受益,包括命名和负载平衡。 Kubernetes使用服务抽象支持命名和负载平衡:服务具有名称和由标签选择器定义的动态Pod集。群集中的任何容器都可以使用服务名称连接到服务。在幕后,Kubernetes会自动在与标签选择器匹配的Pod之间对与服务的连接进行负载平衡,并跟踪Pod在哪里运行,因为它们由于故障而随着时间重新安排。
内省至关重要。尽管博格几乎总是“行之有效”,但是当出现问题时,找到根本原因可能是具有挑战性的。在Borg中,一个重要的设计决定是向所有用户公开调试信息,而不是将其隐藏:Borg有成千上万的用户,因此“自助”必须是调试的第一步。尽管这使我们更难弃用功能并更改用户依赖的内部政策,但这仍然是一个胜利,而且我们已经
找不到现实的选择。为了处理大量数据,我们提供了多个级别的UI和调试工具,因此用户可以快速识别与其工作相关的异常事件,然后从其应用程序和基础结构本身中深入查看详细的事件和错误日志。
Kubernetes旨在复制Borg的许多自省技术。例如,它附带了诸如cAdvisor [15]之类的工具,用于资源监视以及基于Elasticsearch / Kibana [30]和Fluentd [32]的日志聚合。可以查询主对象以获取其对象状态的快照。 Kubernetes具有统一的机制,所有组件都可以使用该机制来记录可供客户端使用的事件(例如,计划中的Pod,容器发生故障)。
主机是分布式系统的内核。 Borgmaster最初是作为一个整体系统设计的,但是随着时间的流逝,它逐渐成为位于服务生态系统核心的内核,这些生态系统可以合作管理用户的工作。例如,我们将调度程序和主UI(Sigma)分离为单独的流程,并添加了用于准入控制,垂直和水平自动缩放,重新打包任务,定期作业提交(cron),工作流管理和归档的服务离线查询的系统操作。总之,这些使我们能够在不牺牲性能或可维护性的情况下扩大工作量和功能集。
Kubernetes架构更进一步:它的核心是一个API服务器,该API服务器仅负责处理请求和处理底层状态对象。集群管理逻辑构建为小型,可组合的微服务,这些微服务是此API服务器的客户端,例如复制控制器,该控制器在出现故障时可维护所需数量的Pod副本,以及节点控制器,它管理机器的生命周期。
8.3 结论
在过去的十年中,几乎所有Google的群集工作负载都已转换为使用Borg。 我们将继续发展它,并将从中汲取的经验教训应用到Kubernetes。
致谢
本文的作者进行了评估并撰写了这篇论文,但是数十名设计,实施和维护Borg组件和生态系统的工程师是其成功的关键。 我们在此仅列出最直接参与Borgmaster和Borglets的设计,实现和操作的人员。 如果我们错过了任何人,我们深表歉意。
最初的Borgmaster由Jeremy Dion和Mark Vandevoorde以及Ben Smith,Ken Ashcraft,Maricia Scott,Iu Ming-Yee和Monika Henzinger共同设计和实施。 最初的Borglet主要由Paul Menage设计和实施。
随后的贡献者包括。。。。
博格SRE团队也发挥了至关重要的作用,并受到称赞。 Borg配置语言(BCL)和borgcfg工具最初是由。
我们感谢我们的审稿人和引路人Chris tos Kozyrakis对本文的反馈。
网友评论