acm-header
登录

ACM通信

实践

失败的规模


失败在规模,说明性照片

来源:Shutterstock.com

回到顶部

失败是任何大型系统工程的一部分。Facebook的文化价值观之一就是拥抱失败。这一点可以从我们门洛帕克总部墙上挂着的海报上看出来:“如果你无所畏惧,你会做什么?”和“命运偏爱勇敢者”。

为了保持Facebook在面对快速变化时的可靠性,我们研究了失败中的常见模式,并构建了解决它们的抽象方法。这些抽象确保了最佳实践应用于我们的整个基础设施。为了指导我们构建可靠性抽象的工作,我们必须理解我们的失败。为此,我们建立了诊断问题的工具,并建立了一种回顾事件的文化,促使我们做出改进,以防止未来的故障。

虽然每一次失败都有一个独特的故事,但许多失败都可以归结为少数几个根本原因。

个人机故障.通常,一台单独的机器会遇到孤立的故障,而这不会影响到基础设施的其余部分。例如,可能一台机器的硬盘驱动器失败了,或者某个特定机器上的服务在代码中出现了错误,比如内存损坏或死锁。

避免个别机器故障的关键是自动化。自动化的最佳工作方式是将已知的故障模式(例如有s.m.a.r.t错误的硬盘驱动器)与搜索未知问题的症状(例如,更换响应时间异常缓慢的服务器)结合起来。当自动化发现未知问题的症状时,手动调查可以帮助开发更好的工具来检测和修复未来的问题。

合法的工作负载的变化.有时Facebook用户的行为改变会给我们的基础设施带来挑战。例如,在世界重大事件期间,特定类型的工作负载可能会以不同寻常的方式给我们的基础设施带来压力。当奥巴马赢得2008年美国总统大选时,他的Facebook主页的活跃度创下了历史新高。在像超级碗或世界杯这样的大型体育赛事中,高潮的发挥会产生非常多的位置。负载测试,包括“暗启动”(功能被激活但对用户不可见),有助于确保新功能能够处理负载。

在这些事件中收集的统计数据通常为系统设计提供了一个独特的视角。通常,重大事件会导致用户行为的改变(例如,围绕特定对象创建集中活动)。关于这些变化的数据通常指向设计决策,允许在后续事件中更平稳地操作。

人为错误.鉴于Facebook鼓励工程师“快速行动,打破陈情”,这是办公室里的另一个装饰海报,人们可能会认为许多错误是由人类造成的。我们的数据表明,人为错误是我们失败的一个因素。图1包括来自对严重到足以被视为违反服务水平协议(SLA)的事件时间的分析的数据。每一个违规都表明我们的内部可靠性目标没有达到,并导致产生警报。因为我们的目标是严格的,大多数这些事件都是轻微的,不会被网站的用户注意到。图1a显示,尽管网站的流量在一周内保持不变,但周六和周日发生的事件大幅减少。图1b显示了六个月的时间里,只有两周没有发生事故:圣诞节的那周和员工被要求写同事评议的那周。

这两个数据点似乎表明,当Facebook的员工因为忙于其他事情(周末、假期或甚至绩效评估)而不积极地对基础设施进行更改时,网站的可靠性水平会更高。我们相信,这不是做出改变的人的疏忽造成的结果,而是证明我们的基础设施在面对机器故障等非人为原因的错误时,在很大程度上能够自我修复。

回到顶部

三种容易引起事故的方法

虽然失败有不同的根本原因,但我们发现了三种放大失败并导致它们广泛传播的常见病理。针对每种病理,我们都制定了预防措施,以减轻广泛的失败。

