混沌工程踩坑记录
跑完之后发现一个问题:我们的支付网关没有做连接池预热。
新 Pod 起来后,第一个请求就打到数据库,导致数据库 CPU 瞬间飙升,影响了其他服务。
我为什么开始搞混沌工程
去年我们在做一个电商结算系统,里面有用户中心、订单服务、库存中心、支付网关、物流查询等五个微服务。测试环境里一直跑得很顺,但一到线上,偶尔会出现订单创建失败、库存扣减不一致等问题。
排来排去,大多是超时、重试、降级没做好。但每次出问题都是事后诸葛亮,除非你能把网络故障、服务停机、资源耗尽这些事提前暴露出来。
当时的想法很简单:不如在测试环境里主动把这些故障都触发一遍。
先试了一下 Chaos Mesh,这是 PingCAP 开源的一个混沌工程平台,对 Kubernetes 环境支持得很好。
Chaos Mesh 的安装和基本配置
我们用的是 Kubernetes 1.24,先拉 Helm Chart:
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
helm install chaos-mesh chaos-mesh/chaos-mesh --namespace=chaos-testing --create-namespace --version 2.5.1
装完之后会有一组 CRD 和几个 Controller,可以通过 Dashboard 管理,也可以直接写 YAML 定义故障。
第一个实验很简单:让订单服务的某些请求延迟两秒,看看会不会触发下游重试风暴。
第一个故障注入:网络延迟
假设订单服务叫 order-service,在 business namespace 里:
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: order-service-latency
namespace: business
spec:
action: delay
mode: one
selector:
namespaces:
- business
labelSelectors:
app: order-service
delay:
latency: "2s"
duration: "5m"
这个 YAML 的意思是:在 business namespace 里,选中 app=order-service 的 Pod,给它的网络增加 2 秒延迟,持续 5 分钟。
应用这个配置后,很快监控系统就报警了——支付网关的响应时间从 50ms 涨到了 3 秒,而且开始出现 502 错误。我们之前设置了 1 秒超时,加上重试三次,结果订单服务这边处理不过来,把线程池打满。
问题出在超时之后会发生什么——这事我们从来没验证过。
后面改了两件事:一是把超时改成 3 秒,二是重试加了指数退避和熔断。再跑一次同样的故障注入,系统扛住了。
Pod 故障:直接杀进程
网络延迟只是最轻的一种。 Chaos Mesh 还能直接杀 Pod、模拟磁盘满、注入 CPU 负载等。
我们试了一下 Pod Failure,也就是模拟 Kubernetes 自己调度失败或者节点挂了:
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: kill-payment-gateway
namespace: business
spec:
action: pod-failure
mode: one
selector:
namespaces:
- business
labelSelectors:
app: payment-gateway
duration: "3m"
这个配置会让选中的 Pod 直接变成失败状态,Kubernetes 会重新调度。
跑完之后发现一个问题:我们的支付网关没有做连接池预热。新 Pod 起来后,第一个请求就打到数据库,导致数据库 CPU 瞬间飙升,影响了其他服务。
后面改成先在一个健康检查接口预热几个连接池,再正式对外接收流量。
踩过的坑
这一年用下来,几个坑比较明显:
第一个坑:不要在生产环境搞混沌。我们一开始在预发环境跑得很开心,有一次顺手在低峰期的生产环境开了一个小规模的故障注入(只影响了 10% 的请求),结果监控没设好阈值,把正常流量波动误当成故障,触发了自动扩容,成本上去了不说,还惊动了老大。
第二个坑:监控要跟得上。混沌实验跑起来之前,你必须知道正常情况下系统的各项指标长什么样。否则你根本分不清哪些是预期内的影响,哪些是真出了问题。我们后面给每个实验都配了 Prometheus 规则,比如订单失败率超过 1% 就立刻告警。
第三个坑:频率和粒度要控制。一开始我们每周都跑一大堆实验,把测试环境搞得跟战场一样,开发团队的抱怨不少。后面改成:只在发版本前跑一遍关键路径的故障注入,其他实验放在专门的演练窗口。
第四个坑:别只盯"系统挂没挂"。混沌工程还得看故障时能不能优雅降级——支付失败应进待支付,库存查询超时应返回空,别直接抛异常订单。
故障演练的流程
我们逐渐形成了一个相对固定的演练流程:
- 先确定要保护的指标,比如订单成功率 > 99.5%、支付接口 P99 延迟 < 500ms
- 识别关键依赖,比如订单服务依赖用户中心、库存、支付网关
- 针对每个依赖设计故障场景:延迟、丢包、服务不可用、资源耗尽
- 配置监控告警,演练开始前确认 baseline
- 逐个跑故障注入,记录系统表现
- 根据暴露的问题修改代码或配置
- 下一次发版前再跑一遍,验证修复
这套流程跑下来大概两个月,线上故障率明显下来了。代码未必写得更好,是那些藏在设计里的假设被一个个挖出来,要么改掉,要么白纸黑字承认它存在。
其他工具的选择
Chaos Mesh 适合 Kubernetes 环境,如果你还在用虚拟机或者物理机,可以考虑:
- Chaos Monkey:Netflix 的老牌工具,专门杀 EC2 实例,适合 AWS 环境
- Chaos Engineering Toolkit:Gremlin 提供的 SaaS 服务,可以注入网络故障、CPU 负载、内存泄漏等
- Litmus Chaos:Red Hat 的开源工具,也支持 Kubernetes,但生态没有 Chaos Mesh 丰富
选工具的时候,优先考虑两点:一是跟你的基础设施匹配,二是社区活跃度。混沌工程不是主力业务,工具维护跟不上会很痛苦。
什么时候该做混沌工程
不是所有团队都需要马上搞这个。我个人的判断是:
- 如果你的系统已经是微服务架构,而且服务数量超过 5 个,值得考虑
- 如果你的业务有明显的 SLA 要求,比如电商、金融、在线教育,应该尽早开始
- 如果你的团队还在为基本功能发愁,先别折腾这个,先把监控和告警做好
混沌工程的成本不在于工具本身,而在于维护实验脚本、分析结果、持续优化。它的价值主要体现在三个地方:
- 提前暴露隐性依赖和假设
- 验证降级策略和容错机制
- 让团队建立起对故障的预判能力
最后一点其实更重要。做过几次演练之后,大家对系统的理解会变得更立体,知道哪个服务一挂就会影响全局,哪些接口可以暂时降级。
小结
混沌工程干的事,是在测试环境里先经历一遍线上可能遇到的故障,这样真报警时心里大概有谱——不必从零摸索。
这一年下来,我们没有做到"故障零影响",但至少在凌晨三点接到报警时,心里大概知道问题出在哪个方向,哪些服务可以先放一放,哪些必须马上处理。这一点,比任何文档都有用。
系统总会出问题,不如自己先让它出一次。
版权声明: 本文首发于 指尖魔法屋-混沌工程踩坑记录(https://blog.thinkmoon.cn/post/999-chaos-engineering-practice-resilience-testing/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。