大规模失败 面对快速变化的可靠性

申明:本文转载自https://queue.acm.org/detail.cfm?id=2839461,由谷歌翻译而翻译。

故障是任何大型系统工程的一部分。Facebook的文化价值观之一就是拥抱失败。可以从门洛公园总部墙壁上张贴的海报中看出:“如果不害怕,您会怎么做?” 和“财富青睐大胆”。

为了在快速变化的情况下保持Facebook的可靠性,我们研究了常见的故障模式并建立了解决方案。这些抽象确保最佳实践可应用于整个基础架构。为了指导我们建立可靠性抽象的工作,我们必须了解我们的失败。为此,我们构建了诊断问题的工具,并建立了一种对事件进行审查的文化,这种文化促使我们进行改进以防止将来发生故障。

为什么会发生故障?

尽管每个失败都有一个独特的故事,但许多失败归结为少数根本原因。

个别机器故障

通常,一台单独的机器会遇到孤立的故障,而不会影响其余的基础架构。例如,一台计算机的硬盘驱动器发生故障,或者一台特定计算机上的服务遇到了代码错误,例如内存损坏或死锁。

避免单机故障的关键是自动化。通过将已知的故障模式(例如带有SMART错误的硬盘驱动器)与搜索未知问题的症状相结合(例如,换出响应时间非常慢的服务器),自动化可以发挥最佳效果。当自动化发现未知问题的症状时,手动调查可以帮助开发更好的工具来检测和修复未来的问题。

合理的工作量变更

有时,Facebook用户更改行为会给我们的基础架构带来挑战。例如,在重大世界事件中,独特类型的工作负载可能会以不同寻常的方式给我们的基础架构带来压力。当巴拉克·奥巴马(Barack Obama)赢得2008年美国总统大选时,他在Facebook上的页面活动达到了创纪录的水平。在诸如超级碗或世界杯之类的重大体育赛事中,高潮会导致大量职位。负载测试,包括“暗启动”功能已激活但用户看不见的负载测试,有助于确保新功能能够处理负载。

在此类事件期间收集的统计信息通常可以提供有关系统设计的独特视角。通常,重大事件会导致用户行为发生变化(例如,通过围绕特定对象创建有针对性的活动)。有关这些更改的数据通常指向设计决策,这些决策将允许在后续事件中更流畅地运行。

人为错误

考虑到Facebook鼓励工程师“快速移动并打破事物”(装饰办公室的另一张海报),人们可能会认为许多错误是人为造成的。我们的数据表明人为错误是我们失败的一个因素。图1包括来自对事件时序的分析的数据,这些时序严重到足以被视为违反SLA(服务级别协议)的程度。每次违反都表示一个实例,其中我们的内部可靠性目标未达到并导致警报生成。由于我们的目标严格,因此大多数此类事件都是次要事件,对于网站用户而言并不明显。图1a显示,即使整个星期内网站的流量保持一致,事件在周六和周日发生的情况也大大减少。

这两个数据点似乎表明,当Facebook员工因为忙于其他事情(周末,节假日甚至绩效审查)而没有积极地对基础架构进行更改时,该站点将具有更高的可靠性。我们认为,这不是做出更改的人粗心大意的结果,而是证明我们的基础设施在面对非人为的错误原因(例如机器故障)时基本上可以自我修复。

引发事件的三种简单方法

尽管故障具有不同的根本原因,但我们发现了三种常见的病因,它们会放大故障并使它们变得更加普遍。对于每种病理,我们都制定了预防措施以减轻广泛的失败。

快速部署的配置更改

配置系统往往旨在在全球范围内快速复制更改。快速的配置更改是一个功能强大的工具,可使工程师快速管理新产品的发布或调整设置。但是,快速的配置更改意味着在部署错误的配置时会快速失败。我们采取了许多措施来防止配置更改导致失败。

• 让每个人都使用通用的配置系统。

使用通用配置系统可确保过程和工具适用于所有类型的配置。在Facebook上,我们发现团队有时倾向于一次性处理配置。避免这些诱惑并以统一的方式管理配置,使配置系统成为使站点更可靠的一种杠杆方式。

• 静态验证配置更改。许多配置系统都允许使用松散类型的配置,例如JSON结构。这些类型的配置使工程师可以很容易地输错字段名称,在需要整数的地方使用字符串或犯其他简单错误。这些类型的直接错误最好使用静态验证来捕获。结构化格式(例如,在Facebook上我们使用Thrift)4可以提供最基本的验证。但是,编写程序验证来验证更详细的需求并不是没有道理的。

• 运行金丝雀。首先将配置部署到服务的一小部分范围内可以防止更改带来灾难性的后果。金丝雀可以采用多种形式。最明显的是A / B测试,例如仅对1%的用户启动新配置。可以同时运行多个A / B测试,并且您可以随时间使用数据来跟踪指标。

但是,出于可靠性目的,A / B测试不能满足我们的所有需求。部署到少量用户但会导致相关服务器崩溃或内存不足的更改显然会产生超出测试中受限用户范围的影响。A / B测试也很耗时。工程师通常希望在不使用A / B测试的情况下进行较小的更改。因此,Facebook基础架构会自动在一小组服务器上测试新配置。例如,如果我们希望将新的A / B测试部署到1%的用户,我们将首先将测试部署到命中少量服务器的1%的用户。我们会在短时间内监视这些服务器,以确保它们不会崩溃或出现其他高度可见的问题。此机制提供了基本的“健全性检查”

• 保持良好的配置。Facebook的配置系统旨在在更新那些配置时面对故障时保留良好的配置。开发人员自然倾向于创建配置系统,当他们收到无效的更新配置时,这些系统将崩溃。我们更喜欢在这些情况下保留旧配置的系统,并向系统操作员发出有关配置更新失败的警报。使用陈旧配置运行通常比将错误返回给用户更可取。

• 使其易于还原。有时,尽管尽了最大的努力,还是部署了错误的配置。快速找到并还原更改是解决此类问题的关键。我们的配置系统具有版本控制功能,可以轻松还原更改。

对核心服务的硬依赖性

开发人员倾向于假设核心服务(例如配置管理,服务发现或存储系统)永远不会失败。但是,即使这些核心服务发生短暂故障,也可能导致大规模事件。

• 缓存来自核心服务的数据。通常不需要对这些类型的服务进行严格的依赖。这些服务返回的数据可以通过某种方式进行缓存,该方式允许大多数服务在这些系统之一短暂中断期间继续运行。

• 提供强化的API以使用核心服务。核心服务最好由使用这些核心服务时遵循最佳实践的通用库来补充。例如,库可能提供用于管理缓存或良好的故障处理的良好API。

•进行消防演习。您可能会认为您能够在核心服务中断后幸免于难,但是直到尝试时您才知道。对于这些类型的中断,我们必须开发用于消防演习的系统,范围从将故障注入应用于单个服务器到手动触发整个数据中心的中断。

延迟增加和资源耗尽

某些故障会导致服务对客户端的延迟增加。延迟的增加可能很小(例如,考虑到人为配置错误,该错误会导致CPU使用率增加,但仍在服务的能力范围内),或者可能几乎是无限的(服务响应的线程已死锁的服务)。尽管Facebook的基础架构可以轻松处理少量额外的延迟,但是大量的延迟会导致级联故障。几乎所有服务都对未完成请求的数量有限制。此限制可能是由于每个请求线程服务中的线程数量有限,也可能是由于基于事件的服务中的内存有限。如果服务遇到大量额外的延迟,则调用该服务的服务将耗尽其资源。

资源耗尽是一种特别有害的失败模式,因为它允许请求子集使用的服务失败,从而导致所有请求失败。例如,假设某个服务调用了仅对1%的用户启动的新实验性服务。通常,对该实验服务的请求将花费1毫秒,但是由于新服务的故障,请求将花费1秒。使用此新服务的1%用户的请求可能会消耗大量线程,以至于其他99%用户的请求无法运行。

我们发现了许多可以避免这种类型的错误率低的技术。

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

onNewRequest(req, queue):

  if (queue.lastEmptyTime() < (now - N seconds)) {
     timeout = M ms
  } else {
     timeout = N seconds;
  }
  queue.enqueue(req, timeout)

在此算法中,如果队列在最近的N毫秒中没有清空,那么在队列中花费的时间将限制为M毫秒。如果服务能够在最近的N毫秒内清空队列,则花费在队列中的时间将限制为N毫秒。此算法可防止排队等候(因为lastEmptyTime遗嘱将在遥远的过去,从而导致M-ms排队超时),同时出于可靠性目的允许短暂的排队突发。尽管具有如此短超时的请求似乎违反直觉,但此过程允许在系统无法跟上传入请求的速率时迅速丢弃请求,而不是建立请求。短暂的超时可确保服务器始终只接受比其实际处理量多一点的工作,因此它永远不会闲置。

该算法的一个吸引人的特性是的值MN往往不需要调整。解决排队问题的其他方法,例如设置队列中项目的数量限制或设置队列超时,都需要基于每个服务进行调整。我们发现,M的值为5毫秒,N的值为100 ms往往可以在广泛的用例中很好地工作。Facebook的开源Wangle库5提供了此算法的实现,供我们的Thrift 4框架使用。

• 自适应后进先出(后进先出)。大多数服务以FIFO(先进先出)顺序处理队列。但是,在排队的时间段内,先进先出的请求通常闲坐了很长时间,以至于用户可能中止了生成请求的动作。处理先入先出的请求首先会消耗与刚到达的请求相比不太可能使用户受益的请求上的资源。我们的服务使用自适应LIFO处理请求。在正常操作条件下,请求以FIFO顺序处理,但是当队列开始形成时,服务器将切换到LIFO模式。自适应LIFO和CoDel可以很好地协同工作,如图2所示。CoDel设置了较短的超时时间,防止了建立较长的队列,而自适应LIFO将新请求置于队列的最前面,从而最大程度地提高了它们满足由以下条件设置的期限的机会CoDel。虚拟机如图3所示,Facebook的PHP运行时包括Adaptive LIFO算法的实现。

