做者介绍:Ben Maurer是Facebook的网络基础团队的技术领先者,主要负责整个Facebook面向用户产品的性能和可靠性。Ben于2010年正式加入Facebook,基础设施团队的成员。在加入Facebook以前,他与Luis von Ahn共同创立的验证码。最近,本与美国数字服务公司合做,以改进在联邦政府的技术使用。算法
数人云今天为你们带来一篇Ben Maurer分享的“Facebook面对大规模系统工程故障排查实践”,因为内容较多,因此数人云今天只为你们带来上半部分,后续内容会在明天发布!api
故障是任何大规模工程系统的一部分。Facebook的文化价值之一就是拥抱失败。这能够从挂在门洛帕克总部墙上的海报上获得体现:“若是你无所畏惧,你会怎样?”“天佑勇者。”缓存
为了使Facebook的系统在快速变化的状况下保持可靠,专门为其研究了常见的故障模式,并创建抽象理念来解决这些问题。这些理念确保最佳实践应用于的整个基础设施。经过创建工具来诊断问题,并建立一种复盘事故的文化来推进并做出改进,防止将来发生故障。服务器
虽然每个故障都有一个独特的故事,可是多数故障均可以归结为少数的缘由。网络
单个机器一般会遇到一个孤立的故障,不会影响基础设施的其他部分。例如,可能一台机器的硬盘驱动器发生了故障,或者某台机器上的服务遇到了代码中的错误,内存损坏或等。并发
避免单个机器故障的关键是自动化,自动化工做最好结合已知的故障模式(如硬盘驱动器的S.M.A.R.T.错误)与未知问题的搜索(例如,经过交换服务器异常缓慢的响应时间)。当自动化发现一个未知问题,手工调查能够帮助开发更好的工具来检测和修复问题。框架
遇到突发情况,Facebook会改变平常的行为习惯,为基础设施带来挑战。例如,在重要的全球事件中,独特的工做负载可能会以不寻常的方式来考验其中的基础设施。当奥巴马赢得2008美国总统大选时,Facebook页面活跃度刷新了记录。如超级杯或者世界杯这样重大的体育赛事也会引起其发帖数量大大增长。负载测试,包括“灰度发布”即有新功能发布,可是对于使用者不可见,有助于确保新功能可以处理负载。工具
在这些事件中收集的统计数据经常为系统的设计提供一个独特的视角。一般状况下,重大事件致使用户行为的变化(例如,经过围绕一个特定的对象建立主题活动)。有关这些更改的数据一般指向设计决策,以便在后续事件中容许更平滑的操做。性能
鉴于Facebook鼓励工程师“快速行动,打破常规”-如同装饰办公室的另外一个海报所示,也许有人会认为,不少错误都是人为形成的。根据数据代表,人为失误是失败的一个因素。图1涵盖了严重到足以被认为违反了SLA(服务水平协议)的事件的时间节点数据。因为目标是很严格的,因此对网站用户而言大多数事件是轻微的,不明显的。图1a显示事件在星期六和星期日发生的几率大幅减小,然而也不会影响网站流量。图1b显示6个月的时间只有两周没有事件:包括圣诞节的一周和员工写互评的一周。测试
这两个数据彷佛代表,当Facebook的员工由于忙于其它事情(如周末、节假日以及员工考核等)而没有积极去改变基础设施的时候,网站的可靠性反而处于一个比较高的水平。致使咱们相信这不是由于Facebook员工过于粗心,而是证实了基础设施在很大程度上是对非人为的错误进行自我修复,如机器故障。
虽然事故有不一样的产生缘由,可是经过总结发现,有三种常见的缘由会使故障扩大并成为大规模的问题。对于每个成因,都应制定相应的预防措施,以减轻大规模事故。
配置系统每每被设计为能在全球范围内迅速复制更改。Rapid配置更改是一个功能强大的工具,可让工程师快速管理新产品的推出或调整设置。然而,快速配置也意味着当配置不当时会快速引起故障。咱们采起了一些方法来防止配置更改致使故障。
让每一个人都使用一个通用的配置系统
使用通用配置系统能够确保程序和工具适用于全部类型的配置。在Facebook,咱们发现团队有时会试图以一次性的方式来进行配置。避免使用这种方式而采用一种统一的方式来进行配置,从而使配置系统成为一种提升站点可靠性的衡量方法。
静态验证配置更改
许多配置系统容许松散类型的配置,如JSON结构。这些类型的配置很容易使工程师犯一些低级错误,例如敲错字段,若是这个字段是必须使用整数的字符串。对于这种类型的错误最好的办法就是使用静态验证。一个结构化的格式(例如,在Facebook使用的Thrift)能够提供最基本的验证。然而,编写验证程序来验证更详细的要求也是合理的。
运行一个Canary
首先将配置部署到服务的小范围,能够防止灾难性的更改。一个Canary能够采起多种形式。最明显的是A / B测试,如只对百分之一的用户推出一个新的配置。多个A / B测试能够同时运行,而且可使用数据随时间进度来跟踪度量。
然而,对于可靠性的目的,A / B测试不知足咱们的全部需求。一个更改部署给少数用户,但致使了服务器崩溃或内存耗尽的变化,显然会产生超出测试的有限用户的影响。A / B测试也费时。工程师们经常但愿在没有使用A / B测试的状况下推出一些微小的变化。为此,Facebook基础设施自动测试新配置的一小部分服务器。例如,若是咱们但愿部署一个新的A / B测试给百分之一的用户,首先部署测试在仅影响不多量服务器的那部分用户,在一个很短的时间内监测这些服务器,以确保他们不会崩溃或有其余很明显的问题。这种机制提供了一个适用于全部变动的基本的“健全检查”以确保它们不会形成大面积的故障。
保持良好的配置
Facebook的配置系统的设计是尽可能确保当更新带来故障时保持良好的配置。开发人员但愿建立的配置系统当接收到无效的更新配置时会崩溃。喜欢在这些类型的状况下保留旧的配置,并向系统操做员发出警报,说明该配置没法更新。继续运行旧有的配置一般优于将错误返回给用户。
使它容易恢复
有时,尽管尽了最大努力,部署的配置依然有问题,快速查找和恢复是解决这类问题的关键,配置系统是由版本控制,这使得系统很容易恢复。
开发者一般默认配置管理,服务发现,存储系统等核心业务永远不会发生故障。但是,这些核心业务的轻微故障都会引发大面积的事故发生。
核心服务的缓存数据
依赖于这些类型的服务一般是没必要要的,能够经过缓存数据的方式,以此保证其中一个系统短暂性中断,而其它服务依旧继续运行。
提供硬化的API使用核心服务
核心服务是最好的补充公共库,遵循最佳实践来使用这些核心服务。例如,库能够提供良好的api来管理缓存或处理故障。
运行的消防演习
你可能认为可以在核心服务中断中生存下来,在尝试以前,你永远不会知道。对于这些类型的中断,咱们不得不开发系统的消防演习,从故障注入系统应用到单个服务器中,以此手动触发整个数据中心的中断。
一些故障致使服务的延迟增长到客户端。这种增长的延迟可能不多(例如,考虑到一我的的配置错误,可是依旧服务的能力致使CPU使用量增长),还有就是,它多是无限的(一个服务线程服务响应陷入瘫痪)。而少许的延迟能够很容易地解决由Facebook的基础设施、大量的延迟会致使全面故障。几乎全部的服务对未完成请求的数量都有限制,这个限制多是因为每一个请求服务线程数量有限,也多是因为基于故障服务中的内存有限。若是一个服务面临大量的延迟,那么调用它的服务将耗尽他们的资源。这种故障会经过许多层面进入系统服务中,致使系统故障的发生。
资源枯竭是一个极具破坏性的故障模式,因为它容许服务请求的子集用于致使失败的全部请求失败。例如,一个服务调用只推出 1%的用户对新的实验服务,一般要求这个实验服务须要1毫秒,但因为在新的服务失败的请求须要1秒,因此1%的用户使用这项新服务请求可能会消耗太多的线程,其余99%用户就不能运行此线程。
现在,咱们已经发现了一些技术,能够避免这种类型的积累与较低的误报率。
控制延迟
在分析以往的事故延迟中,咱们发现许多最糟糕的故障涉及大量队列等待处理的请求。有问题的服务有一个资源限制(如活动线程或内存的数量)和将缓冲请求以保持低于限制使用的请求。因为服务没法跟上传入请求的速度,队列会变得愈来愈大,直到它突破了应用程序定义的限制。为了解决这种状况,咱们但愿在不影响正常操做和保证可靠性的状况下,来限制队列的大小。咱们研究了一个很类似的bufferbloat,在保证可靠性的同时,使用队列从而不会形成过分延迟。尝试了一种codel1(延时)控制算法:
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将在遥远的过去,致使anM-ms排队超时),一次达到短期的排队对于可靠性的目的。虽然它彷佛有悖常理,请求时间较短,这个过程容许的迅速丢弃服务,而不是创建在系统没法跟上传入请求的服务。短的超时可确保服务器老是处在工做的状态,而不是空闲。
该算法的一个吸引人的特性是,M和N的值每每不须要调整。解决排队问题的其余方法,如在队列中设置项目的数量或设置队列的超时时间的限制,须要在每一个服务基础上进行调整。咱们已经发现,M和100毫秒的值是5毫秒,它能够很好的用于N中。Facebook的开源码library5提供的算法是由thrift4框架实现。
自适应后进先出(后进先出)
大多数服务进程队列FIFO(先进先出)。当处于高额度处理进程中时,先进命令明显已经运行了很长时间,以致于用户可能已经停止了生成请求的操做。当处理先进申请命令时,相比之下这种刚刚抵达的请求命令,首先会消耗少量可能益于用户的请求命令,服务进程请求程序使用的是应后进先出的方式。在正常工做条件下,要求按照先进先出的顺序进行处理,可是当一个队列正要开始成形时,服务器会切换为LIFO模式,这时,LIFO和CoDel就能够很好的结合在一块儿,如图2所示。CoDel超时设置时,阻止长的计算机程序队列增加,而后具备适应性的先出后进命令在计算机程序队列设置新的请求模式,而后在数字信号编码器的做用下,他们两个可以发挥最大化的做用。HHVM3,Facebook的PHP运行时,自适应后进先出法的算法得以实现。
并发控制
不管是编码和自适应的后进先出法都在服务器端运行。服务器一般是执行延迟的最好的措施——服务器更倾向于大量的客户同时可以拥有更多的客户信息。然而,有些故障是如此严重,以致于服务器端控件没法启动。为此,咱们在客户端实施一种权宜之计。每一个客户端会跟踪每一个服务器所未完成的出站请求数量。当发送新请求时,若是对该服务的未执行请求的数目超过可配置的数字,则该请求将当即标记为错误。这种机制可防止单个服务垄断其客户端的资源。
以上内容是数人云今天为你们带来的“Facebook面对大规模系统工程故障排查实践”的上半部分,其中主要涵盖致使故障的缘由、以及可使用一个通用的系统等相关内容,但愿能够对你们有所帮助~明天还会为你们带来最终的解决方案哟,敬请期待~
做者:Ben Maurer
原文:Fail at Scale Reliability in the face of rapid change