Hunter的大杂烩

March 27, 2009

大规模服务设计部署经验谈(下)

Filed under: 架构 — hunter @ 8:51 pm

摘自《程序员》2008.8

声明:本文所有权属于《程序员》杂志,如本转摘有任何产权方面的问题,请及时联系本人,谢谢!

============================================= 

大规模服务设计部署经验谈(下)

文/J a m e s H a m_lto n译/赖翥翔

运营和功能计划

    要高效地运营服务,关键在于让构建的系统有效地消除运营团队的绝
大部分管理交互。这样做的目标,是让一个高度可靠的24 x 7小时运行的
服务,由一个8 x 5小时工作的运营团队就足以维护起来。
    不过世事难料,一组或者多组系统救火不成,无法恢复上线的事情是
时有发生的。在熟知这些可能性的情况下,实现把损坏的系统标为当机这
个过程的自动化。依赖运营团队手动更新SQL表或者使用特别的技术移
动数据,都会招致灾难。与故障交战正酣时,往往错误也容易迭出。先预
估运营团队需要采取的补救措施,然后预先编写和测试这些过程。一般来
说,开发团队必须将紧急恢复措施自动化,而且他们必须对之进行测试。
显然,百密一疏,并非所有故障都能预估到,但通常一小组恢复措施就可
以用来恢复多种类型的故障。从根本上说,要构建并测试可以根据灾难的
范围和性质以不同方式使用及结合的“恢复内核”。
    恢复脚本应当在生产环境中进行测试。这里有一条普适规则,即如果
没经过频繁测试,什么程序都无法正常工作,因此不要实现团队没勇气使
用的任何东西。如果在生活环境中测试风险过高,那么脚本就没有达到能
在紧急情况下使用的标准,或者说在紧急情况下不安全。这里很关键的一
点是,灾难总是可能发生的,由无法按预期结果运行的恢复步骤所导致的
小问题酿成大灾难的例子是屡见不鲜的。要预见到这样的事件,设计出自
动化措施,让服务回复正常状态,而不至于丢失更多数据或损耗更多正常
运行时间。
    ◆让开发团队承担责任。Amazon也许是沿着这条道路贯彻得最坚定最矢
志不渝的公司了——他们的口号是“你创建就该你管”。这样的立场也许要比
我们会选择的更坚定一些,但这显然是一个正确的大方向。如果不得不频繁在
深更半夜给开发团队打电话,那么你就得做出一套自动化方案。如果需要频繁
给运营团队打电话,那么通常的反应就是要增加运营团队的人手。

   —- 我们的俗语是:不要让别人帮你搽屁股^_^
    ◆只进行软删除。绝不要删除任何东西,只可以把这些东西标记成删
除状态。在有新数据进入时,即时将请求记录下来。每两周(或者更长时间)
保存一份所有操作的历史记录,可以有助于从软件或者管理上的错误进行恢复。如
果有谁犯了错误,忘记在delete语句加上where子句(这样的错误以前发生过,以
后也可能再犯),那么数据的所有逻辑拷贝都会被删除。不管是RAID还是镜像都
无法防止这样的错误。具备数据恢复的能力,可以让原来会十分令人窘迫难堪的
大问题转化为一个小到甚至可以忽略不计的小障碍。对于那些已经做过离线备份的
系统,只需要从上次备份开始记录进入服务的附加数据就可以了。不过谨慎起见,
不管怎么说我们还是推荐进行更进一步的备份。

  —- 这个需要考量数据的重要性,平衡操作上的繁琐带来的代价以及必要性
  ◆跟踪资源分配。了解性能规划的额外负载所带来的开销。每个服务都需要开
发出一些使用的度量标准,例如并发在线用户数量、每秒用户访问数或者其它合
适的标准。不管度量标准是什么,在这个负载度量和所需的硬件资源之间肯定存
在一个已知的直接相互关系。估算的负载数字应当由销售和营销部门提供,并在
性能规划过程中为运营团队所使用。不同的服务会拥有不同的变更速率,也要求
不同的订购周期。在我们开发过的服务中,我们每90天更新一次市场预报,
每30天更新一次性能规划和订购一次设备。
  ◆每次变更一样东西。在出现麻烦时,应当每次只向环境应用一个变
更。这条准则看起来显而易见,但我们也看见过许多场合里出现多个变更,
导致起因和效果不能吻合。

   —- 很好的经验法则,以前在debug pc机硬件故障时常常用到
  ◆使所有资源都可以配置。在生产环境中,任何存在变更需求的资源,
都应无需经过任何代码改变,就能在生产环境中进行配置和调优。即使你
没什么好理由说明为什么某个值会有必要在生产环境中更改,只要实现起
来没什么难度,还是让它可变更好些。不应在生产环境中随意更改这些开关,
而应该使用为生产环境所规划的配置对整个系统进行彻底测试。不过,在
出现生产环境问题时,比起编码、编译、测试再部署代码变更的过程,进行简
单的配置变更永远是更加简单、安全和快速的。

  —- 努力目标,不过也需要平衡,一个有几百项配置参数的配置文件往往会把新人吓垮
  运营团队不能在部署环境中装配服务。要在部署过程中付出实质性的
努力,以确保系统中的每个组件都可以生成性能数据、健康数据和吞吐量
数据等。
  在任何有配置变更发生的时候,都必须在审核日志中记录详细变更内
容、变更人和变更时间。在生产环境出现异常时,第一个要回答的问题就
是最近到底进行过哪些变更。离开了审核跟踪,那么这个答案就是“什么
都没被改过”,而且情况往往会是,被人们忘掉的就是引发问题的变更。

  —- good !运维的兄弟们,好好学学哈!
    预警是一门艺术。人们总是倾向于对所有事件都做警报,因为开发人
员认为这些事件可能值得关注。正是如此,多数服务的第一版通常都会产
生长篇累牍的无用预警,结果再也没有人去理睬。要提高效率,每个警报
都得说明一个问题,否则运营团队会慢慢学会对这些警报置之不理。进行
互动慢慢跳出需要警报的条件,保证所有关键性事件得到预警,以及在无
需采取应对措施时没有警报。除此之外,要实现正确的预警别无灵丹妙药。
要得到正确的预警标准,有两条度量标准很有用,也值得尝试:一、警报
和实际故障比(同时要设定一个较为接近的目标);二、没有相应警报的系
统健康问题数量(并设定一个近于O的目标)。

  —- 老是喊狼来的监控系统,最终会变成摆设!
    ◆对所有资源进行检测。测量通过系统的每一次用户交互或事务,报
告异常情况。尝试“运行器(人为的工作负载,用来模拟生产环境中用户
和服务的交互)”也是可以的,不过这还远远不够。如果只是单独使用运行
器,我们发现需要花费数日才能检测到一个严重错误,因为运行器的标准
工作负载也会被继续良好地处理,接下来还要再花几天才能查出原因。
    ◆数据是最有价值的资产。如果没有充分理解正常的操作行为,那么
要对非正常行为做出响应就不是件容易的事情。我们需要汇集许多系统内
发生的事件信息,才能知道系统是否真的正常运行。许多服务都经历过灾
难性故障,而只有电话铃响起的时候,人们才意识到故障的发生。
    ◆从客户的角度看服务。进行端到端的测试。虽然单有运行器不够,
但还是需要它们来保证服务器的完整运行。保证例如新用户登录这样重要
的复杂过程经过运行器的测试。避免误警,如果在某个运行器上的故障没
被当作重要故障,那么变更测试对象,换到是重要故障的运行器上。重申一
下,一旦人们开始对数据视而不见,真正的损失就会让人们措手不及。
    ◆检测是生产环境测试所必不可少的。要在生产环境中实现安全的测
试,就有必要进行全面监控和预警。如果某个组件开始出现故障,就必须
快速检测出来。
    ◆延迟是最棘手的问题。缓慢的I/O,以及尚未出现故障但处理缓慢的
现象,都是很好的例子。这些问题发现起来很困难,所以一定要仔细检测,
保证这样的现象可以检测出来。
    ◆要有足够的生产环境数据。为了发现问题,数据是不可或缺的。在
早期就要建立细粒度的监控机制,否则放到后面再翻新成本就高得多了。
我们所依赖的数据中最重要的包括:
    ◇对所有操作使用性能计数器。至少记录操作的延迟和每秒钟的操作
次数。这些数值突然出现的此消彼长现象,是一个十分危险的信号。
    ◇审核所有操作。每次有人进行操作之后,尤其是那些明显的操作,
一定要记入日志。这样做有两个目的:首先,可以对日志进行挖掘,找出用
户采取的操作类型(在我们的例子里是用户查询的种类);其次,一旦发现
问题,这样做有助于调试问题。
    相关视点:如果每个人使用相同的账号管理系统的话,这么做带来不
了多少好处。相反这是一个非常糟的办法,不过这种情况不多。
    ◇跟踪所有容错机制。容错机制会把故障隐藏起来。每次出现重试、
