微信支付是国家关键信息基础设施,服务千万商户和亿级用户,可用性要求高于5个9。
为了分析影响可用性的因素,我们对2018 ~ 2021近几年微信支付的故障复盘数据分类,发现软硬件异常导致的故障占比较高(如下图的基础设施、组件、上下游部分)。
各类引发故障的因素,微信支付采取了相应的应对措施,如
-
功能/程序缺陷:单元测试,集成测试,UAT 测试,流量回放;
-
-
人为破坏(人因):去登陆 IDC,变更审核,权限控制,模块认证;
但对于软硬件故障,虽然基础组件会涉及一些容错处理,但设计是否全面、开发是否完全实现、真实场景是否有效,没有统一的工具和验收标准,并推动解决。
如何检验系统具备应对软硬件异常的容灾能力,我们调研了公司和业界的方法,其中演习和混沌工程最为常见和有效。它们都强调在线上实施,目的都是提高系统的可靠性和弹性,但方法和侧重点有所不同(区别两者概念和思路,对实际落地影响较大)。
演习通常是有
明确的执行计划和预期
,要求注入故障的影响已知且明确。
更关注人的反应和应急响应计划,还包括非系统因素都在考量范围。比如验证某个系统失效后的,人为干预流程、机制是否顺畅等。
(公司和业界有非常多的关于混沌工程的概念介绍,如不了解可先自行搜索,不再赘述。)
混沌工程在目标上更强调
发现未知的风险
,更关注系统的弹性,不涉及系统外的因素。其中 Netflix 提出的混沌工程五大原则是业界落地实践的普遍共识:
其中多样化的事件和控制爆炸半径的方法,相对演习有明显差异。也正是控制了爆炸半径,混沌工程里注入故障的强度、复杂度远高于演习能力,能更全面、真实的模拟现网故障,以达到”发现未知风险“的目的。
此前,演习是微信支付容灾验证的重要手段,我们发现演习因为没有系统的爆炸半径控制方法,所以从工具开发、监控稳态识别、事件的多样性、自动化等方面,演习都需要较多的定制开发和人工实时观察,人力投入成本较高。
因此,引入混沌工程体系,通过有效的控制爆炸半径,进而更大幅度的注入异常并适当建设自动化能力,来识别系统薄弱点并推动持续治理来提高可用性,是当前值得探索的方向。
为了贴近真实环境,注入故障离真实环境越近越有效,但这又势必影响业务可用性。且微信支付对可用性要求高,不接受不可预知的风险。如果没有解决业务安全问题,就引入混沌工程“探索未知风险”的期望就较难落地。
在2021年,微信商业支付做了多分区改造,将业务流量路由到不同分区,分区间相互独立、互不影响,分区间环境一致性通过 DevO
ps 部署保证。在分区隔离的 IDC 环境里注入故障,调配可控的流量,既能贴近生产环境,又能确保故障收敛、控制爆炸半径。这样,微信商户支付落地混沌工程已具备条件和可行性。
从业界落地混沌工程经验来看,基本是围绕 Netflix 提出的5大原则展开。
建立稳定状态的假设,即从用户角度衡量业务健康度,一般业务都有现成的业务监控,大多情况无需额外开发。
用多样的现实世界事件做验证 即 丰富注入故障的类型,自动化实验以持续运行 即 降低人工参与成本,两者都需要额外开发,但先开发哪些、做到什么程度,可结合业务目标,更多的是开发人力成本和可挖掘到的风险收益之间 ROI 考量。
在生产环境中进行实验和控制最小化爆炸半径,是混沌工程的有效性和安全性的两个关键问题。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4. 独立部署线上无状态服务,和线上共用组件与存储
|
|
|
|
|
|
|
|
|
|
|
|
一般来说,有效性和安全性很难同时满足,而且是此消彼长的关系,越是希望真实还原故障,就需要在有业务流量的真实环境中模拟故障,这又会影响业务安全;但若一味追求安全,不能对业务有任务影响,就无法还原故障发生的真实场景,又很难发现问题。可结合实验目标,确定业务能接受最低限度的安全性,来选择有效性最高的方案。
爆炸半径的方案选择,在混沌工程实施的不同阶段,也是可以调整的。对系统可用性的信心越足,越是可以贴近真实环境,因此前期可以在有效性低的环境中实验,当实验手段很难发现新问题且发现的问题都已解决时,可以考虑升级到有效性更高的方案。
对微信商业支付,如前文介绍在2021年已经进行了多分区改造,因此我们在多分区架构上,单独规划了分混沌分区路由和4个业务分区,部署的业务逻辑模块、存储、组件等都与生产分区完全一致,前期放入仿真流量到混沌分区后注入故障,当对系统具备足够的信心后,后期在生产分区注入故障。
由于多分区仅对核心业务做了改造,仍存在外部依赖服务,为了防止混沌实验对外部依赖的影响,对不同场景的风险源做了相应处理:
考虑”数据篡改“类的故障,可能会导致写脏数据,尤其是篡改业务执行主体(如商户 ID、用户 ID)写脏真实业务数据的风险,因此禁止”篡改执行主体“的故障。
考虑执行的实验的风险,我们对这些场景加了审批:
我们主要在隔离的混沌分区实验,实验环境与真实环境一致性越高,真实环境出现类似的问题概率就越大。
维度
|
一致性需求
|
应对方案
|
地域
|
高
|
同地部署
|
园区
|
中
|
多园区部署,随机选择园区机器实验
|
机房/交换机/机架
|
低
|
对上层相对不透明,且可实验空间较小(爆炸半径过大),项目未要求
|
机型
|
中
|
较少涉及此类差异的故障,线上基本一致,项目未要求
|
操作系统
|
中
|
容器
|
高
|
一致的容器版本,一致的容器规格
|
二进制(代码)
|
极高
|
统一部署,完全一致
|
配置
|
极高
|
我们将上述高一致性要求的地域、园区、容器、二进制、配置,做到 DevOps 部署工具中,上线模块、发布变更时做到与投产环境一致。
实验流量与线上接近的目的,一是还原机器/容器负载情况,二是还原业务执行的分支,两者在某些复杂组合场景下,是否会触发未知风险。我们从两个维度来评价流量仿真程度:
流量指标
|
基本要求
|
更高要求
|
场景覆盖率
|
核心链路
|
覆盖线上所有依赖服务接口
|
请求并发度
|
平均或峰值 TPS
|
1. 贴近线上各类场景的流量比
例
2. 贴近线上随时间波动的流量曲线
|
流量类型
|
建设方法
|
优点
|
人造流量
|
基于业务用例与规则,开发执行脚本
|
1. 开发简
单
2. 流量可控性强
|
流量回放
|
线上录制,替换并重建资料类数据,mock 边界调用,mock 随机数、时间等系统调用等
|
1. 流量与线上一致性强、丰富度
高
2. 后期新增场景的投入成本低
|
考
虑前期建设成本和不确定性,我们选择了人造流量方案。
从0到1建设混沌工程系统和业务落地,我们遇到了这两类问题:
我们选择从业务目标出发,优先看高价值的风险点,工具配合业务目标逐步丰富故障原子。根据不同阶段的侧重点,将项目的实施路径拆分两个阶段:
前期,人力、工具有限的条件下,如何发掘高价值风险(高 ROI)?
较简单的方法是:随机注入。工具团队可根据近几年的故障类型,按照优先级开发出对应的故障原子,无差别的注入到目标模块。当前期自动注入、稳态分析不够健全的情况下,仍需人工参与,这种方法有2个局限:
-
实施的业务团队人力投入大:人工注入故障,人工观察、分析业务监控,以及监控偏离时是否影响业务、是否会扩散风险、是否有其他潜在风险;
-
工具团队不能做到有的放矢,不能将有效的人力投入到最有价值的原子开发上来。
为了快速找到高价值风险,进而提升大家参与的积极性,我们选了如下2个策略:
-
实验对象从 高价值 往 低价值 覆盖。根据这个思路,得到了优先实施的模块。1)公共组件:RPC 框架、队列、存储 、缓存,安全认证、日志等;2)生命线业务:收付结退。
-
基于高可用原则拆解,得到工具待开发的原子需求和待注入的资源。
设计路径如上图,根据历史故障和常用的高可用要求原则,假设某个组件/业务满足该高可用原则,根据该原则拆解到需注入的故障原子和注入范围(靶点),某个业务或组件待实施的混沌实验计划如下图:
通过这些策略,我们快速的找到 HTTP 接入模块、事件中心、跨城组件等微信基础组件的多个高风险的共性问题。
上述方法,只能覆盖到有限的故障域,而实际故障往往是由多个事件同时发生而触发,因此后期加强单原子无差别的注入和组合故障的注入可以发现更多中长尾问题。
对于组合故障,如果也是无差别策略,那么组合空间极大,就以微信支付委托代扣业务为例:
-
单故障注入:18模块 * 15个故障原子 = 270个,即单原子无差别的注入次数;
-
2个组合空间为 C2270 = 36315,任一组合空间为2270。
即使工具已经具备自动注入、自动分析的能力,实验周期和消耗资源也将会很高,因此合理的剪枝很有必要,可采用基于相关性由强到弱的多故障组合,如:
从实验流程来看,除了“修复问题”,另外三个流程可以做到一定程度的自动化。
一个实验任务由4个部分组成:
-
业务资产:集群、模块、接口;集群是多个模块的总称,如某个存储集群包括接入模块、存储模块、元数据模块;
-
系统资源:业务资产执行所依赖的机器、容器、网络、CPU、磁盘、进程、文件、内存、共享内存等;
-
故障类型:针对每种系统资源的有多种故障,如机器包括机器重启、机器时间错误等,如网络包括慢、丢包、乱序等。
-
故障程度:除个别的像机器重启这类0-1类型故障,其它大多数涉及一定程度的故障,比如网络丢包30%,不同程度下的表现略有不同,一般将故障程度拆解成几档,如丢包有单机内10%、30%、50%、80%、100%,再组合多机情况。
一个实验,首先圈出所包含的业务资产,再整理出其依赖的系统资源(除了磁盘、文件、共享内存这几项,其它一般都包含),再展开系统资源的故障类型,最后确定故障程度,有两种策略:
因此业务需要做的是:圈定资产范围及依赖的系统资源,并标记资产类型或适用于哪些高可用原则,便可自动生成实验任务。
自动执行
系统建设不是一蹴而就的,从手工执行到自动化我们经历了三个阶段: