导语:Twine是Facebook的IaaS层,可以说绝大部分的Facebook服务器都运行在这个系统下面。本篇文章介绍了Facebook使用Twine进行高效,可靠的大规模集群管理的实践经验。
作者:Kenny Yu,Chunjiang(CQ)Tang
译者:海容川
在今天的Systems@scale会议上,我们介绍了我们的集群管理系统Twine,它可以在数百万台服务器上编排容器,来运行我们几乎所有的服务。自2011年我们首次部署Twine以来,我们的基础设施已经从一个数据中心扩展成一个由十五个地理分布数据中心组成的机群。在同一时期内,Twine已显著发展,以跟上我们的成长速度。我们将分享Twine在几个方面推动集群管理的最新进展,包括对有状态服务的无缝支持,跨数据中心的单一控制平台,以及在服务之间实时转移容量的能力。我们还将共享目前系统发展时所学到的一些经验与教训。
Twine服务于多个利益相关者。应用程序开发人员使用Twine来部署和管理应用程序。它将应用程序代码和依赖项打包到镜像中,并将其作为容器部署到服务器上。容器实现了多个应用程序运行在同一服务器上的的隔离,允许开发人员专注于应用程序逻辑,而不必担心获取服务器的方式或编排其应用程序的升级。此外,Twine监视服务器的健康状况,并在检测到故障时将容器从受影响的服务器上移走。
容量工程师使用Twine根据预算限制和业务优先级在各个团队之间实施服务器容量分配。他们还利用Twine来帮助提高服务器利用率。数据中心操作员使用Twine在我们的数据中心中合理地分散容器,并在维护事件期间停止或移动容器。这有助于数据中心操作员以最少的人为干预执行服务器,网络和设施维护。
Twine结构
Twine结构 PRN是我们的数据中心区域之一。
一个区域由彼此相邻的多个数据中心建筑物(PRN1和PRN2)组成。我们正在向“一区一控制”发展 ,可以控制其领域中的所有服务器应用程序开发人员将服务部署为Twine作业,其中一个作业由多个容器组成,通常所有容器都运行相同的应用二进制文件。Twine负责容器分配和容器生命周期管理。它由以下组成部分组成:
Twine前端为UI,CLI和其他自动化工具提供API,以便与Twine交互。它向作业所有者隐藏了Twine的内部细节。
Twine调度器是负责作业和容器生命周期管理的控制平台。它部署在区域与全局范围内,其中区域调度器管理来自同一区域的服务器,全局调度器管理来自多个区域的服务器。调度器是分片的,每个分片管理其范围内的工作。
Twine调度器代理隐藏了调度器分片的内部细节,它为Twine用户提供了单个控制平台的抽象性和可用性。
Twine分配器负责将容器分配给服务器。调度器负责容器启动,停止,更新和故障转移的编排。目前,一个分配器的可伸缩性足以处理整个区域而无需分片。(请注意与其他系统在术语上的一些差异。例如,Twine调度器映射到Kubernetes控制平台,而Twine分配器映射到Kubernetes调度器。)
资源代理(Resource Broker)储存了SOT服务器信息和维修事件。我们在每个数据中心运行一个资源代理,它存储关于数据中心中所有服务器的信息。资源代理和一个称为 resource allowance系统的容量管理系统一起工作,动态地决定哪个调度器部署管理哪些服务器。健康检查服务监视服务器并将其健康信息存储在资源代理中。如果服务器不健康或需要维护,资源代理会通知分配器和调度器停止或将容器移动到其他服务器。
Twine代理是运行在每台服务器上的daemon,它负责设置和拆除容器。应用程序在容器内运行,以提供更好的隔离和可重复性。我们在去年的Systems@scale事件中描述了如何使用镜像,btrf,cgroupv2和systemd来设置单独的Twine容器。
Twine的特征
虽然Twine与其他集群管理系统(如Kubernetes和Mesos)有许多共同的特性,但它在以下方面有其独特之处:
这些领先的特性,是随着对全球共享机群中运行的无状态和有状态应用程序的需求产生的。
对有状态服务的无缝支持
Twine运行着许多关键的状态服务,为Facebook,Instagram,Messenger和WhatsApp等产品存储持久数据。示例包括大型key值存储(如ZippyDB)和监视数据存储(如ODS Gorilla和Scuba)。支持有状态服务是具有挑战性的,因为系统需要确保容器部署能够应对大规模故障(包括网络分区和断电)。虽然常见的实践(例如将容器分散到故障域中)对于无状态服务很有效,但有状态服务需要额外的支持。
例如,如果服务器故障导致数据库的一个副本不可用,我们是否应该继续允许对10,000台服务器中的50台服务器执行内核升级的自动化维护呢?这个得看情况。如果那50台服务器中的一台碰巧托管了同一数据库的另一个副本,那么最好等待,不要同时丢失两个副本。控制维护和系统健康状况的动态决策要求有每个有状态服务器的内部数据复制和放置逻辑的可见性
一个名为TaskControl的接口允许有状态服务参与可能影响数据可用性的决策。调度器利用接口将容器生命周期通知给外部应用程序,如重新启动,更新,迁移和维护事件。有状态服务执行一个控制器,该控制器在安全执行每个操作时指导Twine,可能根据需要重新排序或临时延迟操作。在上面的示例中,Databases控制器可以告诉Twine升级50台服务器中的49台,但暂时保留一台特定的服务器(X)。最终,如果内核升级已过截止日期,而数据库仍然无法恢复失败的副本,Twine将继续升级服务器X。
许多运行在Twine上的有状态服务通过ShardManager间接使用TaskControl,ShardManager是一个在Facebook广泛使用的用于构建有状态服务的编程框架。Twine允许开发者指定容器在我们的数据中心传播的意图;ShardManager允许开发人员指定数据分片跨容器传播的意图。ShardManager知道其应用程序的数据放置和复制,它通过TaskControl接口与Twine一起工作,在没有应用程序直接参与的情况下规划容器生命周期的操作。这种集成极大地简化了有状态服务管理,但那并不是TaskControl全部功能。例如,我们的大型web层是无状态的,并使用TaskControl来动态调整容器之间的更新速度。因此,web层可以在不影响可用性的情况下每天快速执行多个软件版本。
跨数据中心管理服务器
当Twine在2011年首次创建时,每个服务器集群都由一个单独的,专用的调度器管理。当时Facebook的一个集群是一组服务器机架连接到一个共同的网络交换机,一个数据中心托管多个集群。调度程序只能管理单个集群内的服务器,这意味着作业不能跨集群。随着我们基础设施的发展,集群退役变得越来越频繁。由于Twine无法透明地将作业从即将退役的集群迁移到其他集群,因此退役需要大量的人工工作以及应用程序开发人员和数据中心操作员之间的仔细协调。这一过程通常会造成大量资源浪费,因为服务器在退役过程中会闲置数月。
我们引入了资源代理来解决这个集群停用问题,并协调所有其他类型的维护事件。资源代理跟踪与服务器关联的所有物理信息,并动态决定由哪个调度程序来管理每个服务器。服务器到调度器的动态绑定为调度器跨数据中心管理服务器提供了灵活性。由于Twine作业不再局限于单个集群,Twine用户可以表明他们的意图,即容器应该如何跨故障域传播。例如,开发人员可以表明他的意图(例如,在PRN区域中跨两个故障域运行我的作业),而无需指定要使用的特定可用性区域。Twine将负责寻找适当的服务器来满足这一意图,即使在集群退役或维护操作的情况下也是如此。
扩展以支持大型全球共享机群
历史上,我们的基础设施被划分为数百个专用服务器池,这些服务器池由各个团队拥有。碎片化和缺乏标准化导致了很高的操作开销,并使空闲服务器更难重用。在去年Systems@scale,我们宣布了我们的基础设施即服务( Infrastructure-as-a-Service),将我们的基础设施整合成一个大型的全球共享服务器群。但是,这种共享机群带来了新的挑战,同时也带来了相互竞争的需求:
我们引入了调度程序分片来解决支持大型共享池的挑战。每个调度程序分片管理区域中的作业子集,这允许我们降低与每个部署相关联的风险。随着共享池大小的增长,我们可以根据需要添加更多的调度器碎片以支持增长。
Twine用户将调度器碎片和代理视为单个控制平台,而不必与编排其作业的众多调度器碎片交互。请注意,调度器碎片与我们的老一代集群调度程序有着根本的不同,因为前者对控制平面进行了碎片化,而不是按网络拓扑静态地对共享服务器池进行碎片化。
利用弹性计算提高利用率
随着我们的基础设施的发展,实现服务器机群的高利用率变得越来越重要,这样我们就可以优化基础设施成本并减少操作负载。提高服务器利用率有两种主要方法:
弹性计算方法是在非高峰时间缩减在线服务,并将释放出来的服务器提供给离线工作负载,如机器学习和MapReduce作业。
资源过度处理方法是将在线服务和批处理工作负载堆叠在相同的服务器上,并以较低的优先级运行批处理工作负载。
在我们的数据中心中,电源是一个有限资源。因此,我们更喜欢使用能够提供更多计算能力的小型节能服务器。但是偏爱CPU和内存较少的小型服务器的的副作用是,资源过度投入的效果较差。虽然我们可以在单个服务器上堆叠多个具有低CPU和内存需求的小型服务容器,但当堆叠在节能的小型服务器上时,大型服务的性能并不理想。因此,我们鼓励大型服务的开发人员大力优化他们的服务,以利用整个服务器。
我们主要通过弹性计算来实现高利用率。我们的许多大型服务,如新闻提要,消息传递和前端web层,呈现出明显的全天模式,在非高峰时段,它们的利用率显著下降。我们有意降低这些在线系统的规模,以便在非高峰时间在更少的服务器上运行,并将释放的服务器提供给离线工作负载,如机器学习和MapReduce作业。
我们已经了解到,提供整个服务器作为弹性容量的单位效果最好,因为大型服务既是弹性容量的最大贡献者,也是最大的消费者,并且为利用整个服务器进行了大量优化。当服务器在非高峰时间从联机服务中释放出来时,资源代理会将服务器借给调度器以运行脱机工作负载。如果联机服务遇到负载峰值,Resource Broker会迅速撤销借出的服务器,并与排定程序协作将服务器返回联机服务。
经验教训和今后的工作
在过去的八年里,我们开发了Twine,以跟上Facebooks快速发展的步伐。我们正在分享我们所学到的一些经验教训,希望这些经验教训能对其他运营快速增长的基础设施的国家有所帮助:
我们还在执行一个大型全球共享机群的早期阶段。目前,我们大约20%的服务器都在共享池中。要达到100%,我们有很多挑战需要解决,包括为存储系统创建共享池支持,自动化维护,添加多租户需求控制,提高服务器利用率,以及增强对机器学习工作负载的支持。我们期待着解决这些问题,分享我们的进展。