故障与高可用

上周在WOT上做的分享《故障与高可用》,PPT和讲义整理在这里。

这里有一份在大会开始前关于这个话题的问答 问答

qqqpppooowww

大家好,今天下午我们一起来讨论故障与高可用这个话题。

高可用这个话题其实是一个非常难讲的话题,因为涉及到这个方面的资料实在是太多了。不过大多数材料大都是告诉大家:“我是这么做的,我是那么做的”,如果大家的业务场景不是类似的,在吸收转化信息时会产生非常高的损耗。

今天我想换一个角度,从故障讲起,因为高可用的最终目标就是不出故障,来分享一下做可用设计时的思路与背后的原因。


qqqpppooowww

我参与过很多的故障解决,一般都是这样的:

  1. 因为缺少监控与日志,花了相当长的时间来找到问题点;
  2. 第一秒看到问题原因代码;
  3. 第二秒想到解决办法;
  4. 第三秒骂自己又犯了一个低级错误。

99%的故障诱因都是非常简单的,只要考虑到,就肯定能在设计阶段解决。极少有那种我们自认倒霉解决不了的问题。


qqqpppooowww

这次分享分为两个部分。

第一部分是对故障诱因的分类,也就是高可用需要解决的问题。你想打到敌人,前提是知道敌人是谁。

第二部分讲的是保障自己服务稳定运行的一些通用办法。


qqqpppooowww

这里我虚构了一个日常生活的故事,基本上就是我出去买饭被淋湿,回来后换上新衣服,在家里往上点餐的过程,来方便大家理解一些后续讲解中的一些思路。


qqqpppooowww

OK,我们现在开始讲三大问题。

对问题的总结方法是非常重要的,总结的精炼一些,得到的解决方案便通用一些。

否则得到的解决方案只能解决某一个具体的问题,没法直接用在别处。

但是如果总结的过于粗暴,比如总结为故障,那也不行,方案肯定没法直接落地。


qqqpppooowww

这是高可用问题中的No.1,但是这个No.1指的是做架构设计时候最容易想到的一类问题。 但要注意的是,它不是带来故障最多的。

挂有是三种表现形式,分别是彻底消失、假死、闪断。

彻底消失是最理想的一种状态,也是大多数同学理解的挂。这里以我要去买饭为例,彻底消失便是我知道那个店已经打烊了,那我就不会去了,非常简单,是吧。

假死是一种很蛋疼的问题,就是我以为你没打烊,然后我去了,你却不接待我,白白浪费我的时间。

闪断指的什么呢,我都坐在你店里了,话刚说到一半,你跑到后厨去了,把我晒在这,他与彻底消失的区别是什么呢,一小会你又回来了,搞得我很烦,走也不是,不走也不是。

右边是我们在挂这个原因上出过的一些问题,大家参考。


qqqpppooowww

现在我们说挂的解决方法,回到刚才的买饭故事。

我出去买饭,下雨了,衣服被淋湿了。其中我发现这三个字非常重要,如果我没有发现衣服被淋湿,也就是没有发现问题,那何谈解决问题。这指的就是监控。

我发现衣服被淋湿之后,便回到家换了一件干净的衣服,重点在于换一件,也就是说,我之所以能解决这个问题,是因为我有好多件衣服。如果我只有这一件衣服,那就没法解决了。这说的便是多实例。“多实例”的设计 是解决所有《挂》类故障的不二之选。

拆分讲的是什么,当业务体系庞大了之后,我们知道挂这个问题是无法避免的,就不要再把所有业务放在同一个地方了。如果衣服、电脑随时都在我的身上,那我一被淋湿之后也就没法用电脑点餐了。


qqqpppooowww

第二个问题,说的是慢!

这里我们拿吃一顿饭所消耗的全部时间举例。

自身慢,指的是我自己吃饭慢,比如正在闹口腔溃疡。

上游慢指的什么呢?比如你家菜单比新华字典还厚,我光点菜就花了一个小时。

下游慢是最容易碰到的!!!我点菜了,一个小时了还没有上齐。(内部版本可以举一下摆手塘的例子)

慢是一种很蛋疼的问题,咱俩的合约都成功达成了,但是你没有按时交付,delay了,我作为请求方会很被动。


qqqpppooowww

解决慢的问题,也离不开刚才我们说的那些方案,比如监控,如果你都不知道慢,那就没法解决。但是因为之前已经讲过了,这里便不再说了,只对有不同含义的地方来说。

第一,多实例在解决慢的问题是有很大帮助的。本来一个进程干,那两个进程干呗,两个不行三个。你炒菜慢,多请几个师傅!

第二,缓存,想象一下。我去你柜台上点餐,需要我跑过去,我在家电脑上也能点,而且不用跑过去。这之间就节省了一个来回 路上的时间。在架构设计中便是缓存的概念,缓存是为性能而生的,没有他解决不了的性能问题。

第三,我去外面买饭,要跑过去,还要跑回来。在网上点餐,快递员会送货上门,我节省了两个路上的时间,这个时间我可以用来干好多其他的事情。


qqqpppooowww

最后一类问题,错!

做错,是导致故障最多的一类问题,横跨P1、P2、P3。

但是相当多的设计同学,都会主动忽略这一类问题,哪怕大家都犯过update后面没有跟where的故障。大家在分析架构中的各个模块时,会想他要挂了、慢了怎么办,极少有人会想他返回的数据要错了怎么办!而对于一些核心系统,比如DNS,必须要考虑系统记录如果被污染了怎么应急处理。

错有两类问题,

第一个是错了,有值,但是错了。

第二类是空,没值。在饭馆吃饭的时候,经常有这种情况,先生,你点的菜卖完了,要不要换一个。


qqqpppooowww

我们都是技术人,都信仰技术的力量,但是技术真不是万能的,任何事物都由正反两方面。

针对错,没有很好的技术机制,需要更多的依赖流程规范的力量。

比如,代码上线前,应该先经过QA测试,然后灰度,全量,最后检查自己业务的核心指标。对于核心的底层配置,要提前做好数据被污染后的应急方案。


qqqpppooowww

在前面我们对最容易导致高可用故障的问题进行了分类,分别是挂、慢、错,解决这三类问题,有5种通用的方法。

接下来说这五种方法。


qqqpppooowww

第一个便是监控,没有监控就是瞎子。知彼知己才能百战不殆,没有监控的系统无论架构再牛逼都不可能是高可用的。

监控里面比较容易出现的问题。

第一便是有数据没报警,这个问题非常普遍。我们有几十万个监控指标,怎么可能一个个去看呢,必须有报警。

第二便是报警不合理,没法看。监控最终是要看的,不能指望通过合并、聚合来解决报警本身不合理的问题,监控点的设计本身就应该是合理的。

第三,比如我们的redis高可用机制,绝对不会因为ping不通redis就直接去做切换的,那很扯淡的,毕竟内网也是有各种原因的网络抖动的。

第四,防止监控拖垮服务,比如去年的P1,lookup同步依赖报警系统,导致自己被拖垮。


qqqpppooowww

不是团队里所有的人知道怎么做一个完善的监控。


qqqpppooowww

多实例的核心就是有备无患,对于“挂”的问题,他是完美的解决方式。

根据需求的不同,实例的概念可以进程、机器、机柜、交换机、机房。

多实例有三种表现形式,冷备、热备、多活。 有的分类方法分了四类,即冷备与热备之间还有一个暖备,我觉得没有意义。

冷备的核心点是需要人工来切换。

热备的关键是检测到错误后能够自动切换,比较经典的案例比如Bond、LVS等。

而冷备和热备有一个共同点,即备用方案平时不提供服务。

多活和他们不一样,多活的所有实例都在同时提供服务,绝大多数的分布式RPC方案都是这种设计,没有那种挂了之后不敢切的顾虑。

我们很多设计是多实例的,也踩到过很多的坑。

第一便是,一定要把握住需求,知道自己的多实例是什么级别的。多实例的级别一定要与机器相匹配。 比如多进程的设计肯定解决不了机器挂掉的问题,多服务器设计未必能解决机柜整体掉电与交换机故障的问题。 所以,首先要知道自己要解决的场景是什么,会不会同时挂掉。

第二个问题,是切换失败。比如很早之前的moa,在redis域名切换之后,需要人工重启,这就让redis的高可用机制实质上是无效的。

第三个问题,什么都是对的,但是某个节点挂掉之后,剩余的节点是否有足够的容量。比如我们的lvs,有时候会为了应对容量高峰,改为多活,主备同时提供服务,但这时如果挂了一个,另外一个是无法负担起全部的流量的。还有像前不久视频的一个moa,本来没有问题,但被人为的隔离掉了很多实例,导致剩下的实例无法承担起足够的高峰压力从而导致实际问题。

这几个问题,都需要通过完善监控指标来避免。比如我们最新在做的moa-monitor,自动分析MOA实例的分布是符合高可用的要求。


qqqpppooowww

大家关注:监控项


qqqpppooowww

拆分是一个非常好用的方法,当你碰到一个棘手的问题,所有的方法都不好使的时候,试试拆分。最起码你可以把问题元素拆出来,别让他影响别人。

拆分有两个方面,一个是拆入口,一个拆阶段。

拆入口,就是不同的任务路由到不同的实例上。比如数据库的读写分离,防止较慢的写操作影响很快读操作;常用的nginx划分upstream,来保护重点业务,防止一挂全挂;还有,向域名级别的拆分,比如我们的日志上传剥离,防止出现上一次那个日志把主业务堵塞的故障。如果我们在设计前便考虑到了这一点,很简单的拆分一下,就会避免那次故障。现在我们已经拆开了,可以想象,就算以后再出问题,影响的也只有日志而已。

在拆分方面,暂时没有什么很严重的问题,欢迎大家提供线索。


qqqpppooowww


qqqpppooowww

缓存的目标非常明确,解决各类性能问题。

缓存中出现的问题还是比较多的

第一便是没有命中率监控,根本不知道缓存起了多大的作用。可以说没有命中率的缓存都是皇帝的新衣,是一种非常流氓的行为。

第二便是脏数据,因为机制上的缺陷导致业务无法容忍的脏数据。陈皓前不久针对这个问题专门写了一篇很详细的文章,大家可以去他的博客查阅。

第三是有缓存,但支持不了太多的穿透率。当缓存服务器宕机时候,整个架构发生雪崩。

第四便是缓存太散。这样就算有缓存,但是当io的时候,需要很多的io次数,比读写db还高。


qqqpppooowww

大家关注:监控项


qqqpppooowww

最后,说一下流程规范。

流程规范的存在,证明了技术不是万能的。谁也不能说因为架构设计的非常优秀,就可以乱来了。

所有的东西都是过去的,未来发生什么,谁也不知道,所以我们必须用流程规范去约束未来的行为,防止未来发生问题。


qqqpppooowww

对于高可用来说,上线是最重要的一个流程规范,灰度发布是最重要的一个环节。希望大家重视。

这是高可用保障中,最后的屏障。上线规划如果可以胡来,那再牛逼的人也做不出高可用的架构产品。


qqqpppooowww

最后,我们来总结一下刚才说的内容。

我们内部所有的架构设计方法与思路,都基于VP春来在很早之前定下的一个论调————基于异常的架构设计。用编程上的例子来讲,叫做防御性编程,大家会好理解一些。

在平时做架构设计时,不要去考虑某个模块会不会出现挂、慢、错的问题,直接去想如果出现了要怎么办,是否可容忍,如果无法容忍需要怎么解决。

今天我把技术体系中所有可能出现的故障诱因进行归类,三大类:挂、慢、错。

为了解决这三类问题,我们在设计时可以考虑的方法,以及优先选用的方法,这些方法是从技术体系整体考虑的,并不完全是技术手段。

所有的东西来自于我对工作的一些总结,这些理论是否适用于其它场景,留给时间去证明。

qqqpppooowww

2016-08

--EOF--

知识共享许可协议 本作品采用知识共享署名 3.0 未本地化版本许可协议进行许可。
comments powered by Disqus