作者:​智妍(郑妍)、​浣碧(何颖)​

什么是混沌工程,云原生大潮下的混沌工程特点

通过使用云计算厂商如阿里云、AWS 等提供的服务,现代服务提供者得以用更低廉的成本,更稳定地进行丰富的软件服务提供。但是真的一切如此轻而易举吗?主流云计算厂商在 SLA 承诺的范围内,都各自出现过一些历史故障,可参见这份血淋淋的 github 上的报告列表 [1] 。另一方面,各个云产品提供给了用户使用的一些高可用能力,经常依然是需要用正确的姿势来配置和使用的。

混沌工程可以帮助业务系统服务提供者通过创建破坏性事件、观察系统和人员响应方式、针对优化改进这 3 个步骤来发现生产服务中脆弱的环节,并根据预期的 SLA 目标进行实施改进。除了指出需要改进的系统组件设计问题之外,混沌工程还可帮助发现需要监控和告警上的盲点、发现人员对系统理解、应急响应 SOP、排查能力上的不足,进而使得业务系统及其研发、运维人员整体的高可用能力水位大大上浮。因此 Netflix 提出此概念后,各大软件厂商纷纷进行了对内实践和对外产品提供。

云原生在传统云计算基础上,提供了更快更低成本的弹性,更好的软硬一体化灵活性,已经成为云计算发展最快的技术方向。云原生帮助开发者大幅度降低资源成本和交付成本,从而更快更好地赢得市场。同时,云原生也给传统运维、研发方式带来了彻底的变革,这就使得传统的混沌工程手段需要跟随演进。

云原生背景下,其上的应用服务的混沌工程实施和传统有什么不同呢?从我们在阿里电商、中间件云原生化的大量实践中,总结出以下主要差异:

在这样差异的背景下,用云原生的手段,实施更加针对植根于云原生应用的场景的混沌工程,是更加恰如其分,能够提供更多能力提升的。

混沌工程实施模式的阶段和发展

既然混沌工程能带来如此多的好处,一个基于云原生的应用服务或体系想要实践,要如何落地呢?

从演练工具和落地实施来看,一个组织的故障演练经常分为几个发展阶段:手工演练,流程工具自动化演练,常态化无人值守演练,生产突袭演练。

这几个阶段的实施难度是从低到高,当然相应的收益也是从低到高。一个组织(云用户)可以随着自己业务应用服务体量的增大、复杂化和高可用能力的增高的历程,根据实际情况需要选择自己合适的阶段,然后随之进行升级和发展。即使从最简便的手工演练开始做起实施,经常也能带来相当明显且长远的高可用能力系统性提升。

那么每个阶段分别有什么特点,又该如何选择呢

  • 手工演练: 一般在高可用能力建设初期阶段,或者一次性验收的情况下手工注入故障完成。通过人为查看告警是否生效,系统恢复情况来进行演练。在这个阶段只需要一些故障注入的小工具或者脚本,方便后续使用即可。
  • 自动化演练: 高可用能力建设到一定阶段后,往往会有定期检查高可用能力是否退化的需求,自动化演练开始排上日程。自动化演练步骤一般包括:环境准备 -> 故障注入 -> 检查 -> 环境恢复。在每个步骤中配置相应的脚本来形成演练流程,下一次就可以一键点击自动化执行了。
  • 常态化执行: 演练进行到下一阶段,我们会有更高的要求,希望演练可以自主混沌化执行,以无人值守的方式进行,这又对系统的高可用能力有了新的挑战。这要求系统有不仅有监控告警可以发现故障,也有对应的预案模块来负责恢复,而要做到无人值守,需要系统进行更智能精确的判断故障情况,自动执行相应预案。

  • 生产突袭: 以上演练大多在灰度环境进行,不会影响到业务,生产突袭则要求系统有能力在生产环境控制爆炸半径的前提下进行故障演练,以期发现一些业务相关、规模相关、配置相关、应急响应相关的,在灰度环境遗漏的部分,生产环境的演练对系统的要求较高,需要有一套执行规范,对系统的隔离能力也有较高要求。大多数的工作,能力建设都在灰度环境完成验证,但生产突袭仍作为一个有效且必要的演练手段,用更真实的场景给研发体感,让其真实执行预案,也锻炼了应急能力,对系统有更多信心和认知。

