转载:来自Google的高可用架构理念与实践

原文:https://blog.coding.net/blog/architecture-concept-and-practice-from-Google

一、决定可用性的两大因素

谈可用性不需要绕来绕去,大家只谈 SLA 即可。大家可以看下面这个图:

图片

要谈可用性,首先必须承认所有东西都有不可用的时候,只是不可用程度而已。一般来说,我们的观念里一个服务至少要做到 99.9% 才称为基本上可用,是合格性产品。否则基本很难被别人使用。

从 3 个 9 迈向 4 个 9,从 8 小时一下缩短到 52.6 分钟的不可用时间,是一个很大的进步。Google 内部只有 4 个 9 以上的服务才会配备 SRE,SRE 是必须在接到报警 5 分钟之内上线处理问题的,否则报警系统自动升级到下一个 SRE。如果还没有,直接给老板发报警。

大家之前可能听说谷歌搜索服务可用度大概是全球 5 个 9,6 个 9 之间。其实这是一个多层,多级,全球化的概念,具体到每个节点其实没那么高。比如说当年北京王府井楼上的搜索集群节点就是按 3 个 9 设计的。

有关 SLA 还有一个秘密,就是一般大家都在谈年 SLA,但是年 SLA 除了客户赔款以外,一般没有任何实际工程指导意义。 Google 内部更看重的是季度 SLA,甚至月 SLA,甚至周 SLA。这所带来的挑战就更大了。

为什么说看这个图有用,因为 99%、99.9% 是基本可以靠运气搞定的哦。到 3 个 9 可以靠堆人,也就是 3 班倒之类的强制值班基本搞定。但是从 3 个 9 往上,就基本超出了人力的范畴,考验的是业务的自愈能力,架构的容灾、容错设计,灾备系统的完善等等。

说了这么多,作为一个架构者,我们如何来系统的分解“提升 SLA”这一个难题呢。

这里我引入两个工业级别的概念 MTBF 和 MTTR。

MTBF: Mean time between Failures。 用通俗的话讲,就是一个东西有多不可靠,多长时间坏一次。
MTTR: Mean time to recover。意思就是一旦坏了,恢复服务的时间需要多长。

有了这两个概念, 我们就可以提出:

图片

一个服务的可用度,取决于 MTBF 和 MTTR 这两个因子。从这个公式出发,结合实际情况,就很好理清高可用架构的基本路数了。那就是: 要么提高 MTBF, 要么降低 MTTR。除此之外别无他法。

要注意的是,考虑 MTBF 和 MTTR 的时候都不能脱离现实。

理论上来说,作为一个正常人类,收到突发报警、能正确的分析出问题所在、找到正确的解决方案、并且 【正确实施】的时间极限大概是 【两分钟】。这个标准我个人觉得是高到天上去了。作为一个苦练多年的 Oncall 工程师,我 2 分钟能看清报警,上了 VPN,找到 dashboard,就不错了。就算是已知问题,有应对方案,能敲对命令,完全成功,也至少需要 15 - 20 分钟。所以如果按照这个标准的话,管理的服务如果想达到 4 个 9,那么一年只能坏 1 次,2 次就超标了。实现高可用基本靠运气~

回过来接着说说 MTBF 吧。请各位想一下,影响服务MTBF的三大因素!

  1. 发布
  2. 发布
  3. 还是发布!

这个术语上叫 Age Mortality Risk。

一般一个服务只要你不去碰他一年都不会坏一次。更新越频繁,坏的可能性就越大。凡是 Software 都有 BUG,修 BUG 的更新也会引入新的 BUG。发布新版本,新功能是 MTBF 最大的敌人。

二、高可用性方案

说了 MTBF 和 MTTR 这两个定义,那么具体究竟应该如何落地实践来提高可用性呢?

首先说几个大家可能都听腻了的方案

一、提高冗余度,多实例运行,用资源换可用性。

虽然道理很简单,实现起来可不简单,有很多很多细节上的东西需要考虑。

第一个细节:N + 2 应该是标配。

N + 2 就是说平时如果一个服务需要 1 个实例正常提供服务,那么我们就在生产环境上应该部署 1 + 2 = 3 个节点。大家可能觉得 N + 1 很合理,也就是有个热备份系统,比较能够接受。但是你要想到:服务 N + 1 部署只能提供热备容灾,发布的时候就失去保护了。

因为刚才说了, 发布不成功的几率很高!

从另一个角度来讲,服务 N + 2 说的是在丢失两个最大的实例的同时,依然可以维持业务的正常运转。

这其实就是最近常说的两地三中心的概念有点像。

第二个细节: 实例之间必须对等、独立。

千万不要搞一大一小,或者相互依赖。否则你的 N + 2 就不是真的 N + 2。如果两地三中心的一个中心是需要 24 小时才能迁移过去的,那他就不是一个高可用性部署,还是叫异地灾备系统吧。

第三个细节:流量控制能力非常重要。

想做到高可用,必须拥有一套非常可靠的流量控制系统。这套系统按常见的维度,比如说源 IP,目标 IP 来调度是不够的,最好能按业务维度来调度流量。比如说按 API, 甚至按用户类型,用户来源等等来调度。

为什么?因为一个高可用系统必须要支持一下几种场景:

  1. Isolation。A 用户发来的请求可能和 B 用户发来的请求同时处理的时候有冲突,需要隔离。
  2. Quarantine。用户 A 发来的请求可能资源消耗超标,必须能将这类请求钉死在有限的几个节点上,从而顾全大局。
  3. Query-of-death。大家都遇到过吧。上线之后一个用户发来个一个异常请求直接搞挂服务。连续多发几个,整个集群都挂没了,高可用还怎么做到?那么,对这种类型的防范就是要在死掉几台服务器之后可以自动屏蔽类似的请求。需要结合业务去具体分析。

但是想要达到高可用,这些都是必备的,也是一定会遇到的场景。还是那句话,靠人是没戏的。

二、变更管理(Change Management)

还记得影响 MTBF 最大的因子吗?发布质量不提高,一切都是空谈。

第一点: 线下测试(Offline Test)

线下测试永远比线上调试容易一百倍,也安全一百倍。

这个道理很简单,就看执行。如果各位的团队还没有完整的线下测试环境,那么我的意见是不要再接新业务了,花点时间先把这个搞定。这其中包括代码测试、数据兼容性测试、压力测试等等。

台上一分钟,台下十年功。

可用性的阶段性提高,不是靠运维团队,而是靠产品团队。能在线下完成的测试,绝不拍脑门到线上去实验。

第二点:灰度发布

这个道理说起来好像也很普通,但是具体实施起来是很有讲究的。

首先灰度发布是速度与安全性作为妥协。他是发布众多保险的最后一道,而不是唯一的一道。如果只是为了灰度而灰度,故意人为的拖慢进度,反而造成线上多版本长期间共存,有可能会引入新的问题。

做灰度发布,如果是匀速的,说明没有理解灰度发布的意义。一般来说阶段选择上从 1% -> 10% -> 100% 的指数型增长。这个阶段,是根据具体业务不同按维度去细分的。

这里面的重点在于1%并不全是随机选择的,而是根据业务特点、数据特点选择的一批有极强的代表性的实例,去做灰度发布的小白鼠。甚至于每次发布的 第一阶段用户(我们叫 Canary / 金丝雀) ,根据每次发布的特点不同,是人为挑选的。

如果要发布一个只给亚洲用户使用的功能,很明显用美国或欧洲的集群来做发布实验,是没有什么意义的。从这个角度来想,是不是灰度发布可做的事情很多很多?真的不只是按机器划分这么简单。

回到本质:灰度发布是上线的最后一道安全防护机制。即不能过慢,让产品团队过度依赖,也不能过于随机,失去了他的意义。

总之,灰度发布,全在细节里。

第三点:服务必须对回滚提供支持

这点不允许商量!

这么重要的话题,我还要用一个感叹号来强调一下!

但是估计现实情况里,大家都听过各种各样的理由吧。我这里有三条买也买不到的秘籍,今天跟大家分享一下,保证药到病除。

理由1:我这个数据改动之后格式跟以前的不兼容了,回退也不能正常!
秘籍1:设计、开发时候就考虑好兼容性问题!!!比如说数据库改字段的事就不要做,改成另加一个字段就好。数据存储格式就最好采用 protobuf 这种支持数据版本、支持前后兼容性的方案。最差的情况,也要在变更实施『之前』,想清楚数据兼容性的问题。没有回滚脚本,不给更新,起码做到有备而战。

理由2:我这个变更删掉东西了!回退之后数据也没了!
秘籍2:你一定是在逗我。把这个变更打回去,分成两半。第一半禁止访问这个数据。等到发布之后真没问题了,再来发布第二半,第二半真正删掉数据。这样第一半实施之后需要回滚还可以再回去。

理由3:我这个变更发布了之后, 其他依赖这个系统的人都拿到了错误的数据,再回退也没用了,他们不会再接受老数据了!
秘籍3:这种比较常见出现在配置管理、缓存等系统中。对这类问题,最重要的就是, 应该开发一种跟版本无关的刷新机制。触发刷新的机制应该独立于发布过程。 要有一个强制刷新数据的手段。

以上三个秘籍覆盖了100%的回滚兼容性问题,如果有不存在的,请务必告诉我!

回滚兼容性问题,是一个整体难题。只有开发和运维都意识到这个问题的严重性,才能从整体上解决这个问题。而解决不了回滚难题,就不可能达到高可用。

三、可用性 7 级图表

说完了变更管理,给大家带来一个7级图表,可以看看自己的服务到底在哪个可用性的级别上。

当一个服务挂了的时候......

第一级:Crash with data corruption, destruction.

内存数据库就容易这样。出现个意外情况,所有数据全丢。写硬盘写到一半,挂了之后,不光进程内数据没了,老数据都丢光了。碰上这样的系统,我只能对你表示同情了。

第二级:Crash with new data loss.

一般来说 正常的服务都应该做到这一点...... 。挂了之后最多只丢个几秒之内的数据。

第三级:Crash without data loss.

要达到这一级,需要付出一定程度的技术投入。起码搞清楚如何绕过 OS 各种 Cache,如何绕过硬件的各种坑。

第四级:No crash, but with no or very limited service, low service quality.

做的好一点的系统,不要动不动就崩溃了...... 如果一个程序能够正常处理异常输入,异常数据等,那么就给刚才说的高级流控系统创造了条件。可以把其他的用户流量导入过来,把问题流量发到一边去,不会造成太大的容量损失。

第五级:Partial or limited service, with good to medium service quality.

这一级就还好了,如果多个业务跑在同一个实例上,那么起码不要全部坏掉。有部分服务,比完全没有服务要好

第六级:Failover with significant user visible delay, near full quality of service

上升到这一级别,才摸到高可用的门,也就是有容灾措施。但是可能自动化程度不高,或者是一些关键性问题没有解决,所以业务能恢复,就是比较慢。

第七级:Failover with minimal to none user visible delay, near full quality

of service.

这边蝴蝶扇了一下翅膀,天空落了个打雷,打掉了一整个机房,结果业务完全没受影响。蓝翔技校一铲子下去,互联网都要抖一抖。嘿嘿, 高可用就是为了这种事情做准备。