BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

LinkedIn 开源其分布式对象存储系统 Ambry

| 作者 Sriram Subramanian 关注 0 他的粉丝 ,译者 张卫滨 关注  他的粉丝 发布于 2016年6月2日. 估计阅读时间: 20 分钟 | ArchSummit北京2018 共同探讨机器学习、信息安全、微服务治理的关键点

日前,LinkedIn在Github上基于Apache 2许可证协议开源了其分布式对象存储系统Ambry。Ambry是一个是不可变对象的存储系统,非常易于扩展,它能够存储KB到GB大小的不可变对象,并且能够实现高吞吐和低延迟,该系统支持跨数据中心的双活部署,并且存储成本低廉。它特别适于存储各种媒体内容。

据Linkedin的前工程主管Sriram Subramanian介绍,媒体内容在Web中已经无处不在,Linkedin中的每项新特性基本上都会与某种类型的媒体内容进行交互。这些媒体内容会存储在后端,并且主要会由内容分发网络(Content Delivery Networks,CDN)来提供服务,后台存储系统会作为CDN的原始服务器(origin server)。

随着Linkedin流量的不断增长,原来所使用的媒体内容存储方案在可扩展性、可用性以及运维方面所遇到的问题越来越多。两年前,他们着手解决这些问题,而Ambry正是该项工作的结果。

2013年时的媒体存储是什么样的?

LinkedIn之前的系统被称为媒体服务器(因为没有一个像样的名字),这个系统由两部分组成,分别是用于媒体文件存储的Filer以及存储元数据的大型Oracle数据库。这些系统的前端是一些运行在SOLARIS上的无状态机器,它们会将请求路由到对应的Filer或数据库上。Filer是通过NFS的方式mount到无状态机器上的,并使用Java的File API进行远程访问。前端会与数据中心(DC)里面的一组缓存进行交互,从而保证如果下游系统(Filer/Oracle)出现性能问题或不可用时,前端不会受其影响。

随着LinkedIn对媒体内容的需求不断增加,原有的系统在面临这些需求时,遇到了如下严重的问题:

  • 频繁出现的可用性问题:每次对文件的元数据操作出现峰值时,原有的系统都会出现延迟。当访问大量的小文件时,对元数据的操作就会增多。每次文件操作都要经过多级的转换(Java、NFS以及Filer),使其很难进行调试;
  • 难以扩展:用来存储数据和元数据的底层系统都是单体的。水平扩展元数据的存储是不可能实现的,为数据存储增加硬件也需要很多的手动过程;
  • 对小对象和大对象的支持效率低下:媒体数据集中包含了数万亿的小对象(50KB-1MB)也包括数亿的大对象(1MB-1GB)。对于小对象的存储来说,元数据操作的代价是很高昂的,而对于大数据,原有的系统缺乏端到端的流支持,难以支持新产品的使用场景;
  • 平均修复时间(MTTR,Mean Time To Repair)指标很差:老系统中的大多数组成部分在很大程度上都是黑盒,这需要获得支持许可证,并且要通过电话的方式来描述和解决问题,这会影响到MTTR;
  • 成本高昂:旧的媒体存储成本很高,再继续扩展的话,成本上已经吃不消了。如果想管理媒体的扩展性,就不能延续该方案了。

在这个过程中,Linkedin探索过多种替代方案,最终还是决定自行实现更匹配其需求的解决方案。

Ambry是如何运行呢?

设计目标

