
我们精心设计的架构图都是谎言。
虽然不是故意的,但它们确实是谎言。它们展示了干净的方框和箭头,描绘了一个服务始终响应、网络永不分区、数据库从不锁定的世界。
但现实系统更加混乱。你的认证服务最终会有糟糕的部署。你依赖的第三方API会毫无预警地对你进行速率限制。网络交换机会以某种方式出现故障,导致部分连接。当这些事情发生时,它们会组合发生,产生你从未想象过的故障模式。
传统测试给了我们虚假的自信。单元测试验证各个组件是否正常工作。集成测试检查服务之间是否可以通信。负载测试证明我们可以处理流量。但这些都没有告诉我们当生产环境中出现问题时会发生什么。
我见过拥有95%测试覆盖率的系统在单个Redis实例变得不可用时而完全崩溃。所有测试都通过了,因为它们从未测试过真正重要的故障场景。
维度 | 传统测试 | 混沌工程 |
|---|---|---|
Focus(关注点) | 验证已知场景 | 发现未知弱点 |
Environment(环境) | 受控的测试环境 | 生产环境或类生产环境 |
Timing(时机) | 部署之前 | 在生产环境中持续进行 |
Goal(目标) | 防止 Bug 上线 | 建立对系统弹性的信心 |
Scope(范围) | 组件或服务级别 | 跨服务、系统级 |
传统测试与混沌工程的对比。
混沌工程不是事后才想到要添加到系统中的东西。它是一种设计哲学,影响你做出的每一个架构决策。以下是应该指导你设计的五个核心原则:
当你绘制从服务A到服务B的箭头时,问问自己"当服务B宕机时会发生什么?当它变慢时会发生什么?当它返回垃圾数据时会发生什么?"
你无法修复你看不见的东西。混沌工程中的可观测性超越了标准指标。你需要了解不仅是什么失败了,而且是故障如何在你的系统中传播的。
强一致性是可用性的敌人。CAP定理不仅仅是学术理论;它是一个基本约束。大多数声称需要强一致性的业务需求实际上只需要有明确解决策略的有界不一致性。
静态容量规划在现实世界中会失败。你的系统需要根据当前条件自动调整,特别是在部分故障期间。
最好的运行手册是你从不手动执行的那些。在设计系统时构建修复步骤。
你不会在一夜之间从零到全面混沌工程。以下是一个映射到实际组织能力的实用成熟度模型:
阶段 | 特征 | 关键活动 |
|---|---|---|
Level 1 Reactive被动响应 | 事故来了才处理,无系统性预防 | 事后复盘、基础监控、手工运行手册 |
Level 2 Proactive Testing主动测试 | 在测试环境做简单故障注入,限定业务低峰 | 每周“游戏日”、随机杀实例、超时模拟 |
Level 3 Automated Chaos自动化混沌 | 生产环境定期实验,有明确假设与验收标准 | 每日自动混沌、网络延迟注入、资源耗尽测试 |
Level 4 Continuous Verification持续验证 | 7×24 持续注入,业务高峰也敢玩,结果自动校验 | 多区域故障转移、依赖项混沌、24×7 实验 |
Level 5 Chaos as Culture混沌即文化 | 混沌嵌入整个 SDLC,设计阶段默认考虑失败 | CI/CD 流水线集成混沌、自动爆炸半径探测、预测性故障建模 |
以船体中的隔舱命名,舱壁防止一个部分的故障级联到各处。关键见解是资源隔离。
舱壁增加约20%的资源开销,但防止整个系统故障。
这是最危险的故障模式之一:服务短暂宕机,数千个客户端立即重试,当服务恢复时,它立即被淹没并再次崩溃。
重试风暴预防的示例。
真正的弹性意味着在区域故障中幸存。以下是实用方法:
不要随意破坏东西。形成一个可证伪的、具体的假设。示例:
要跟踪的关键指标:
在团队观看的工作时间运行。如果你只在流量低时测试,你不会了解系统在真实条件下的行为。
真正的学习发生在分析中。记录:
关键见解: 失败的实验通常最有价值,因为它们揭示了盲点。
假设: 应用程序可以在没有面向用户错误的情况下处理数据库重启。
现实: 连接池检测到故障并积极重试。在30秒内,所有数据库连接都耗尽。数据库恢复,但立即被淹没。响应时间飙升到60多秒。
教训: 连接池重试需要带抖动的指数退避。实现渐进式连接池预热:从1个连接开始,每5秒翻倍直到达到目标。
假设: 当推荐服务变慢时,产品页面将优雅降级。
现实: 每次尝试3秒超时,但重试逻辑尝试了三次。总延迟:9+秒。应用服务器开始排队请求。在2分钟内,整个应用实际上宕机。
修复: 跨所有重试的总超时预算。3次连续超时后熔断器。非关键功能的单独请求队列。当队列深度超过阈值时,负载削减返回缓存数据。
类似于错误预算,混沌预算是你的系统应该容忍多少混乱的可量化度量。如果你有99.9%正常运行时间的SLA,你每月有43分钟的可接受宕机时间。你的混沌预算是这部分的一部分。
类别 | 时间配额 | 用途 |
|---|---|---|
总错误预算 | 43 分钟 | 可接受的总宕机时间 |
混沌预算 | 10 分钟(25%) | 专门用于混沌实验 |
实例故障测试 | 3 分钟 | 每周终止实例 |
网络延迟测试 | 2 分钟 | 注入依赖延迟 |
资源耗尽测试 | 2 分钟 | CPU/内存压力测试 |
区域故障转移 | 3 分钟 | 月度区域失效模拟 |
预算分配框架:
生产具有仅在实际负载下出现的涌现行为。从非常有限的爆炸半径(0.1%流量)开始在生产环境中测试。
在开始之前定义阈值:错误率阈值、延迟阈值、影响阈值和最大实验持续时间。
混沌工程也关乎组织弹性。运行手册是否可访问?团队成员能否找到合适的人进行上报?游戏日同时测试系统和协调。
从用户体验反向推导。跨调用链分配超时预算。将剩余预算作为头部传递。当没有时间剩余时快速失败。
你的系统不断变化。安排定期实验(最少每周)。自动化常见实验。在CI/CD管道中包含混沌。
故障不是敌人。对故障的无知才是。每个复杂系统都会失败。你的数据库会宕机。你的云提供商会出问题。依赖会返回错误。这些不是可能性;它们是必然性。问题不是故障是否会发生,而是当它发生时你是否准备好。
混沌工程让你从希望你的系统有弹性转变为证明它有弹性。它将架构从静态练习转变为持续验证的动态实践。
从小开始。本周运行你的第一个实验。杀死一个实例,看看会发生什么。记录你学到的东西。下周再用不同的东西做一次。随着时间的推移,你将建立系统弹性和组织信心。
在生产中幸存的系统不是那些从不失败的系统。而是那些优雅失败、快速恢复并从每次失败中学习的系统。构建拥抱混沌的系统,你晚上会睡得更好。 翻译:https://dzone.com/articles/chaos-engineering-for-architects-designing-systems