大规模失败:LIFO(左)和带有CoDel的自适应LIFO(右)

• 并发控制。CoDel和自适应LIFO都在服务器端运行。服务器通常是实施延迟预防措施的最佳场所-服务器倾向于为大量客户端提供服务,并且通常具有比其客户端拥有的信息更多的信息。但是,有些故障非常严重,以至于服务器端控件无法启动。因此,我们在客户端中实施了权宜之计。每个客户端都基于每个服务跟踪未完成的出站请求的数量。当发送新请求时,如果对该服务的未完成请求数超过可配置的数目,则该请求将立即标记为错误。此机制可防止单个服务垄断其所有客户端资源。

帮助诊断故障的工具

尽管采取了最佳的预防措施,某些故障仍然会发生。在中断期间,正确的工具可以迅速导致根本原因,从而最大程度地减少故障持续时间。

带有立体派的高密度仪表板

处理事件时,快速访问信息很重要。好的仪表板使工程师能够快速评估可能异常的指标类型,然后使用此信息来推测根本原因。但是,我们发现仪表盘变得如此庞大,以至于很难快速浏览它们,并且这些仪表盘上显示的图表有太多行,一目了然,如图3所示。

为了解决这个问题,我们使用Cubism构建了我们的顶级仪表板,2这是一个用于创建地平线图的框架-折线图使用颜色更密集地编码信息,从而可以轻松比较多个相似的数据系列。例如,我们使用立体主义比较不同数据中心之间的指标。我们围绕立体主义的工具可简化键盘导航,因此工程师可以快速查看多个指标。图4使用面积图和地平线图显示了在不同高度下的相同数据集。在面积图版本中,很难读取30像素的版本。另一方面,即使在高度为30像素时,水平图也使查找峰值非常容易。

刚刚改变了什么?

由于人为错误是导致失败的最主要原因之一,因此调试失败的最有效方法之一就是寻找人为最近发生的变化。我们使用称为OpsStream的工具收集有关最近更改的信息,从配置更改到新软件的部署。但是,随着时间的流逝,我们发现该数据源变得异常嘈杂。在成千上万的工程师进行更改的情况下,事件发生时经常需要评估太多。

为了解决此问题,我们的工具尝试将故障与相关更改关联起来。例如,当引发异常时,除了输出堆栈跟踪信息外,我们还输出该请求读取的任何配置设置,这些配置设置的值最近都已更改。通常,导致生成许多堆栈跟踪的问题的原因是这些配置值之一。然后,我们可以快速解决问题,例如,通过恢复配置并让进行更改的工程师参与。

从失败中学习

发生故障后,我们的事件审查过程可帮助我们从这些事件中学习。

事件审查过程的目的不是要怪罪。没有人被解雇,因为他或她造成的事件受到了审查。审查的目的是了解发生的情况,纠正允许事件发生的情况,并建立安全机制以减少未来事件的影响。

审查事件的方法

Facebook开发了一种称为DERP(用于检测,升级,补救和预防)的方法,以协助进行有效率的事件审查。

• 检测。如何检测到问题-警报,仪表板,用户报告?

• 升级。合适的人迅速介入了吗?难道这些人是通过警报而不是手动引入的?

• 补救措施。采取了哪些步骤解决此问题?这些步骤可以自动化吗?

• 预防。有哪些改进可以消除此类故障再次发生的风险?您如何才能优雅地失败,或者更快地失败以减少此失败的影响?

DERP帮助分析当前事件的每个步骤。借助这种分析,即使您无法阻止再次发生此类事件,也至少可以在下一次更快地恢复。

通过减少更少的事情来快速移动

“快速”的心态不必与可靠性背道而驰。为了使这些哲学兼容,

Facebook的基础设施提供安全阀:我们的配置系统可防止不良配置的快速部署;我们的核心服务为客户提供强化的API,以防止出现故障;并且我们的核心库可以防止资源在面对延迟时耗尽。为了解决不可避免的问题,我们构建了易于使用的仪表板和工具,以帮助查找可能导致正在调查的问题的最新更改。最重要的是,在发生事件之后,我们将汲取的经验教训使我们的基础架构更加可靠。

参考文献

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

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

3. HipHop虚拟机(HHVM);https://github.com/facebook/hhvm/blob/43c20856239cedf842b2560fd768038f52b501db/hphp/util/job-queue.h#L75

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

5.王乐图书馆;https://github.com/facebook/wangle/blob/master/wangle/concurrent/Codel.cpp

Ben Maurer是Facebook Web Foundation团队的技术负责人,负责Facebook面向用户产品的整体性能和可靠性。Ben于2010年以基础架构团队成员的身份加入Facebook。在Facebook之前,他与Luis von Ahn共同创立了reCAPTCHA。最近,本与美国数字服务局合作,改善了联邦政府内部技术的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值