某个数据被一处复制到另一处,以及机器或者服务重启这类现象时,都要
进行跟踪。要了解容错机制在何时隐藏了小故障,这样就可以对这些小故
障进行追溯,以防其变成大面积故障。我们曾经碰到过这样一个问题:一个
跨2000台机器的服务在几天内慢慢地瘫痪,最后只剩400台机器可用,而
一开始这个问题却没有发现。
    ◇跟踪对重要实体的操作。为某个特殊实体的所有重要操作记录一份
“审核日志”,不管这个实体是一个文档,或者一组文档。在运行数据分析时,
常常能在数据中发现异常现象。要了解数据的来源及其经历的处理过程。
到了后期才往项目加入这样的功能是非常困难的。
    ◇断言(assert)。不要吝惜断言的使用,而且要贯彻在整个产品中。收
集因此产生的日志或者崩溃转储(crash dump),并进行调查研究。对于在同一
个进程边界内运行不同服务并且无法使用断言的系统,要写下跟踪记录。不管
哪种实现,都要能够对错误进行标记,频繁挖掘不同问题的频率。
    ◇保留历史记录。历史性能和日志数据对于趋势的总结和问题的诊断
都是非常必要的。
    ◆可配置的日志功能。对可配置的日志功能提供支持,这些日志记录
可以有选择性开启或关闭,以便进行错误调试。在故障过程中,如果不得
不部署带额外监控功能的新构建版本是非常危险的。
    ◆外部化健康信息,用于监控。考虑能实现外部监控服务健康程度的
方式,并使对生产环境进行监控容易实现。
    ◆保证所有报告的错误可以应对。问题总会发生,系统也总会出错。如
果在代码中检测到无法恢复的错误,并且在日志或者报告中归为错误,那
么错误信息应当指出错误可能发生的原因,并提供修复的建议。无法采取
应对措施的错误报告毫无用处,而且时间一久这些错误报告会像“狼来了”
一样被人们忽略,那时候真正的错误就可能被错过。
    ◆启用生产环境问题的快速诊断。
    ◇为诊断提供足够信息。当问题被标出时,要提供足够的信息,人们
才可以对其进行诊断;否则门槛会非常高,标注也会被忽略。例如,不要
    记录各个月艮务器完整的历史数据非常重要只说“10个查询都没返回
结果”,还得补上“列表在这里,还有问题出现的次数”。
    ◇证据链。保证存在一条贯穿始终的路径,可供开发人员诊断之用。
通常这都是由日志实现的。
    ◇在生产环境中的调试。我们偏爱这样的一种模式:系统没有被包括
运营团队在内的任何人触碰过,并且调试是通过镜像快照、内存转储并将
所得数据发送到生产环境之外来实现的。当生产环境成为唯一选择是,开
发人员就是最好的选择。要确保开发人员得到良好的培训,知晓生产环境
的操作限制。我们的经验是,系统在生产环境中动的次数越少,客户通常
也越开心。因此我们推荐,一定要多努一把力,尽量避免接触生产环境中
的系统。
    ◇记录所有重要的操作。每次系统执行了重要的操作,特别是对网络
请求和数据修改上,要进行记录。这既包括用户发送命令的事件,也包括
系统内部的行为。有了这样的记录,调试问题的时候就会获益无穷。更重
要的是,还可以开发出相应的挖掘工具,用来找出有用的集合,比如用户
的查询都是什么样的(也就是说,用了哪些关键字,有多少关键字等等)。

优雅降级和许可控制

    当发生DoS攻击或者由于用户使用模式变化而带来的负载激增时,服务器
需要能实现优雅降级和管理控制。例如,911恐怖袭击发生后,大多数的新闻服
务都发生瘫痪,无法向所有用户群体提供任何可用的服务。与此相比,如果能
够将~部分文章可靠地提供给用户,总比什么都不能提供好。最佳实践有两种
“大红开关(big red switch)”和许可控制,如果能够针对服务进行量身定制,
作用将会十分强大。
    ◆支持“大红开关”。这个概念最初来源于Windows Live Search,它拥
有很大的权力。我们对这个概念进行了一定程度的推广,更事务性的服务
与搜索差别迥异。不过这个想法非常有效,而且可以应用于任何地方。一
般来说,“大红开关”是当服务已经或者即将无法满足SLA时,采用的一个
经过精心设计和测试的措施。将优雅降级比喻成“大红开关”稍微有些不
恰当,但核心意思是指在紧急时刻卸掉非关键的负载。
    “大红开关”的主要思想是保证关键任务的运行,同时卸掉或者延迟