在了解Ambry的设计和内部运行原理之前,明确其设计目标是很有帮助的,这决定了它的实现方式。

  • 高可用性和水平可扩展:该系统要处理实时流量,会直接影响到站点的可用性,因此它必须具有很高的可用性。另外,还希望新系统能够尽可能地实现无缝的集群扩展;
  • 降低运维的负担:分布式系统一般都会难以管理,对于频繁的集群操作,能够实现自动化是非常重要的,这能避免系统成为运维的一种负担。复杂的系统通常很难实现自动化并可靠的运行,因此新系统的设计要简单、优雅并自动化;
  • 更低的MTTR:分布式系统出现故障是难以避免的,但是很重要的一点在于快速修复故障,让各个子组件启动并运行。这就需要系统的设计简单,并且不会出现单点故障;
  • 跨DC双活:Linkedin有多个数据中心,因此所有的系统都要支持双活配置,这样的话,系统能够更新不同数据中心中的同一个对象;
  • 提升小对象和大对象的效率:请求是由小对象和大对象所组成的,小对象通常是1K到100K,超出这个范围的对象会位于大对象桶中(bucket)。要同时处理好各种大小的对象,通常来讲是很困难的。大量的小对象会给元数据带来很高的负载,造成硬盘碎片,需要很多的随机IO,而大对象则需要很好的内存管理、端到端的流处理和有限的资源使用;
  • 廉价:媒体内容很快就会占据很大的存储空间,它的另外一个特点是旧数据会变成“冷”数据,并不会频繁访问。针对这种情况有很多优化技术,包括使用密集的硬件(denser hardware)、分层存储、擦除编码以及数据去重等。在设计时,Ambry希望媒体内容能够高效存储在密集型的机器上,并且能够非常容易地使用其他优化成本的方案。

概览

总体上来讲,Ambry由三部分组成,分别是用来存储和检索数据的一组数据节点,路由请求的前端机器(请求会在一些预处理之后路由到数据节点上)以及协调和维护集群的集群管理器。数据节点会在不同节点之间复制数据,同时支持数据中心内部和数据中间之间的复制。前端提供了支持对象PUT、GET和DELETE操作的HTTP API。另外,前端所使用的路由库也可以直接用在客户端中,从而实现更好的性能。在LinkedIn,这些前端节点会作为CDN的原始服务器。

API

Ambry提供了REST API,它们适用于大多数的场景。在有些场景下,需要更好的性能,如果是这样的话,Ambry也支持在客户端使用路由库,直接针对数据节点的流字节进行读取和写入。目前,路由库是阻塞的(同步),不过Ambry目前正在致力于实现非阻塞(异步)版本,同时也会提供对路由库的多语言支持。

Clustermap

Clustermap控制拓扑结构、维护状态并帮助协调集群的操作。Clustermap有两部分组成:

  • 硬件布局:包含了机器的列表、每台机器上的磁盘以及每个磁盘的容量。布局还维护资源的状态(机器和磁盘)并指定主机名和端口,通过主机名和端口就能连接到数据节点;
  • 分区布局:包含了分区的列表、它们的位置信息以及状态。在Ambry中,分区有一个数字表示的ID,副本的列表可以跨数据中心。分区是固定大小的资源,集群间的数据重平衡都是在分区级别进行的。

数据节点和前端服务器都能够访问clustermap,并且会始终使用它们当前的视图来做出决策,这些决策涉及到选择可用的机器、过滤副本以及识别对象的位置等。

存储

存储节点会用来存放不同分区的副本。每个存储节点会有N块磁盘,副本会跨磁盘分布存储。这些副本的结构和管理都是相同的。

在存储方面,Ambry涵盖的功能包括如下几个方面:

  • 持久化:磁盘上的每个副本均被建模为预先分配的log(preallocated log)。所有新的消息都会按照顺序附加到log上,消息是由实际的对象块(chunk)和相关的元数据(系统和用户)所组成的。这能够使写入操作实现很高的吞吐量,并且避免出现磁盘碎片。Ambry会使用索引将对象id与log中的消息映射起来,索引本身是一组排序的文件片段,条目按照最新使用在前,最旧的条目在后的顺序,从而便于高效查找。索引中的每个条目都维护了log中消息的偏移量、消息的属性以及一些内部使用的域。索引中的每个片段会维护一个bloom filter,从而优化实际磁盘操作所耗费的时间;
  • 零拷贝:通过使用sendfile API,在进行读取时,字节从log转移到网络的过程中实现了零拷贝。通过避免额外的系统调用,实现了更好的性能,在这个过程中,会确保字节不会读入到用户内存中,不必进行缓存池的管理;
  • 恢复:因为系统和机器会出现宕机,磁盘上的数据也有可能会损坏,所以有必要实现恢复(recovery)的功能。在启动的时候,存储层会从最后一个已知的检查点读取log,并重建索引。恢复也有助于重建内存中的状态。Log是恢复的来源,并且会永久保存;
  • 复制:存储节点还需要维护分区中各副本的同步。每个节点上都会有一个复制服务(replication service),它会负责保证本地存储中的副本与所有的远程副本是同步的。在这里,进行了很多的优化,以保证复制过程的高效可靠。