快速部署的配置更改.配置系统往往被设计成在全局范围内快速复制更改。快速配置更改是一个强大的工具,可以让工程师快速管理新产品的发布或调整设置。但是,当部署了错误的配置时,快速的配置更改意味着快速的失败。我们使用许多实践来防止配置更改导致失败。

  • 让每个人都使用一个通用的配置系统。使用公共配置系统可以确保过程和工具适用于所有类型的配置。在Facebook,我们发现团队有时倾向于一次性处理配置。避免这些诱惑并以统一的方式管理配置,使得配置系统成为使站点更加可靠的一种有效方式。
  • 静态验证配置更改。许多配置系统允许松散类型的配置,例如JSON结构。这些类型的配置使工程师很容易键入错误的字段名,在需要整数的地方使用字符串,或犯其他简单的错误。这些简单的错误最好使用静态验证来捕获。结构化格式(例如,在Facebook我们使用Thrift)4可以提供最基本的验证。然而,编写程序验证来验证更详细的需求并不是不合理的。
  • 一只金丝雀。首先,将您的配置部署到服务的小范围内可以防止灾难性的更改。金丝雀可以有多种形态。最明显的是A/B测试,例如只向1%的用户启动新配置。多个A/B测试可以并发地运行,并且您可以随着时间的推移使用数据跟踪指标。

然而,为了可靠性的目的,A/B测试并不能满足我们所有的需求。部署到少量用户的更改,但导致相关服务器崩溃或耗尽内存,显然会产生超出测试中有限用户的影响。A/B测试也很耗时。工程师通常希望在不使用A/B测试的情况下推出较小的更改。由于这个原因,Facebook基础设施会在一小组服务器上自动测试新配置。例如,如果我们希望向1%的用户部署一个新的a /B测试,我们将首先将测试部署到1%的访问少量服务器的用户。我们会在短时间内监视这些服务器,以确保它们不会崩溃或出现其他明显的问题。该机制对所有更改提供基本的“健全检查”,以确保它们不会导致广泛的失败。

  • 保持良好的配置。Facebook的配置系统被设计成在更新配置失败时保持良好的配置。开发人员自然倾向于创建当他们收到无效的更新配置时将崩溃的配置系统。在这种情况下,我们更喜欢保留旧配置的系统,并向系统操作员发出配置更新失败的警报。使用陈旧的配置运行通常比向用户返回错误更可取。
  • 使它易于恢复。有时,尽管尽了最大努力,还是部署了一个糟糕的配置。快速发现并恢复更改是解决这类问题的关键。我们的配置系统是由版本控制支持的,因此很容易恢复更改。

对核心服务的硬依赖.开发人员倾向于假设核心服务,如配置管理、服务发现或存储系统永远不会失败。然而,即使是这些核心服务中的短暂故障也可能演变成大规模事件。

  • 从核心服务缓存数据。对这些服务类型的硬依赖通常是不必要的。这些服务返回的数据可以以一种允许大多数服务在其中一个系统短暂中断期间继续运行的方式进行缓存。
  • 提供加固的api来使用核心服务。使用这些核心服务时遵循最佳实践的公共库是对核心服务的最佳补充。例如,库可能为管理缓存或良好的故障处理提供良好的api。
  • 消防演习。您可能认为您能够在核心服务中断时幸存下来,但在您尝试之前,您永远不会知道。对于这些类型的停机,我们必须开发用于消防演练的系统,从应用于单个服务器的故障注入到手动触发整个数据中心的停机。

延迟增加,资源耗尽.有些失败会导致服务对客户端的延迟增加。延迟的增加可能很小(例如,考虑人为配置错误导致CPU使用量增加,但仍然在服务的能力范围内),也可能几乎是无限的(服务响应的线程已经死锁)。虽然少量的额外延迟可以由Facebook的基础设施轻松处理,但大量的延迟会导致级联故障。几乎所有的服务都有未完成请求数量的限制。这种限制可能是由于每个请求线程的服务中的线程数量有限,也可能是由于基于事件的服务中的内存有限。如果一个服务经历了大量的额外延迟,那么调用它的服务将耗尽它们的资源。这种故障可以通过多个服务层传播,导致广泛的故障。


为了指导我们构建可靠性抽象的工作,我们必须理解我们的失败。为此,我们建立了诊断问题的工具,并建立了一种回顾事件的文化,促使我们做出改进,以防止未来的故障。


资源耗尽是一种破坏性特别大的失败模式,因为它允许请求子集使用的服务的失败导致所有请求的失败。例如,假设一个服务调用了一个新的实验性服务,该服务只向1%的用户启动。通常情况下,对这个实验服务的请求需要1毫秒,但由于新服务的故障,请求需要1秒。使用此新服务的1%用户的请求可能会消耗太多线程,导致其他99%用户的请求无法运行。

我们已经发现了一些技术,可以避免这种类型的积累,以较低的假阳性率。

  • 控制延迟。在分析过去涉及延迟的事件时,我们发现许多最糟糕的事件都涉及大量的请求在队列中等待处理。有问题的服务有资源限制(如活动线程或内存的数量),并且会缓冲请求,以便将使用量保持在限制以下。由于服务无法跟上传入请求的速率,队列将变得越来越大,直到达到应用程序定义的限制。为了解决这种情况,我们希望在正常操作期间限制队列的大小而不影响可靠性。我们研究了bufferbloat的研究,因为我们的问题看起来很相似,都需要排队以获得可靠性,而不会在拥塞期间造成过多的延迟。我们用CoDel的一个变体做了实验1(延迟)控制算法:

ins01.gif

在此算法中,如果队列最后没有为空N毫秒,那么在队列中花费的时间将被限制为毫秒。如果服务已经能够在最后一个N毫秒,那么在队列中花费的时间被限制为N毫秒。该算法防止了站立队列(因为lastEmptyTime会不会在遥远的过去,引起一场-ms queuing timeout),同时出于可靠性考虑,允许短时间的排队。虽然具有如此短的超时时间的请求似乎违反直觉,但当系统无法跟上传入请求的速度时,此过程允许快速丢弃请求,而不是增加请求。较短的超时确保服务器总是接受比它实际能够处理的稍微多一点的工作,因此它永远不会闲置。

的值是该算法的一个吸引人的特性而且N往往不需要调整。解决排队问题的其他方法,如设置队列中项目数量的限制或设置队列超时,都需要针对每个服务进行调优。我们找到了一个5毫秒的值和100毫秒N倾向于在广泛的用例集上很好地工作。Facebook的开源Wangle库5提供了这个算法的实现,它被我们的Thrift4框架所使用。

  • 适应性后进先出(后进先出)。大多数服务以先进先出(FIFO)的顺序处理队列。然而,在高排队期间,第一个进入的请求经常被搁置了很长时间,以至于用户可能已经中止了生成请求的操作。首先处理first-in请求会将资源消耗在一个比刚刚到达的请求更不利于用户的请求上。我们的服务使用自适应后进先出处理请求。在正常运行条件下,请求按照FIFO顺序处理,但当队列开始形成时,服务器切换到后进先出模式。Adaptive LIFO和CoDel很好地发挥在一起,如图所示图2.CoDel设置了短超时,防止长队列的形成,自适应后进先出将新请求放在队列的前面,最大限度地提高它们满足CoDel设置的最后期限的机会。HHVM,3.Facebook的PHP运行时,包括一个自适应后进先出算法的实现。
  • 并发控制。CoDel和自适应后进先出都在服务器端操作。服务器通常是实现延迟预防措施的最佳场所,因为服务器往往服务大量客户机,并且通常拥有比客户机所拥有的更多的信息。然而,有些故障非常严重,以至于服务器端控制无法启动。为此,我们对客户实施了权宜之计。每个客户机在每个服务的基础上跟踪未完成出站请求的数量。当发送新请求时,如果对该服务的未完成请求数量超过了可配置的数量,则该请求立即被标记为错误。这种机制可以防止单个服务独占其客户机的所有资源。

回到顶部

帮助诊断故障的工具

尽管采取了最好的预防措施,还是会发生一些故障。在停机期间,正确的工具可以快速地找到根本原因,将故障持续时间缩短到最小。

高密度的仪表盘与立体主义.在处理事件时,快速获取信息是很重要的。好的仪表板允许工程师快速评估可能异常的度量类型,然后使用该信息假设根本原因。然而,我们发现我们的仪表板变得如此之大,以至于很难快速浏览它们,而且仪表板上显示的图表有太多的行,很难一眼就看清楚。

为了解决这个问题,我们用立体主义构建了顶层仪表盘,2一种用于创建地平线图和折线图的框架,该框架使用颜色对信息进行更密集的编码,便于对多个相似的数据序列进行比较。例如,我们使用立体主义来比较不同数据中心之间的指标。我们围绕立体派的工具允许简单的键盘导航,所以工程师可以快速查看多个指标。图3使用区域图和水平图在不同高度显示同一数据集。在面积图版本中,30像素的版本很难阅读。另一方面,地平线图使得找到峰值非常容易,即使在30像素的高度。

只是改变了什么?由于失败的主要原因之一是人为错误,调试失败最有效的方法之一是查找人类最近发生了什么变化。我们通过一个名为OpsStream的工具收集从配置更改到新软件部署的最新变化信息。然而,我们发现随着时间的推移,这个数据源变得非常嘈杂。由于成千上万的工程师在进行更改,在一次事故中往往有太多的更改需要评估。

为了解决这个问题,我们的工具试图将失败与相关的更改关联起来。例如,当抛出异常时,除了输出堆栈跟踪外,我们还输出由该请求读取的任何配置设置,这些设置的值最近发生了更改。通常,产生许多堆栈跟踪的问题的原因是这些配置值之一。然后我们可以快速地对问题做出响应,例如,通过恢复配置并让做出更改的工程师参与进来。

回到顶部

从失败中学习

失败发生后,我们的事件回顾过程帮助我们从这些事件中学习。

事件回顾过程的目的不是推卸责任。没有人因为他或她引起的事件受到审查而被解雇。审查的目的是了解发生了什么,补救导致事件发生的情况,并建立安全机制以减少未来事件的影响。

一种回顾事件的方法.Facebook开发了一种名为DERP(用于检测、升级、补救和预防)的方法,以帮助有效的事件审查。

  • 检测。手臂、仪表盘、用户报告是如何检测到问题的?
  • 升级。合适的人很快就参与进来了吗?这些人是通过警报而不是人工引入的吗?
  • 补救。采取了什么措施来解决这个问题?这些步骤可以自动化吗?
  • 预防。哪些改进可以消除此类故障再次发生的风险?你怎样才能优雅地失败,或者更快地失败以减少这次失败的影响?

DERP帮助分析手边事件的每一步。在此分析的帮助下,即使您不能防止这种类型的事件再次发生,您至少可以在下次更快地恢复。

回到顶部

打破更少的东西,快速前进

“快速行动”的心态不一定与可靠性相悖。为了使这些理念兼容,Facebook的基础设施提供了安全阀:我们的配置系统可以防止糟糕配置的快速部署;我们的核心服务为客户提供强化的api,以防止出现故障;我们的核心库在面对延迟时防止资源耗尽。为了处理遗漏的不可避免的问题,我们构建了易于使用的仪表板和工具,以帮助找到可能导致正在调查的问题的最近更改。最重要的是,在事故发生后,我们利用吸取的教训使我们的基础设施更加可靠。

ACM队列的q戳相关文章
queue.acm.org

自动化软件故障报告
布伦丹·墨菲
http://queue.acm.org/detail.cfm?id=1036498

规模失败
乔治Neville-Neil
http://queue.acm.org/detail.cfm?id=2147781

在互联网上提高性能
汤姆•雷顿
http://queue.acm.org/detail.cfm?id=1466449

回到顶部

参考文献

1.CoDel(可控延时)算法;http://queue.acm.org/detail.cfm?id=2209336

2.立体主义;https://square.github.io/cubism/

3.HipHop虚拟机(HHVM);http://bit.ly/1Qw68bz

4.节俭框架;https://github.com/facebook/fbthrift

5.骗取库;https://github.com/facebook/wangle/blob/master/wangle/concurrent/Codel.cpp

回到顶部

作者

本·毛雷尔是Facebook网络基金会团队的技术主管,负责Facebook面向用户产品的整体性能和可靠性。在Facebook之前,他与路易斯·冯·安共同创立了reCAPTCHA。

回到顶部

数据

F1图1。SLA侵犯事件。

F2图2。后进先出(左)和带代码的自适应后进先出。

F3图3。面积图(左)和水平图(右)。

回到顶部


版权归作者所有。授权ACM出版权利。

数字图书馆是由计算机协会出版的。版权所有©2015 ACM, Inc.


没有发现记录

Baidu
map