如何进行一次完整的故障演练实施

当应用首次使用 Kubernetes 进行应用部署和扩容时,最先关注的更多是功能是否可用,故障演练则是更高级别的要求,我们假设当前的系统已经初步通过了功能验收,但对于一些故障情况下系统的表现还未知的前提下,来开始我们的故障演练之旅。故障演练本身作为一种破坏性的操作,需要循序渐进,遵循一定的规范和流程来落地。下面我们从环境建设、系统能力分析、高可用能力建设、演练实施建议几个方面来介绍一下,一个首次在 Kubernetes 中部署起来的应用应该如何循序渐进的实施故障演练。

Step 1:隔离环境建设

故障演练,特别是首次执行之前,我们需要明确好当前注入故障的环境情况,是否可能影响到业务流量,是否会造成无法弥补的损失,在阿里内部,我们有复杂的环境隔离和变更管控,以防故障注入影响到业务流量。

在环境类别上,我们会区分为以下几类:

  • 业务测试环境:用来进行 e2e 测试,全面的功能验收,这个环境和有业务流量的生产网络是隔离的,从网络上避免了流量错误进入到其他环境,因此可以在这个环境上尽情的进行各种容错性测试。
  • 金丝雀环境:可以理解为是一种全面的链路灰度环境,这个环境有当前系统的所有组件,一般用来做上下游联调,系统内部的链路灰度使用,这个环境是没有实际业务流量的;
  • 安全生产灰度环境:这个环境我们会引入 1% 的生产流量,并提前建设了切流能力,一旦这个环境出现问题,可以把流量迅速切换到生产环境中,该环境一般用来结合用户流量做一段时间的灰度,以免全量发布导致的不可控;
  • 生产环境:真实用户流量的环境,这个环境的任何运维动作都需要进行严格的变更审核和前几个环境的灰度通过才能变更;

故障演练一般会开始在金丝雀环境引入,可以在全链路、无真实流量的环境中做一些高可用能力的建设和验收,常态执行的演练,在这个环境演练多次的场景,可定期在灰度环境和生产环境中、控制爆炸半径的前提下进行真实突袭,作为能力的验收。

一般情况下,考虑到成本投入和系统复杂度,业务应用可能不会建设 4 个隔离环境来循序渐进的推进,但我们推荐应用应该至少有两个环境来区分用户流量,环境上至少有一个和生产隔离的灰度环境,至少初期必须如此。环境建设中需要关注的问题如下:

  • 隔离性:灰度环境和生产环境尽量做到隔离,包括但不限于网络隔离,权限隔离,数据隔离等,考虑到一些容灾的能力,还可以将两个集群建设在不同地域的 Kubernetes 集群中。
  • 真实性:灰度环境和生产环境尽量保持一致,比如外部依赖,组件版本。

环境建设达标后,才具备了演练的准入条件。

Step 2:故障场景分析

在分析系统的高可用能力时,往往没有一个统一的答案,每个系统的薄弱点,瓶颈都不尽相同,但整理系统高可用能力时,我们可以提供一些通用的思路。

  • 历史故障:

历史故障通常是快速了解一个系统薄弱能力的教科书,通过分析历史故障,进行分类,可以快速得出当前系统那些组件更容易出现问题。

比如系统能力需要进行快速的弹性伸缩,伸缩失败可能影响业务流量,可以推断出它强依赖 Kubernetes 的扩缩容能力,需要监控关注此能力的可用性;比如系统数据读写频繁,历史出现过数据不一致问题,则可以考虑在数据层面进行稳定性建设,增加备份能力,回滚能力等。

  • 架构分析

系统的架构在一定程度上决定了这个系统的瓶颈,通过分析系统的依赖也可以更了解系统的边界,也更便于进行运维上的优化。

比如一个应用的部署方式是主备模式的,那必须要检查的能力就是主备切换是否顺畅,切换过程是否影响到业务流量;比如一个应用强依赖底层存储,一旦存储挂掉,业务会大面积故障,则在整理高可用能力的时候就需要想到存储挂掉后是否有降级方案,存储问题是否可以提前预警。

  • 社区经验:

很多系统的架构都是大同小异的,参考社区或友商的经验就像提前看了模拟考题,总会有意想不到的收获。我们总会在业界爆出一些故障时进行自我反思和重新整理,多次发现了自身的一些问题。网线被挖断、删库跑路等宝贵的经验库,都在我们定期演练的列表中。

在阿里云原生的架构上,我们整理了如下所示的演练模型供参考,在这个高可用能力模型中,我们根据系统架构按照管控层组件、元集群组件、扩展组件,数据存储,节点层,整体集群进行区分,在每个模块中有一些通用的故障可以互相借鉴。

Step 3:系统高可用能力建设

在实际进行故障注入前,我们还需要问自己几个问题。根据上述已经分析到的我们想让系统拥有的高可用能力列表,系统是否具备当这些故障来临时有敏捷的发现能力,人员有迅速的响应能力,系统本身是否具备自愈的能力或一些可用来在故障过程中使用快速恢复系统的工具呢?下面我们从发现能力和恢复能力两个方面来给一些通用的建议。

  • 发现能力

监控和告警是能够发现系统是否处于稳态并让应用负责人一目了然的方式。阿里内部团队建设了两种监控告警方式,一种是白盒告警,借助系统内部暴露出来的各种维度的可观测性数据的异常波动来发现潜在问题;一种是黑盒告警,从客户视角把系统当做黑盒,探测正向功能。

  • 恢复能力

故障来临后,最优的结果是系统稳定丝滑,毫无影响,这对系统的能力建设要求极高,而实际的情况往往更为复杂。在阿里内部的实践中,除了对系统本身专门建设了基本的进程自愈、切流能力、迁移能力、限流能力等,也建设了预案中心,中心化的沉淀我们所有的止损能力到系统中,白屏管理,接入,运行,根据专家经验建立止损能力集,作为故障时的重要工具。

Step 4:演练实施

以上步骤完成之后,我们认为系统已具备了初步的高可用能力,可以开始实施故障演练。

一般情况下首次演练我们会挑选一些核心场景进行,在预发或测试环境,工具上使用半自动化的脚本或仅包含故障注入模块的流水线来触发,在研发和运维人员在场的情况下进行首次试验。试验前确认场景的预期,比如故障注入后需要 1min 进行告警,10min 内系统自愈恢复,以便在演练过程中随时确认。演练执行后需要各部分人员进行人工确认系统表现是否符合预期,演练结束后及时恢复故障和环境。场景在演练过程中不符预期的部分,需要多次在此阶段不断验证和演练;符合预期的场景进行标记,可以开始进入到常态化演练阶段。

常态化演练阶段的关键词是混沌化、无人值守,Kubernetes 集群由于架构的优势,本身具备一定的自愈能力,因此更适合无人值守的演练。我们会筛选已经通过半自动演练的场景集合,组织为一些故障演练流水线,每个流水线中一般包含故障注入、监控检查、恢复检查、故障恢复等步骤,来闭环完成单个演练流程。同时阿里内部使用云原生技术进行混沌化触发,实现在演练对象、环境、时间、场景上的随机,使得这些演练场景可以混沌化、常态化、无人值守的执行。通过常态化故障演练,助于发现一些偶发性系统问题,并可以在系统升级过程中协助检查已有的高可用能力。

生产突袭的实施需要根据系统的架构情况进行,在阿里内部的实施中,一种控制风险的方式是选择流量低峰去进行,并提前预备一键切流预案,一旦出现故障无法恢复的情况,立即切流止损。其他突袭相关的风险控制设计我们会在后续的系列文章中详细分析。

结语

在内部云原生领域实施故障演练的过程中,我们分析了 200 多个演练场景,通过 1000+/月的频次进行常态化故障演练,有效的发现了 90 多个问题,避免了问题半径进一步扩大;通过演练流程的搭建、校验和混沌化执行,定期监测系统的告警和预案恢复能力,有效的拦截 50 多个新增高可用问题上线。生产环境的突袭演练是我们迈出的艰难但有力的一步,锻炼了研发运维人员的应急响应能力,在真实用户场景下锤炼系统,推进了产品的轮班制度,提升了云原生底座的稳定性和竞争力。

相关链接

[1] github 上的报告列表:​https://github.com/danluu/post-mortems

点击此处,前往故障演练 Chaos 主页查看更多详情!

03-05 22:48