没出过事,不等于系统稳
一个系统跑了三年没出过大事,团队就容易产生一种错觉:它很稳。但真相往往是——不是系统扛得住故障,而是故障还没真的来过。数据库从主从切换演练过吗?机房断网能自动切走吗?依赖的第三方接口挂了,我们的服务是优雅降级还是跟着雪崩?这些问题,靠「平时没出事」是永远答不上来的。
混沌工程就是主动把这些故障提前、小范围、可控地注入到系统里,看它到底扛不扛得住。Netflix 当年搞出这套方法,背景正是自家系统被一次 AWS 宕机打醒——与其等真实故障教你做人,不如自己先在家小规模试一遍。
监控告诉你系统现在怎么样;混沌工程主动问一句——如果现在突然坏一下,会发生什么?
- 假设演练前先写清楚「我赌系统在这种故障下不会挂」,然后去验证。
- 故障注入杀 Pod、断网络、打满 CPU、延迟依赖调用,都是可以做的实验。
- 爆炸半径一开始只影响一小部分流量或一台机器,绝不一上来全量。
- 复盘演练不是为了搞垮谁,是为了把暴露的弱点排进改进清单。
混沌工程的核心原则
混沌工程不是「在生产环境搞破坏找刺激」,它是一套有纪律的实验方法。在动手之前,先记住这几条原则:
第一,先定义「正常」长什么样。你得先有一条基线:平时成功率多少、P99 延迟多少、用户在做什么。没有基线,你根本看不出注入故障后系统是变好了还是变坏了。第二,从最小的爆炸半径开始——先拿一台机器、1% 的流量开刀,确认可控再扩大。第三,能随时叫停。每一次演练都要有明确的中止条件和一键回滚开关,一旦影响超出预期立刻停手。
第四,在非生产环境先演练,再谨慎上生产。很多团队直接拿测试环境练手,把流程跑顺、把应急预案验熟,才敢在生产用极小流量做真实演练。
| 原则 | 反面做法 |
|---|---|
| 先定基线再实验 | 不知道平时什么样,凭感觉判断 |
| 从小爆炸半径开始 | 一上来就把整个集群打挂 |
| 随时可中止 | 注入了故障却拔不掉,干等它扩散 |
| 非生产先演练 | 第一次演练就直接在生产全量 |
故障注入:从最简单的开始
「故障注入」听起来很玄,其实日常能做的实验就那么几类,先从最简单的入手:
# 最简单的一类:随机杀掉几个 Pod
# 看 Deployment 能不能自动补回来、用户有没有感知
kubectl delete pod -l app=web --all --grace-period=1
# 给某个依赖接口人为加 3 秒延迟
# 看超时、重试、熔断有没有按预期生效
# 把某台机器的网卡带宽限到 1Mbps
# 看跨机房调用会不会雪崩
# 模拟一次数据库主库不可用
# 看是否自动切到从库、切的过程用户感知多久
这些实验背后要验证的,全是你平时嘴上说「我们做了高可用」的那些配置:健康检查到底摘没摘节点?熔断到底断没断请求?故障切换到底切过去了没有?很多团队第一次演练就翻车——发现自己配了半年的高可用,其实切换脚本从来没真跑通过。这正是演练的价值:让你在凌晨三点的真实事故之前,先在工作日的下午发现这件事。
爆炸半径:把影响圈住
混沌演练最大的风险,是「玩脱了」。控制风险的核心概念叫爆炸半径——这次故障注入,最多能影响到多少用户、多少机器、多大流量。好的演练从设计上就把它圈死:
比如在多副本服务里,一次只杀 1 个 Pod 而不是全部;比如在灰度环境里做,而不是用户流量的主集群;比如挑业务低谷时段,而不是大促零点。每一次实验开始前,都要能一句话说清:「这次最坏情况下,受影响的是 X 台机器、Y% 的请求,而且 5 分钟内能恢复。」说不清这句话,就还没准备好开始。
配套还要有一个明确的中止条件写在演练预案里,比如「错误率超过 5% 立刻停止并回滚」「P99 延迟超过 2 秒立刻停止」。这个阈值要提前定,不能出事的时候才现场讨论——现场争论要不要停,是演练里最危险的时刻。
从被动告警到主动韧性
把前面几篇串起来看,运维的成熟度其实是一条递进的路:先是被动告警——出了事才有人响应;再是监控可观测——能第一时间看到出了什么事;再是高可用架构——一部分故障系统自己就能扛过去;混沌工程是最后一步:主动去验证「这套高可用,是不是真的高可用」。
这四步缺了前面的,混沌工程也跑不起来。你连系统现在健不健康都看不清(没有监控),连故障来了能不能自愈都没配置(没有高可用),那主动注入故障只会变成主动制造事故。所以一个务实的顺序是:先把监控和高可用补齐,再开始做小范围的混沌演练,把演练反过来当成对前面工作的验收。
慢慢你会发现,混沌演练暴露的问题往往不是「机器扛不住」,而是「流程扛不住」:告警没人接、值班联系不上人、切换手册写了没人看过。这些问题,平时单靠看监控是永远发现不了的。
演练复盘:把事故变成改进
演练本身只是手段,真正的产出是演练之后的复盘和改进项。一次有价值的混沌演练,结束后应该留下这么一份记录:
演练记录模板:
- 假设:杀掉 1 个 web Pod,成功率应不受影响
- 实际:成功率从 99.9% 掉到 97%,持续了 40 秒
- 发现:健康检查摘节点慢了,40 秒里还在往坏 Pod 发请求
- 改进项:把就绪探针的失败阈值从 3 次调到 1 次,下次再验
- 负责人 / 截止时间:张三,两周内
复盘的关键纪律:对事不对人。演练暴露了问题是好事,不是谁的锅——否则下次没人敢在演练里「搞破坏」了,演练就变成了走过场。每一条暴露出来的弱点,都要落到具体的人和改进时间,进 backlog 跟踪。一个不产出改进项的演练,等于白做。定期重复同一类演练,还能验证之前的改进是不是真的生效了——这就是「韧性」一点点长出来的方式。
第一次混沌演练怎么开场
对一个从没做过演练的团队,别一上来就搞什么「整个机房断电」的大场面。推荐按这个最小顺序开场:
第一次(测试环境):
随便杀一个测试 Pod,看 Deployment 能不能自动拉回来
目标:把演练流程、观察面板、中止按钮先跑通
第二次(测试环境):
把某个测试依赖接口人为加延迟,看超时和重试逻辑
第三次(生产 1% 灰度):
挑业务低谷,杀生产里 1 个 Pod,全程有人盯监控、随时准备停
记住几个新手最容易犯的错:第一,没写清楚假设就动手——你连自己想验证什么都不知道,演练完也不知道是成功还是失败。第二,没有中止预案——真出事时手忙脚乱。第三,演练完不复盘、不改进——图个热闹,下次该什么样还什么样。
说到底,混沌工程不是一门高深的技术,它是一种态度:不相信「没出事」等于「很稳」,主动把故障请到家里来小规模演练。系统的韧性,从来不是设计出来的,是一次次被小故障打、又一次次自己爬起来,慢慢长出来的。