路由/前端

前端服务器提供了HTTP接口,供客户端与之通信。除此之外,它们还会负责为CDN设置正确的头信息、进行安全校验,并将对象以流的形式返回给路由库和客户端。

路由所负责的功能如下所示:

  • 请求管理:请求的端到端生命周期是由路由来进行管理的。路由会处理PUT、GET以及DELETE请求。对于其中的每个请求类型,路由都会跟踪副本成功和失败的数量从而确定Quorum的值、维护分块的状态、生成对象id并在成功或失败的时候触发对应的回调;
  • 分块:大对象会分解为块(chunk),每个块都能够跨分区独立地进行路由。每个块都会有一个id来进行唯一标识。路由会生成一个元数据对象,其中包含了块的列表以及它们所需的获取顺序。元数据对象存储为独立的blob,它的id也会作为blob的id。在读取的时候,会得到元数据对象,然后检索各个块并返回给客户端;
  • 故障检测:故障检测的逻辑要负责主动识别宕机或状态出问题的资源。资源可以是机器、磁盘或分区。路由会将出现问题的资源标记为不可用,这样后续的请求就不会使用它们了;
  • Quorum:Ambry为写入和读取实现了一种多主人(multi-master)的策略。这能够实现更高的可用性并减少端到端的延迟,这是通过减少一个额外的hop来实现的,在基于主从结构(master slave)的系统中,往往会有这个额外的hop。请求通常会发往M个副本,然后等待至少N个成功的响应(这里N<=M)。路由会优先使用本地数据中心的副本,向其发送请求,如果本地存储无法实现所需的Quorum的话,它会代理远程数据中心的访问;
  • 变更捕获:在每次成功的PUT或DELETE操作之后,路由会生成一个变更捕获(change capture)。变更捕获中所包含的信息是blob id以及blob相关的元数据,这个信息可以被下游的应用所使用。

在路由中,典型的PUT和GET操作的流程分别如下所示,系统的实际运行过程会比下述的描述会更复杂一些:

  • PUT操作:客户端会将对象以及一些元数据信息以流的形式发送到前端,当流到达时,前端会将对象进行分块、选择可用的分区、为blob或分块生成blob id,并将请求分发给W个副本。然后,前端就开始等待至少Q个成功的响应(Q<=W),等到之后,会将blob id返回给客户端。如果无法达到足够的Quorum,那么前端会报告一个错误。当然,Ambry也实现了当Quorum失败的时候,选择另外一个分区的功能,从而提升可用性。
  • GET操作:客户端通过将id发送给前端来请求某一个blob。前端会根据id来确定分区,并在数据节点中检索blob相关的块。对于每个块,前端会并行发送R个请求,在将blob或分块发送给客户端之前,前端会等待Q个成功响应(Q<=R)。

解决运维的难题

分布式系统最困难的在于它的运维,在这个过程中,需要工具、度量并且要进行广泛地测试,从而保证所有的事情都能符合预期。在这个过程中,Ambry积累了很多的工具和实践。

  • Simoorg:为了模拟各种故障,他们孵化并开源了Simoorg。这是一个分布式的故障引入系统,能够在集群中引入各种故障,如GC暂停、磁盘错误、节点宕机以及网络故障,从而校验在各种情况下,系统的正确性,有助于预先发现并修正严重的缺陷;
  • 生产环境的正确性测试:当新版本部署到集群中的部分机器进行验证时,可以进行正确性测试,从而保证生产环境的健康状态。通过加压访问所有可用的API(组合使用各种可能出现的输入参数),确保结果的正确性;
  • 审计:当新的blob写入到磁盘时,都会产生复制事件,这个事件包含了blob的信息以及事件的来源。所有存储节点的事件都会聚集到Hadoop中,这样就能审计是否所有的副本都进行了写入。目前该系统并不是实时的,不过,Ambry规划会构建一个实时的审计系统。

除此之外,Ambry还实现了指标和告警工具,用来帮助识别系统中的异常行为以及维护集群的管理工具。

迁移工作是如何进行的?