非关键的负载。从设计角度来说,这种情况应该不会发生,但在发生紧急
情况时不失为一个好的救火办法。在紧急情况已经发生之后再来考虑这些
问题是十分危险的。如果有哪个负载可以被加入队列并延后处理,或者在
关掉高级查询后仍能继续运行事务系统,那么这都是作为“大红开关”的
理想对象。关键在于,判断在整个系统出现问题时有哪些任务是必不可少
的,然后实现并测试在问题出现时关闭非关键服务的选项。切记“大红开
关”必须是可逆的,也就是说当紧急情况解除后,必须保证开关能正确地
让所有的批处理工作和先前被停止的非关键任务恢复原状,这是应当经过
测试的。
    ◆控制许可。许可控制是另外一个重要的概念。如果都无法对当前的
任务进行处理,向系统引入更多的工作负载只会将不好的体验扩散到更大
的用户群中。这要如何实现和系统有关,有些系统实现很容易,有些则比
较困难。拿我们上次的邮件处理服务作为例子,如果系统超过负荷,开始
排队列的话,我们最好是拒绝接受后续邮件进入系统,然后让这些邮件在
来源处列队等候。这样做很有意义并实际上减少了整体服务延迟,主要原
因在于一旦队列在我方形成,系统会处理得更慢。如果我们拒绝形成队列,
吞吐量也会得以提升。另外一个诀窍是:重要客户优先于非重要用户,注
册用户优先于访客(但如果是以吸引新访客作为商业模式的,另当别论)。
    ◆对许可进行计量。还有一个无比重要的概念,就是前面所说的许可
控制观点的修改方案。如果系统故障并当机,必须能够实现逐步恢复用户
使用,并确保系统运行正常。比如先允许1个用户进入,然后允许每秒进
入10个用户,随后再逐渐放宽限制。对于重新上线或者从严重错误中恢复
过来的系统,应该确保每项服务都有一个细粒度的开关,来实现用户的缓
慢增加,这是至关紧要的。大多数系统的初始版本中很少有包括这项功能。
对于有客户端的系统,一定会存在一种方式,用来通知客户端服务器当机,并
且告知可能的恢复时间。这样一方面使客户端尽可能继续基于本地数据来运行,另
一方面也可以避免客户端打扰服务器,以便后者更快恢复。这样同时也给了服务拥
有者一个直接和用户沟通的机会(见下节),用来调整用户的期望值。另外一个关于
客户端的技巧是刻意设置扰动(jitter),和自动备份来防止客户端在同一时刻“扑
向”脆弱的服务器。

客户和媒体沟通计划

    当系统出错时需要就发生的延误和其它相关事项与客户进行沟通。沟
通应当能以可选的方式通过多个渠道完成:RSS、Web,还有即时消息等等。
另外,对于拥有客户端的系统,通过客户端来进行和用户的沟通也是很有
效的。可以告诉客户端在一定时间内或者特定时间点之前避免访问服务器,
或者如果支持的话,可以告诉客户端在离线和缓存的方式运行。客户端可
以把系统状态告诉用户,并说明可以预计完整的功能在何时恢复。
    即使在没有客户端的情况下,比如用户通过网页来和系统交互,仍然
可以告知用户系统当前的状态。当用户了解发生的状况并对系统的恢复时
间有一个合理的期望值,他们的满意度会大大提高。系统管理员常常会不
自觉地倾向于隐藏系统发生的问题,但是跟据我们的经验,我们确信,将
系统的状态告知用户会极大地提高其满意度。即便是非付费系统,如果人
们知道系统发生了状况并被告知其何时会恢复,他们放弃使用这项服务的
可能性也会减小。
    某些事件会引发媒体报导。如果这样的场景有预先备好的应对方案,
那么会更加真实地反应服务的情况。大量数据丢失或损坏、安全遭到破坏、
违反隐私以及服务器长时间当机,这样的情况都可能引起媒体的关注。事
先准备好一份沟通计划。清楚电话通知谁,并能主导谈话。沟通方案的框
架应当事先搭好。应针对每一种事故制定一份沟通方案,内容包括该给谁
电话通知、电话时间,还有如何掌控沟通过程。

客户自预置及自助服务

    客户自己进行预置可以大幅度降低成本,同时还能提升客户满意度。如果
客户可以访问网站,输入所需数据,然后就可以开始使用服务,那么他们要比
不得不在电话处理队列中浪费时间开心得多。我们一直认为,主流移动运营商
因为没为那些不想致电客户支持组的用户提供自助服务,错过了一次拯救并提
升客户满意度的好机会。

结语

    要降低大规模互联网服务的运营成本并改善服务的可靠性,一切从编
写服务时注重运营友好开始。在这篇论文中,我们为“运营友好”做了诠释,
并根据从事大规模服务的工程师的经验,总结了服务设计、开发、部署和
运营的最佳实践。■

 

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress