没出过事,不等于系统稳

一个系统跑了三年没出过大事,团队就容易产生一种错觉:它很稳。但真相往往是——不是系统扛得住故障,而是故障还没真的来过。数据库从主从切换演练过吗?机房断网能自动切走吗?依赖的第三方接口挂了,我们的服务是优雅降级还是跟着雪崩?这些问题,靠「平时没出事」是永远答不上来的。

混沌工程就是主动把这些故障提前、小范围、可控地注入到系统里,看它到底扛不扛得住。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 秒立刻停止」。这个阈值要提前定,不能出事的时候才现场讨论——现场争论要不要停,是演练里最危险的时刻。