团队需要将所有的媒体内容从遗留系统迁移至Ambry,在这个过程中还要服务于所有的流量,不能出现任何的宕机时间。除此之外,团队还面临着多项deadline,了解Ambry是如何组织研发和部署的,对我们会有一定的指导意义:

  1. 当时,公司正在将所有的服务从Spring RPC方案中剥离出来。从构建Ambry开始计算,团队有四个月的时间支持新的API并移除Spring RPC;
  2. 一个新的数据中心正好需要搭建,Ambry团队并不想再去部署遗留系统了,因为这会带来很高的成本。这同时也就意味着为了避免部署遗留系统,需要在八个月内完成Ambry;
  3. 最后,团队希望在数据中心里面移除掉Solaris的方案,遗留系统是运行在Solaris上的,它的deadline是一年。

Ambry团队采用了一种特殊的方式来达到这些里程碑节点。首先,构建前端并使用它来代理所有对旧系统的请求,然后再将所有的客户端迁移到新的前端上。这虽然费了很大的功夫,但是确保了第一个deadline目标的达成。

第二步是让Ambry能够以端到端的方式运行,并且只将其部署到新的数据中心上,然后将所有的数据从旧系统迁移至Ambry。在代码中,添加了一定的逻辑,确保如果新数据中心发生故障的话,将会使用旧的系统。这可能会产生更多的延迟,但是团队决定承担这个风险。

在新数据中心搭建完成之后,在接下来的几个月里,团队不断运行并稳定Ambry。基于测试和审计结果,当对新系统完全自信的时候,团队决定停掉遗留的系统。这样就在一年的deadline之内完成了目标。

在接下来的一年中,Ambry成为了Linkedin中媒体内容的唯一来源。它的成功要归因于周密的规划以及渐进式的基础设施研发。

Ambry是如何适应Linkedin的生态系统的?

媒体基础设施是针对媒体内容的端到端管道,涉及到上传、存储、处理、元数据管理以及内容下载。在基础设施中,Ambry是很重要的一个环节,基础的稳固是非常重要的。在Ambry就绪之后,就可以围绕着它进行扩展并关注生态系统中的其他组成部分。

下一步的研发计划

目前,Ambry主要进行中的任务包括在前端和路由层实现非阻塞、存储节点实现机架感知等功能。团队希望不断地为Ambry添加新的特性,并且构建活跃的开源社区。完整的未来工作计划列表可以参见Github上的相关页面,如下列出了当前正在进行的或者可能会开展的项目:

  • 非阻塞:阻塞类型的请求通常会占用一个进程,直到请求结束并且不支持管道。为了达到更高的吞吐量,并且避免大对象所造成的资源枯竭现象,需要将路由和前端变成完全非阻塞的。这样的话,就能支持更大吞吐量,在操作执行时,不再受限于线程资源,从而能够实现更好的可用性。目前,前端实现已经完成,正在进行测试。路由库的代码预计也将会很快完成,可以参考其repository来了解最近的更新情况;
  • 机架感知:现代的数据中心为了降低成本都将机架切换视为很重要的因素。这意味着软件要足够智能来应对切换故障。Ambry正在构建的一项功能就是确保新分区的副本在跨数据中心存放时,能够遵循机架感知的方式。目前,该项工作正在进行之中
  • Bucket/Container:Ambry尚不支持命名空间的概念。如果要在群组的级别上强化控制的话,那命名空间是非常有用的。在Ambry中,可能将会引入Bucket或Container的理念。这有助于在bucket级别上定义用户群组、访问控制和配额,这种方式会比在对象级别进行维护容易得多;
  • 安全:Ambry目前支持数据节点之间的加密,在前端和数据节点之前的通信也可以启用加密功能。不过,在安全方面,Ambry会有更多的进展,包括REST级别的认证、授权以及对加密的支持。该项功能预计会在Bucket/Container实现完成之后开展。

对于社区来讲,这个系统会有很大的用处,有助于支持媒体内容的实时上传和媒体服务的构建,详细的文档可以参见Github上的相关页面。如果读者有反馈意见或有志于为该项目做出贡献的话,可以参考其开发指南。Ambry团队希望该项目的开发能够保持开放的状态,并帮助社区使用它来构建应用程序。另外值得一提的是,2016年度的SIGMOD(Special Interest Group on Management Of Data)已经接受了一篇关于Ambry的论文,该会议将会在六月份举行,可以浏览其网站了解更多信息。

查看英文原文Introducing and Open Sourcing Ambry


感谢李建盛对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT