Chaos Monkey 随机杀 Pod 没发现任何问题,但真实故障还是发生了:混沌工程不是随机搞破坏
title: Chaos Monkey 随机杀 Pod 没发现任何问题但真实故障还是发生了混沌工程不是随机搞破坏description: 从 Netflix 的 Chaos Monkey 到生产级故障注入深入讲解混沌工程的设计原则、实验框架和 3 个 Java 故障注入实现。tags: [混沌工程, Chaos Engineering, 故障注入, Java, 微服务, resilience]2023 年 9 月我们在生产环境引入 Chaos Monkey每天随机杀掉 5% 的 Pod。跑了两个月所有服务都 表现良好——自动扩容、自动恢复、无告警。但 11 月的一次真实故障打了脸一个下游支付接口延迟从 50ms 涨到 8 秒我们的服务因为没有设置 read timeout连接池被占满级联雪崩。Chaos Monkey 从来没发现这个问题因为它只杀 Pod不模拟 慢节点。那次教训让我明白混沌工程不是随机搞破坏是有假设、有度量、有针对性的实验。混沌工程的 5 个原则不是 3 个也不是随便玩Netflix 提出的混沌工程有 5 个核心原则建立稳态假设系统正常时的关键指标是什么QPS、P99、错误率、业务成功率引入真实世界的变量网络延迟、CPU 满载、磁盘满、依赖服务故障在生产环境运行staging 和生产的差异往往就是故障的触发条件自动化持续运行人工执行的一次性实验没有统计意义最小化爆炸半径用金丝雀、流量镜像、分区隔离控制影响范围我们早期的问题在于只做了第 2 步随机杀 Pod忽略了第 1 步没有定义稳态和第 4 步没有自动化。实验 1模拟慢节点Latency Injection真实世界里节点很少 直接挂掉更多时候是 变慢。CPU steal time 高、GC 频繁、网络拥塞都会导致节点响应变慢但又不完全不可用。// 用 Byteman 在方法入口注入延迟 // 规则文件slow-method.btm RULE slow down order query CLASS com.example.OrderService METHOD queryOrder AT ENTRY IF true DO Thread.sleep(5000); // 注入 5 秒延迟 ENDRULEByteman 是 JBoss 开发的 Java 字节码注入工具不需要改代码通过 attach 到 JVM 进程动态生效。AT ENTRY表示在方法入口处注入Thread.sleep(5000)让每次调用延迟 5 秒。但这种硬编码的注入太粗暴。我们后来换成了Chaos Mesh JVMChaos# Chaos Mesh 的 JVMChaos 实验定义 apiVersion: chaos-mesh.org/v1alpha1 kind: JVMChaos metadata: name: slow-order-service spec: action: latency class: com.example.OrderService method: queryOrder latency: 5000 # 注入 5s 延迟 mode: all # 对所有匹配 Pod 生效 selector: namespaces: - production labelSelectors: app: order-serviceaction: latency是注入延迟mode: all表示对 selector 匹配的所有 Pod 生效。Chaos Mesh 是 PingCAP 开源的 Kubernetes 混沌工程平台支持网络、Pod、JVM、磁盘等多种故障注入。这个实验暴露了我们服务的一个严重问题HTTP 客户端没有设置 read timeout。RestTemplate默认是无限等待的下游 5 秒延迟直接导致我们线程池堆积。// 修复前默认 RestTemplate无超时 RestTemplate restTemplate new RestTemplate(); // 危险 // 修复后显式设置连接和读取超时 SimpleClientHttpRequestFactory factory new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(1000); // 连接超时 1s factory.setReadTimeout(3000); // 读取超时 3s RestTemplate restTemplate new RestTemplate(factory);setReadTimeout(3000)是关键超过 3 秒没收到响应就抛ResourceAccessException触发熔断降级而不是无限等待把线程池占满。实验 2模拟 CPU 满载有些故障只在 CPU 高负载时触发比如 Go 的 GC、Java 的 Safepoint。// Java CPU 满载注入用无限循环占满一个核心 public class CpuBurner { public static void burnCpu(int cores, int durationSeconds) { ExecutorService executor Executors.newFixedThreadPool(cores); AtomicBoolean running new AtomicBoolean(true); for (int i 0; i cores; i) { executor.submit(() - { while (running.get()) { // 数学计算占满 CPU避免被 JIT 优化掉 Math.random() * Math.random(); } }); } try { Thread.sleep(durationSeconds * 1000L); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } running.set(false); executor.shutdown(); } }Math.random() * Math.random()是刻意设计的 不可优化 计算JIT 编译器不能把这段代码优化掉因为它每次结果不同。AtomicBoolean控制停止信号保证实验结束后 CPU 恢复正常。我们在订单服务上做了这个实验占满 2 个核心容器 limit 4 核发现- QPS 从 3500 降到 1800- P99 从 45ms 涨到 320ms- 但错误率依然是 0%这看起来 还行但监控显示GC 次数从每分钟 2 次涨到 40 次。如果持续高负载GC 压力会导致更严重的停顿。这个实验促使我们把容器的 CPU limit 从 4 核调到 6 核并设置了 HPA水平自动扩缩容的 CPU 阈值从 70% 降到 60%。实验 3模拟网络分区网络分区是分布式系统最可怕的故障之一节点没挂但互相之间通不了信。# 用 tc iptables 模拟网络分区 # 把 Pod-1 和 Pod-2 之间的网络切断 # 在 Pod-1 上执行 iptables -A OUTPUT -d Pod-2-IP -j DROP iptables -A INPUT -s Pod-2-IP -j DROPiptables -A OUTPUT -d IP -j DROP把目标 IP 为 Pod-2 的出站包全部丢弃。这个实验在 Redis Cluster 上跑时发现了脑裂问题两个主节点互相认为对方挂了都把自己提升为主导致数据不一致。# Chaos Mesh 的网络分区实验 apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: name: redis-partition spec: action: partition mode: all selector: labelSelectors: app: redis-node-1 direction: both target: mode: all selector: labelSelectors: app: redis-node-2 duration: 5maction: partition是网络分区direction: both表示双向隔离Pod-1 不能发给 Pod-2Pod-2 也不能发给 Pod-1。duration: 5m实验持续 5 分钟。这个实验后我们给 Redis Cluster 加了min-replicas-to-write 1和cluster-node-timeout 5000的配置缩短故障检测时间降低脑裂窗口。混沌工程的落地框架不是工具越多越好我们的混沌工程实践分三层基础设施层Chaos Mesh 做 Pod/网络/磁盘级别的故障应用层Byteman 做 JVM 方法级注入业务层自定义脚本模拟业务异常如订单状态不一致、库存超卖// 业务层混沌模拟库存扣减异常 Component public class InventoryChaos { Autowired private InventoryService inventoryService; // 按 0.1% 概率注入库存不一致 public boolean deductWithChaos(Long skuId, int quantity) { if (Math.random() 0.001) { // 模拟扣减成功但数据库未提交 inventoryService.mockCommitFailure(skuId, quantity); return true; // 返回成功实际没扣 } return inventoryService.deduct(skuId, quantity); } }业务层混沌是最难做的因为它需要深入理解业务逻辑。但收益也是最大的——我们发现过一个订单状态机漏洞已支付 状态可以非法转移到 已取消这个漏洞在代码 review 时没发现但在混沌实验里被触发了。稳态度量没有度量实验就是瞎搞每次实验前我们必须定义 3 个稳态指标指标正常值实验阈值超出后动作P99 延迟 100ms 300ms自动停止实验错误率 0.1% 1%自动停止实验订单成功率 99.9% 99%自动停止实验// 实验自动停止的钩子 Component public class ChaosAbortPolicy { Autowired private MetricsClient metrics; Scheduled(fixedRate 5000) public void checkSteadyState() { double errorRate metrics.getErrorRate(order-service); if (errorRate 0.01) { chaosEngine.stopAllExperiments(); alertService.sendP1Alert(Chaos experiment breached error rate threshold); } } }Scheduled(fixedRate 5000)每 5 秒检查一次指标。chaosEngine.stopAllExperiments()立即停止所有正在运行的实验。这个自动保护机制是必须的——人工观察 Grafana 再手动停止可能已经晚了。总结混沌工程不是 搞破坏看谁先挂是在受控条件下验证系统韧性 。Chaos Monkey 随机杀 Pod 只是入门真正的价值在于模拟真实世界的复杂故障慢节点、CPU 满载、网络分区、业务异常。我的建议是1.不要从生产环境开始先在 staging 跑 1 个月修复明显问题后再上生产2.不要随机搞每个实验都要有假设如果下游延迟 5 秒我们的熔断会触发吗3.必须有自动停止机制没有稳态度量和自动停止的实验是自杀4.记录和复盘每个实验的结果都要写成文档作为架构改进的依据那次支付接口雪崩的事故后我们团队形成了一个规矩每次引入新的外部依赖必须先做一个 下游延迟 10 秒 的混沌实验。这个规矩在接下来的一年里提前发现了 3 个潜在的单点故障。思考题你的系统现在能容忍下游服务延迟 5 秒吗超时设置在哪里有没有 fallback如果你现在要在生产环境跑混沌实验你敢先从哪个服务开始它的爆炸半径怎么控制

相关新闻

pi coding agent CLI 深度解析:架构、agent loop 与 TUI 启动报错排查

pi coding agent CLI 深度解析:架构、agent loop 与 TUI 启动报错排查

1. 从“pi”这个标题说起:一个极简命名背后的技术野心第一次看到“pi”这个项目标题,很多人会愣一下——是数学常数?是树莓派?还是某个内部代号?我当初也是同样的反应。但把热搜词摊开一看,答案就清楚了&am…

2026/10/4 8:21:41 阅读更多 →
Cursor插件机制深度解析:plugin.json、TypeScript SDK与Web Boot原理

Cursor插件机制深度解析:plugin.json、TypeScript SDK与Web Boot原理

1. 项目概述:从“plugins”这个词开始,我们到底在谈什么?“plugins”——这个词在开发者日常里出现的频率,大概和“undefined”报错一样高频。但有意思的是,绝大多数人每天点开插件市场、安装、启用、再卸载&#xff0…

2026/10/4 8:21:41 阅读更多 →
1976国际标准大气模型Matlab实现:温度气压密度计算代码

1976国际标准大气模型Matlab实现:温度气压密度计算代码

做飞行器仿真、无人机性能估算、探空气球数据处理,或者只是写课程作业,我猜你迟早会搜到“1976国际标准大气”这个词。我最早接触它是在做高空气象数据还原的时候,当时在Matlab里查一次表插值一次,效率低还容易错,后来…

2026/10/4 8:21:41 阅读更多 →

最新新闻

OmniRoute本地模型代理层实战:多后端路由配置与性能调优指南

OmniRoute本地模型代理层实战:多后端路由配置与性能调优指南

1. 为什么要在本地跑一个模型代理层很多人第一次接触“本地模型代理”这个概念时,脑子里冒出来的第一个疑问是:我明明可以直接调用本地部署的模型服务,为什么还要在中间加一层代理?这不是脱裤子放屁吗?我刚开始也是这么…

2026/10/4 9:06:13 阅读更多 →
插件加载机制深度拆解:从激活失败到排障实战

插件加载机制深度拆解:从激活失败到排障实战

关于plugins的深度拆解:从百战不殆到十面埋伏先交代一下背景。最近在折腾几个项目,结果被plugins这个词反复折磨。不是那种装上就能用的顺心插件,而是各种Failed to load、entry did not activate的报错连环弹。把我真实踩过的坑、排查的思路…

2026/10/4 9:06:13 阅读更多 →
Windows 11效率低?用OpenShell开源工具还原经典开始菜单

Windows 11效率低?用OpenShell开源工具还原经典开始菜单

1. 为什么 Windows 11 会让老用户想找回经典开始菜单把 Windows 11 装完的第一周,我做的最多的一件事是:按一下 Win 键,盯着那个居中排列、塞满“推荐项”和固定应用图标的菜单,然后按 Esc 退出。新布局对触屏和触控板用户确实友好…

2026/10/4 9:06:13 阅读更多 →
插件加载与激活机制全解析:从did not activate到web boot排查

插件加载与激活机制全解析:从did not activate到web boot排查

"plugins"这个词,做开发的基本每天都能撞见。IDE 里有 plugins,构建工具里有 plugins,播放器里有 plugins,甚至连浏览器启动阶段都会因为某个 plugin 没激活而刷一屏的failed to load plugins。很多人被这类报错折磨过&…

2026/10/4 9:06:13 阅读更多 →
ENVI遥感水质反演实战:从模型原理到叶绿素a与悬浮物制图

ENVI遥感水质反演实战:从模型原理到叶绿素a与悬浮物制图

经常有同行问我,说看到别人用ENVI做水质反演,一键就能出叶绿素a或者悬浮物的分布图,自己照着操作却总是不对劲,要么反演结果出现成片负值,要么模型精度低到不敢用。其实遥感水质反演这件事,听起来高大上&am…

2026/10/4 9:06:13 阅读更多 →
Python Selenium 高级防检测:模拟真人滚动、随机行为、彻底去除机器人特征

Python Selenium 高级防检测:模拟真人滚动、随机行为、彻底去除机器人特征

前言用过 Selenium 的同学都知道: 原生脚本百分百被风控识别。原因不止 UA,而是:存在 webdriver 自动化标记滚动行为机械固定操作时序太规律无随机无效行为本文给大家一套工业级学习版真人浏览脚本,去除所有自动化特征&#xff0c…

2026/10/4 9:05:13 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →