Hike News
Hike News

混沌工程

为什么需要混沌工程

减少故障的最好方法就是让故障经常性的发生。通过不断重复失败过程,持续提升系统的容错和弹性能力。

在分布式架构环境下,服务间的依赖日益复杂,没有人能说清单个故障对整个系统的影响,构建一个高可用的分布式系统面临着很大挑战。

分布式系统天生包含大量的交互、依赖点,可以出错的地方数不胜数。硬盘故障、网络不通、流量激增压垮某些组件,这都是每天要面临的常事儿,处理不好就会导致业务停滞,性能低下,或者是其他各种无法预期的异常行为。

在复杂的分布式系统中,人力并不能够阻止这些故障的发生,我们应该致力于在这些异常行为被触发之前,尽可能多地识别出会导致这些异常的,在系统中脆弱的、易出故障的环节。 在线上事故出现之前, 通过观察分布式系统在受控的故障注入测试中的行为变化,提前识别出系统中有哪些弱点以及这些弱点的影响范围。 当我们识别出这些风险,我们就可以有针对性地进行加固、防范, 提高系统可靠性,建立系统抵御失控条件的能力,从而避免故障发生时所带来的严重后果。

相比等待故障发生然后被动式的故障响应流程,混沌工程可以在可控范围或环境下对系统注入各种故障,持续提升分布式系统的容错和弹性能力,从而构建高可用的分布式系统。

怎么样实践混沌工程

实践原则

完善的监控系统

如果没有对系统的可见能力,就无法从实验中得出有效的结论。

if you can't measure it you can't improve it.

尽可能的贴近生产

(流量、数据量、配置、架构等)越贴近生产环境演练效果越好。

演练事件真的可能发生

  • 故障类:服务器异常、断网等硬件故障,外部服务不可用,人工误操作等;
  • 非故障类:流量激增、 伸缩事件;

可以分析曾经引起生产系统故障的事件,针对性的排列优先级并实施,避免故障重现。

让故障演练成为常态

服务迭代频繁,系统不断变化,需要经常性的演练才能覆盖到系统的变更。

混沌工程不是制造问题,而是揭示问题。

最小化影响范围

混沌工程可能导致线上功能不可用,所以在以找出系统弱点为目标的前提下,需要最小化故障影响范围,并且当出现问题时可以迅速恢复,即故障是可控的。

演练方案

先测试后生产

演练计划先在测试环境验证,故障可控的前提下再到生产环境实施。

先次要后主要

先在边缘系统进行故障演练,再对在线业务实施故障演练。

跟踪演练计划

记录每次演练的故障对象、故障场景、期望状态、实际状态、影响范围、修复方案。

  1. 如果故障对象对该故障场景的实际状态和期望状态一致,则可以认为该故障对象对这种故障是高可用的。
  2. 如果故障对象对该故障场景的实际状态和期望状态不一致,那就是一个系统弱点,需要制定方案来修复,修复后继续演练来验证。

如果不能准确的观察结果和深入分析原因并得出修复方案,那就只是在搞破坏,而不是在做混沌工程。

演练目标

提升系统的容错和弹性能力,达到小的故障不需要人工介入,大故障人工介入可以快速恢复的目的。

  • 不需要人工介入的故障:做成不定时不定目标不定异常类型的自动化实现,常态化执行。
  • 需要人工介入的故障:告警及时、准确,监控数据清晰、有效,定位问题迅速,预案完善。

阶段一

在测试环境制造故障,观察服务状态和监控数据,评估系统可靠性,寻找系统弱点。

建立查找问题-解决问题-验证问题的循环,明确该问题是否需要人工介入。

对于需要人工介入的故障优化监控、告警,制定预案。

阶段二

将测试环境演练成功的计划放到生产环境执行验证。明确故障影响范围及解决时效,汇总实验数据。

  • 不需要人工介入的故障是否有对用户造成影响,自动解决的时间多久。
  • 需要人工介入的故障影响范围及问题解决时长。

持续提高团队对故障的检测、响应、处理还有恢复能力。

阶段三

建设全链路压测来模拟流量激增现象,建设A/B测试来最小化影响范围,增强混沌工程演练能力。

阶段四

建设故障演练平台,自动化演练(启动执行、监控、终止、结果分析),常态化执行。

实施步骤

  1. 首先,用系统在正常行为下的一些可测量的输出来定义“稳定状态”。
  2. 其次,假设系统在控制组和实验组都会继续保持稳定状态。
  3. 然后,在实验组中引入反映真实世界事件的变量,如服务器崩溃、硬盘故障、网络连接断开等。
  4. 最后,通过控制组和实验组之间的状态差异来反驳稳定状态的假说。

img

破坏稳态的难度越大,我们对系统行为的信心就越强。如果发现了一个弱点,那么我们就有了一个改进目标。可以避免在系统规模化之后被放大。

系统弱点

以 A 调用 B,B 调用 C,A 同时也调用 D 举例,B1、B2 是 B 服务的多个实例,依次类推。

受上游服务影响

被上游服务的高并发压垮

请求限流

场景模拟:高并发请求B1。

预期方案:B1设有最大并发量,达到最大并发量后B1服务触发限流,新请求快速失败。

修复方案:添加限流能力。

受下游服务影响

下游服务异常导致本服务异常

失败重试

场景模拟:对B1注入故障,A服务调用到B1时会出现调用失败。

预期方案:A服务的请求路由到B2进行重试。

修复方案:添加失败检测和请求重试能力,B服务的接口需要有幂等性。

实例隔离

场景模拟:对B1注入宕机故障,A服务调用到B1时会出现调用失败。

预期方案:给定的失败时间或者给定的失败次数后,自动隔离或下线 B1 实例。

修复方案:添加服务质量检查,下线不可用的服务实例。

下游服务异常拖垮本服务

服务降级

场景模拟:对B所有实例注入故障,A服务调用B时都失败。

预期方案:调用本地降级方法。

修复方案:准备一个本地的fallback回调,返回一个默认值。

服务熔断

场景模拟:对B所有实例注入故障,A服务调用到B时都失败。

预期方案:给定的失败时间或者给定的失败次数后,A服务触发熔断,快速失败。

修复方案:添加熔断能力,下游服务不可用时能立即熔断,快速失败。

受中间件影响

中间件异常导致的程序逻辑出错

中间件异常导致的服务不可用

受第三方服务商影响

第三方服务不可用导致本服务功能不可用

场景模拟:A短信供应商不可用。

预期方案:切换B短信供应商。

修复方案:供应商备份。

第三方服务异常拖垮本服务

场景模拟:调用OSS服务响应慢。

预期方案:熔断第三方服务,暂不提供该服务。

修复方案:供应商备份。

故障场景

CPU异常

  • CPU高负载

内存异常

  • 内存高负载

磁盘异常

  • 磁盘高负载
  • 磁盘空间不足
  • 部分数据丢失
  • 全部数据丢失
  • 不可读
  • 不可写

网络异常

  • 瞬断
  • 延时
  • 丢包
  • 隔离
  • 外部服务不可达

进程异常

服务进程

  • Tomcat线程池打满
  • 异常关闭
  • 重启
  • 内存溢出

系统进程

  • 系统时间不同步
  • OOM kill

中间件

Redis

  • 节点故障
  • 重启
  • 批量删除
  • 数据丢失
  • 响应慢

MySQL

  • CPU升高(*2019-06-01)
  • IO升高
  • 节点故障
  • 数据丢失
  • 响应慢
  • 大量慢SQL
  • 连接池占满
  • 死锁
  • 不可写
  • 不可读
  • 数据同步延迟
  • 主备延迟

RocketMQ/Kafka

  • NameServer故障
  • Broker故障
  • 数据丢失
  • 消息推送延迟
  • 大量消息挤压

Apollo

  • ConfigService故障
  • AdminService故障
  • Portal故障
  • 数据丢失

XXL-Job

  • 调度器故障
  • 执行器故障

ElasticSearch

  • 节点故障
  • 内存溢出
  • 写请求飙高
  • 读请求飙高

DNS

  • 解析超时(*2019-10-01)
  • 解析错误

代码异常

  • 空指针

异常操作

  • 删除数据
  • 强杀进程

服务器异常

  • 宕机
  • 重启
  • 中木马
  • 修改系统配置

第三方服务不可用

  • 响应慢
  • 访问不了

数据中心故障

  • 断电
  • 断网

演练工具

chaosblade

GitHub:https://github.com/chaosblade-io/chaosblade

文档:https://chaosblade-io.gitbook.io/chaosblade-help-zh-cn/blade

chaosmonkey

GitHub:https://github.com/Netflix/chaosmonkey

pumba

GitHub:https://github.com/alexei-led/pumba

kube-monkey

GitHub:https://github.com/asobti/kube-monkey

参考

https://www.infoq.cn/article/jjp0c2bR4*Ulld0wb88r

https://tech.youzan.com/chaosmonkey/

https://www.cnblogs.com/tianqing/p/10499611.html