又一宕机事故!都怪当初没做好故障演练系统……

刘志志 2022-02-23 10:54:42

本文根据刘志志老师在〖deeplus直播:逆袭生产力担当,云原生时代的运维新归宿〗线上分享演讲内容整理而成。(文末有回放的方式,不要错过)


大家好,我是来自去哪儿网的刘志志,19年加入去哪儿网,主要参与CI/CD平台建设,负责故障演练平台的开发。今天的分享主要分为以下三个部分:

 

图片

 

一、背景&价值

 

图片

 

如图所示,左边是近期发生的一件影响较大的事故:Facebook服务宕机。持续时长约7小时,造成了次日超过60亿美金的市值下跌,损失数额巨大。右边所展示的则是我们公司中某个业务线的服务调用关系。可以看到,整个链路非常复杂,如果其中某个链路出现问题,就有可能导致上下游发生故障,甚至造成整个业务瘫痪。

 

而现实生产过程中存在着各种各样的故障,我们有什么方案可以应对这些故障、做好应急预案,以增强我们对故障抵御的信心呢?答案是演习。

 

很多行业都会有演习,包括军事演习、消防演习等。而软件开发领域也有一个类似的实践,即故障演练。故障演练可以通过模拟真实故障,验证系统弱点,同时帮助我们完善故障应急预案和处理体系。

 

我们公司曾经因为微服务太过复杂而导致故障频发,在此之前,公司内部并没有统一的故障演练工具。19年携程曾经出现过一次宕机事故,当时正值五一促销前夕,造成了较大的负面影响。因为我们跟携程的子公司关系,这件事也给我们敲醒了警钟。从那之后,我们便开始着手做故障演练。

 

二、故障演练系统演进

 

图片

 

我们的故障演练系统主要经历了三个演进阶段:

 

  • 第一个阶段是关机演练。这个时候正处于系统初建时期,以资源型演练为主。将内部的IM接、权限接进来,同时接入监控告警。

     

  • 第二个阶段是强弱依赖演练。这个阶段主要做的事情是应用间依赖信息的收集。在这个阶段我们做了依赖标记的入口,并接入了线上回归巡检。

     

  • 第三个阶段是容器化的支持。从去年底到今年,应用开始往容器上迁移,配套工具也因此需要进行升级,所以我们也做了容器化的支持。

 
 

1、关机演练

 

图片

 

首先来介绍一下关机演练。因为当时还没有系统,所以需要先进行选型。图片中展示的是我们当时的备选系统,分别是:Netflix公司的ChAP、阿里巴巴的Chaosblade和PingCAP的Chaos Mesh。
 
Netflix它是通过发布系统扩容来创建实验环境的,可以将对生产环境影响降到最低,但因为它并没有开源,所以也没法考虑,我们在选型时主要考虑Chaosblade跟Chaos Mesh。跟Chaos Mesh相比, Chaosblade支持虚拟机和K8s上的演练,场景比Chaos Mesh更为丰富。它支持多语言应用服务,比如Java、Golang、C++等。而Chaos Mesh它的整体性比较好,既有故障注入也有控制面,Chaosblade当时只有Agent。
 

图片

 
二者各有优劣,而我们公司当时的状况是:底层的资源基本上都是虚拟机,故障注入也是以虚拟机场景为主,公司内部基本上百分之八九十的后端服务都是Java。所以相对来说,Chaosblade更契合我们当时的场景。我们当时选择用它的Agent,加上自研控制端来实现。这是Chaosblade的简单介绍。
 

图片

 
官网上对它的介绍是:它是一个简单易用并且强大的混沌工程工具,今年加入了CNCF Sandbox 项目。主要分为三个部分:
 
  • Chaosblade。命令行工具,包含一些执行器,可以实现Go、Java、C++等几种语言和Linux操作系统以及容器的故障注入。
     
  • Chaosblade-box。今年年初开源的控制面。
     
  • Chaosblade-operator。可以在K8s上使用。 
 
上图的右部分罗列了它的一些故障注入能力,从基础资源到应用服务到云原生基本上都有包含,能力较为全面。同时它还跟云平台有较好的结合。我们这边主要使用了它的Chaosblade跟operator,而Chaosblade主要是应用在 OS层和JVM。
 
选型和分析结束之后,接下来是关机演练。
 
首先是我们的演练目标:
 
  • 流量正常切换
  • 核心服务容量保障
  • 弱依赖服务不影响主流程
  • 中间件&存储高可用
 
而要想实现这些目标,大概需要演练平台的能力能达到:同一机房某业务线所有服务节点全部关机(单次1k节点)的水平。
 
在正式开始关机演练前,我们在设计阶段需要考虑以下几点:
 
  • 机房聚合信息查询,方面应用改造
  • 自动拉群,进度周知
  • 虚拟机关机,实体机kill进程
  • 接入告警,告警事件关联推送
  • 虚拟机开机后关联服务自动恢复
 
这是关机故障的实际流程图:
 

图片

 
左边是虚拟机。虚拟机关机是 Ops封装提供关机接口,由演练平台发起关机,Ops平台会将结果多次回调给我们,回调了之后,我们这边再做一次ping,相当于二次确认,对于超时的要做单个的重试。然后是开机。开机是批量开机,在收到了成功消息之后,调用我们的自助运维平台去将服务拉起来,然后做上线。
 
而实体机使用的是Chaosblade的Agent,和虚拟机相比,它的不同之处在于,它在kill进程之前需要先装上Agent。我们公司内部使用的运维工具是 Saltstack,在关机阶段,我们会把Agent安装上,然后在机器上启动Chaosblade的一个HTTP服务。Agent安装完了之后会调用kill策略结束服务对应的进程。开机的时候就是正常地把服务拉起来,但最后会卸载Chaosblade。
 
这部分我总结了两点经验:
 
  • 异步任务,合理超时,开关机如果时间太久,需要人工进行兜底。
     
  • Chaosblade的安装包大概100M,如果机器的网络资源比较紧张就需要考虑一下限速。
 
下面介绍一下用户的演练流程:
 

图片

 

整体流程跟学校里的考试流程有些类似:考前复习 — 学生做卷子练习 — 学校统考。而对于单个服务负责人来说,他们也需要经历这几个步骤:梳理 — 改造 — 模拟验证。

 
  • 梳理

 

在做统一的关机演练之前,我们会列出checklist,每个服务都要去自检:本身是不是做了多机房?服务依赖的中间件、存储等其他的服务是不是也做了多机房?还要对容量进行评估,特别是核心系统。同时还要做扩容的测试,防止在真正宕机时要做应急扩容的情况发生。

 

  • 改造

 

在改造阶段,我们会根据 checklist发现的问题进行针对性改造。

 

  • 验证

 

通过故障演练系统对单个机房模拟宕机。

 

每个业务线一般都有专门的演练负责人,他们负责组织、进行演练以及复盘的相关事宜。

 

  • 组织

 

提前规划好时间和演练的应用范围以及机房信息,将一些不能够参加演练的应用排除,并根据这些信息生成演练,进行周知。

 

  • 演练

 

一般较大规模的演练会选在低峰时段进行,由负责人进行演练流程的控制:开启 — 结束 — 恢复。相关的业务负责人会去关注业务情况,如果中间发生了业务抖动等意料之外的情况,就需要去沟通是否是要终止演练。

 

  • 复盘

 

演练完成之后还需要进行复盘,对演练过程中发现的问题进行总结叙记录,同时针对这些问题制定改进计划以及下次的验证计划。

 

最开始的演练投入的人数比较多,一开始有几十个人参与,但是到后面做验证性的时候基本上一两个人就可以做上千个节点的演练。

 

 

2、强弱依赖演练

 

什么是强弱依赖?举个例子,服务A依赖服务B,二者之间存在依赖关系。当服务B出问题时,如果服务A的核心流程正常,那么它们之间的关系是弱依赖;如果核心流程出现问题,那么它们之间的关系就是强依赖。

 

下面是强弱依赖演练的背景:

 

  • 微服务稳定性治理

 

  • 依赖关系导致的蝴蝶效应。

  • 弱依赖超时是否合理,熔断是否符合预期?

  • 异常是否被处理等。

  • 强依赖太多,能否降级为弱依赖?

 

  • 依赖数据统一收集管理

 

每个业务线或者每个应用负责的相关人员,他们都会做依赖的梳理,但没有统一的依赖信息收集管理工具,依赖查询和梳理很耗时。强弱依赖信息没有统一的数据源。

 

图片

 

上图展示了一个服务依赖关系。我们公司主要的技术栈其实比较收敛, DB一般都是Redis和MySQL ,服务访问一般是HTTP、dubbo。所以依赖关系的收集,主要也是基于这4种类型。

 

这里简单介绍一下服务上下游的概念。比如说服务A依赖服务B的一个接口,那么服务A就是服务B的上游。我们在做故障注入时,一般是注入在服务A上,也就是在HTTP client或者 dubbo consumer做故障注入。故障的类型一般分为异常和延迟,如果正常的是50毫秒,那我们可以给它延迟100毫秒。

 

1)依赖关系收集
 

图片

 

依赖关系收集的主要来源有两种:一种是日志,即HTTP的access.log文件;另外一种是服务的注册信息,目前我们是用ZK 做注册中心。我们把这些元数据汇集到一个分析聚合的服务中,它会每天更新七日内的数据,并把这些数据聚合之后生成依赖关系存到 DB里面。

 

依赖关系主要包括流量和依赖方向,强弱信息需要用户进行标记。我们在这个演练平台上提供了一个模块,可以实现对强弱依赖关系的标记,同时还能为其他平台提供依赖信息查询。

 

通过日志跟注册信息来取依赖关系各有各的优劣,日志的处理量比较大,而注册信息可能会存在过期数据。

 

2)故障注入编排

 

对于一次故障演练中需要同时注入较多故障的情况,我们做了一个编排的功能。

 

图片

 

强弱依赖主要依赖于 Java的 Agent。故障注入的整个流程主要分为三步:

 

  • 第一步,安装Chaosblade,attach Java进程。

  • 第二步,开始进行故障注入。

  • 第三步,服务恢复,同时卸载attach。

 

在第二步,进行故障注入时,如果是多个故障的话,我们就需要有一些编排策略。比如,当我们需要将5个故障注入到3个Java服务中时,一共有三种注入方式:

 

  • 一种是并行,一次把所有的故障都注入到机器上面。这种情况一般是用于验证弱依赖,而且对于结果比较有信心。如果只是去验证的话,一次把故障都注入进去会比较快。

     

  • 另外一种是串行,机器之间注入故障是并行的,但是每一个机器上服务生效是串行的。这样一来,每个机器上面同一时间只有一个故障,比较容易进行问题排查。

     

  • 我们还提供了一个手动的流程控制,你可以根据自己的需要去选择:什么时候开始哪些机器上的哪些故障。

 

通过这三种方式,基本上可以满足大部分故障注入的需求。

 

3)用户使用流程

 

图片

 

用户使用流程一般是闭环的形式。我们把依赖关系收集好之后,用户会进行强弱标记。标记之后需要进行强弱依赖演练来验证判断:该服务是否是预想中的强依赖或者弱依赖。演练完成之后需要对问题进行分析修复。

 

故障演练平台的使用流程跟关机演练有些类似,都是要准备、演练进行、恢复、复盘:

 

① 演练准备

 

  • 制定演练目标

  • 选择APP code

  • 选择机器资源

  • 选择演练策略

  • 生成演练计划

  • 周知相关人员

 

② 演练进行

 

  • Agent装载

  • 故障注入

  • 用流量验证故障是否生效

 

流量来源主要分为两类:真实流量和模拟流量。我们可以通过自动化测试、人工case,也可以用流量copy,如果在线上进行演练就用线上真实的流量。同时,在演练时要做观察日志跟监控,如果影响了线上服务就需要及时终止。

 

③ 演练恢复

 

演练的恢复包括三种,一种是人工恢复,另一种是超时自动恢复,还有一种异常情就是人工触发恢复。如果发生核心告警就会触发这个应用,相当于演练的熔断,这也会自动触发恢复。恢复之后我们需要把Agent卸载掉。

 

④演练复盘

 

演练结束之后,我们需要把流水记录下来,进行问题总结,做好改进规划,同时商讨下次演练时间。

 

4)问题&解决思路

 

图片

 

接下来简单介绍一下强弱依赖演练遇到的问题,主要问题都集中在Chaosblade的Java Agent上

 

① 插件不足

 

这个Agent提供了一些开源组件的插件,比如 Java、Mysql、httpclient、dubbo等,但有很多公司的中间件都是自研的,包括一些开源的组件,那这一部分它就没法支持,可能需要自己再去按照规范进行开发。

 

我们在把一些开源的组件的插件开发完成之后也提交给了官方,同时,我们自己也开发了一些功能,一个是调用点支持, 在程序内部的整个调用链路上,通过调用函数签名在调用链路上进行匹配,做故障注入。也开发了基于Tracing 的context的匹配, 很多公司内部都有一个类似Skywalking的Tracing工具,Trace中可以携带一些信息,可以根据这些信息去做一些流量的筛选,这样话整个的故障注入的条件会更精细。

 

② 功能缺失

 

我们当时在测试的时候发现httpclient的没有完整支持delay场景,特别是异步的。这一点我们已经把它实现了,也已经提交到了官方。

 

③ Agent冲突

 

这一类问题不太好查。因为公司的接口自动化测试,有部分他用的是阿里开源的jvm-sandbox-repeator ,这个Agent跟Chaosblade Agent都是用jvm-sandbox作为底层,它们之间存在有命名空间冲突,导致不能同时attach成功。跟内部的tracing agent也有冲突,导致故障策略无法生效。

 

面对这样的情况,我们需要使用不同的命名空间。解决方案就是用3.0以上的jvm-sandbox,在启动的时候指定不同的命名空间。

 

还有一个问题,如果公司内部有自己研发的Java Agent,比如skywalking是全链路压测的这种Agent。如果基于ByteBuddy 开发,且两个一起使用,就有可能导致Chaosblade的Agent切不到。因为这个Agent它的主要原理就是做切面增强。故障是注入进去了,但没有生效。这是开源社区的一个issue (https://github.com/alibaba/arthas/issues/1141),但是原理跟解决方案都是类似的,如果大家遇到这个情况可以参考一下。

 

图片

 

还有一个问题:在java agent attach的时候,由于需要动态做字节码增强,导致cpu和load都会瞬时飙⾼。在后续的策略下发时依旧是平稳的。

 

对于这个问题,解决思路是:在attach之前先做流量摘除,开始之后再把流量切入进去。因为在真正的故障策略下发时,对 CPU、Load的影响很小。

 

之前我们遇到过一次演练完成了,而且 Agent也卸载了,但是过了一段时间却莫名挂掉的情况,至今也没有找到原因。所以如果为了保险起见,演练完成之后,我们可以做一次服务重启。

 

5) 容器化支持故障注入改造方案

 

图片

 

如图所示,在进行容器化支持故障注入改造方案时,我们比对了三种不同的方式,对它们的优劣势进行分析,最后选择了一种折中的方案。

 

在Chaosblade-operator上就是增加了几个简单的功能,包括 Agent的安装、卸载,还有 Java进程的attach。策略下发用的还是原来的方式,在POD上起一个端口,通过 HTTP来进行交互。这样一来,只需要把以前的Salt部分用operator来代替,后面的整个交互流程还是跟原来一样,改造成本会相对较小。

 

我们的改造主要是实现了关机以及Java部分的故障。但是因为在POD上做网络实验需要的权限比较大,安全组那边一直没有同意,所以我们目前还没有支持。

 

图片

 

这个是整个的一个架构图,从上到下有4层。第一层信息来源,我们这边是portal,就是一个应用管理平台,主要包含应用的信息、应用画像等;另外一个是依赖信息平台,也就是一些元数据的提供方。

 

接下来是故障演练系统,主要负责故障的演练编排。再往下是一些执行通道,包括Salt、Ops提供的API、Chaosblade-operator等,故障的真正注入主要是通过 HTTP接口来执行的。

 

下面是几种不同的资源。右边是提供一些通用能力的服务方,包括监控平台、线上巡检、权限系统以及IM。

 

三、收益&规划

 
 

1、故障演练收益

 

图片

 
图中列举了部分通过故障演练发现的风险类型,包括依赖处理不当、超时配置不合理、报警不合理等。右边则是我们达成的收益汇总。
 
 

2、故障演练和混沌工程

 

图片

 

故障演练是混沌工程的工程实践。简单地来说,故障演练是混沌工程的一部分,但是它的能力相对更加底层。

 

故障演练更多是以测试验证为主,但混沌工程它是通过实验去发现系统问题;混沌工程更强调在线上做随机化演练,而故障演练虽然也有在线上,但更多的还是先从测试环境验证开始;混沌工程更多强调形成文化,跟Devops流程结合,故障演练目前还是以定期组织为主。

 

 

3、近期规划

 

接下来分享一下我们近期的规划,主要包括4个方向:

 

1)强制依赖自动标记

 

结合自动化接口测试平台,通过基准环境和故障环境请求结果对比,结合策略命中信息,强弱关系预判辅助用户决策。

 

2)线上自动化随机演练

 

基于故障注入能力以及强弱依赖关系,加上tracing信息,针对访问链路上的应用进行自动计算爆炸半径并生成自动化演练。

 

3)参与开源共建

 

新功能的添加以及Bug修复已经回馈给开源版本,我们组已经有2位Chaosblade的Commitor,并且和Chaosblade社区有过深度交流,保持合作关系。

 

4)使用体验

 

  • 增加可观测性,减少问题排查时间;

  • 演练参数固化,减少用户准备操作。

 

以上就是我分享的全部内容,大家如果有什么想法,欢迎在评论区提出~

 

>>>>

 

Q&A

 

Q1:服务依赖的故障会对业务影响很大,怎么获取服务质量的强弱关系图?

A1:这部分文章中有提到,这部分的信息主要有两个来源,一个是日志,另外一个是服务的注册中心以及DB的元信息。但通过这几个途径只能获得服务间的依赖关系,强弱目前还是依赖人工去做标记。

 

Q2:混沌工程直接上生产太过冒险,有没有比较温和的从线下走到生产的方式?

A2:我们的演练目前既可以支持测试环境,也能够支持仿真环境和线上环境。现在的业务也是按照一定的顺序,先从测试环境开始验证,然后到仿真,然后再到线上。而注入的流量也是如此,比如一开始是人工触发的流量,后面就是用线上拷贝流量,再后面就用真实流量。都是需要循序渐进来做的。

 

Q3:怎么实现全链路精准故障注入?

A3:我们对Chaosblade的Java Agent做了一个开发,根据Trace当中的信息来做流量匹配。因为我们这边C端来的流量都会有一个标识,通过这个标识我们可以知道:它是从哪一个外网入口进来的。所以,通过Trace中的信息做匹配,就相当于做了一个流量筛选,这样的话就能实现精准注入。

 

Q4:在故障注入后,要如何判定系统的稳态是否被改变,怎么定义稳态?采用什么手段判定?

A4:我们现在主要还是依赖于监控告警和一个基于异常检测的平台。在注入故障之后,在这两个平台如果产生了故障信息,就可以认为它不正常。我们针对故障演练做了一个分组,相当于设置了一些特殊关注的监控指标。通过这两个平台的事件,我们来判定它是不是我们看稳态,目前是这样。

 

Q5:我们服务在k8s集群,有状态和无状态的服务都有,也是计划使用故障演练或混沌工程,建议从那入手?

A5:我认为应该先从无状态应用入手。因为我们现在做的演练基本上还是以无状态应用为主,无状态应用它会更简单一些,你可以把整个流程串起来,同时积累一些经验。有状态服务其实我们这边做的比较少,像中间件或者是DB这些团队,他们其实并没有用我们的演练平台,而是主要依赖于自己的一些测试手段来保证高可用。

 

Q6:做监控和可观测性有什么不同?

A6:监控它只是一部分,像监控、日志和Tracing等,而可观测性包括的范围更广。

 

Q7:开发人员如何部署混沌工程实验策略?

A7:混沌工程的实现需要从上到下的支持和配合,如果只是想自己或者用一个小团队来做的话,可能会非常难实现。但比如说Chaosblade的Agent本身能力比较强,其实你只需要用它一部分功能做一些简单的自动化,也可以实现故障注入的测试验证。

获取本期PPT,请添加群秘微信号:dbachen

点这里回看本期直播

最新评论
访客 2024年04月08日

如果字段的最大可能长度超过255字节,那么长度值可能…

访客 2024年03月04日

只能说作者太用心了,优秀

访客 2024年02月23日

感谢详解

访客 2024年02月20日

一般干个7-8年(即30岁左右),能做到年入40w-50w;有…

访客 2023年08月20日

230721

活动预告