3. CPU故障注入:从高负载到满载的实战演练
CPU故障注入,说白了就是让CPU“累趴下”,看看你的系统会怎么反应。我刚开始做稳定性测试时,总觉得CPU满载不是什么大事——不就是慢一点吗?直到有一次线上事故,一个死循环进程把整台机器的CPU打满,核心业务全部超时,我才意识到:CPU资源耗尽,往往是系统雪崩的起点。
这一章,我们聚焦两个工具:stress和ChaosBlade。前者轻量、直接,适合快速验证;后者是混沌工程领域的标准工具,适合纳入自动化故障注入体系。两种思路,你都得掌握。
3.1 先理解CPU故障注入的目标
做CPU故障注入,不是为了把机器搞死。我们的目标很明确:
- 验证限流降级机制是否生效
- 检查监控告警能否及时触发
- 观察服务响应时间的退化曲线
- 确认自动弹性伸缩策略是否合理
我个人习惯,在注入之前先问三个问题:
- 当前CPU基线是多少?(空闲时、正常负载时)
- 故障注入的预期目标是多少?(50%、80%、100%)
- 注入时长多久?恢复策略是什么?
这三个问题想清楚,再动手。否则你就是在瞎搞。
3.2 使用stress工具模拟CPU高负载
stress是Linux下一个非常轻量的压力工具。它不复杂,但够用。我最早接触它是在2016年,当时要验证一个实时计算集群的调度策略,stress帮我快速制造了各种负载场景。
3.2.1 安装stress
# Ubuntu/Debian
sudo apt-get install stress
# CentOS/RHEL
sudo yum install stress
# macOS (通过Homebrew)
brew install stress
安装完,跑一下stress --help,你会看到几个核心参数:
| 参数 | 含义 | 示例 |
|---|---|---|
--cpu N |
启动N个worker,每个worker计算平方根 | --cpu 4 |
--timeout T |
运行T秒后自动退出 | --timeout 60 |
--vm N |
启动N个worker,分配内存并读写 | --vm 2 --vm-bytes 256M |
--io N |
启动N个worker,执行sync()系统调用 | --io 4 |
3.2.2 实战:模拟50% CPU负载
假设你的机器是4核CPU,想模拟50%的负载:
# 启动2个CPU worker(4核的50%)
stress --cpu 2 --timeout 120
打开另一个终端,用top或htop观察:
top -bn1 | grep "Cpu(s)"
# 输出示例:%Cpu(s): 50.3 us, 0.2 sy, 0.0 ni, 49.5 id, ...
嗯,这里要注意:stress的CPU worker是纯用户态计算,它不会触发系统调用,所以sy(系统态)占比很低。如果你想模拟系统态负载,得用别的工具。
3.2.3 实战:模拟CPU满载
# 4核机器,启动4个CPU worker,跑5分钟
stress --cpu 4 --timeout 300
跑起来之后,你会看到CPU使用率瞬间冲到接近100%。这时候,我建议你同时观察几个指标:
- 系统平均负载(load average):会超过CPU核数
- 上下文切换次数:
vmstat 1看cs列 - 运行队列长度:
sar -q 1看runq-sz
ab或wrk一起用。
3.3 使用ChaosBlade注入CPU满载故障
ChaosBlade是阿里巴巴开源的混沌工程工具。相比stress,它更专业:支持实验定义、实验编排、实验停止、实验报告。我个人在团队里推广混沌工程时,首选就是它。
3.3.1 安装ChaosBlade
# 下载最新版(以v1.7.1为例)
wget https://github.com/chaosblade-io/chaosblade/releases/download/v1.7.1/chaosblade-1.7.1-linux-amd64.tar.gz
# 解压
tar -xzf chaosblade-1.7.1-linux-amd64.tar.gz
# 进入目录
cd chaosblade-1.7.1
# 验证安装
./blade --version
3.3.2 注入CPU满载
ChaosBlade的CPU故障注入命令非常直观:
# 注入CPU满载(默认使用所有核)
./blade create cpu fullload
# 指定CPU核数(比如只压2个核)
./blade create cpu fullload --cpu-percent 80 --cpu-count 2
# 指定运行时间(比如60秒后自动恢复)
./blade create cpu fullload --timeout 60
执行后,你会得到一个实验UID,比如e6f8f9a0b3c4d5e6。这个UID很重要,用来查询状态和停止实验:
# 查询实验状态
./blade status e6f8f9a0b3c4d5e6
# 停止实验
./blade destroy e6f8f9a0b3c4d5e6
3.3.3 对比:stress vs ChaosBlade
| 维度 | stress | ChaosBlade |
|---|---|---|
| 安装复杂度 | 极简(apt/yum直接装) | 中等(需下载二进制) |
| CPU控制粒度 | 按worker数控制 | 按百分比+核数控制 |
| 自动恢复 | 仅支持timeout | 支持timeout+手动destroy |
| 实验可观测性 | 无 | 有UID、状态查询、日志 |
| 适合场景 | 快速验证、临时压测 | 自动化混沌实验、CI/CD集成 |
--timeout,结果实验跑了一整夜。第二天发现机器上所有服务都挂了。所以,生产环境务必设置超时时间,或者配合监控自动熔断。
3.4 监控CPU指标:你该看什么?
注入故障只是第一步。真正有价值的是监控数据。我见过太多人,故障注入完只看CPU使用率,然后就觉得“哦,满了”。其实远远不够。
3.4.1 核心监控指标
| 指标 | 命令/工具 | 关注点 |
|---|---|---|
| CPU使用率 | top, htop |
user/sys/iowait/idle占比 |
| 平均负载 | uptime, cat /proc/loadavg |
是否超过CPU核数 |
| 上下文切换 | vmstat 1 |
cs列,过高说明锁竞争严重 |
| 运行队列 | sar -q 1 |
runq-sz,超过核数说明CPU不够用 |
| 中断次数 | cat /proc/interrupts |
硬件中断是否异常 |
3.4.2 实战监控脚本
我习惯写一个简单的监控脚本,在注入故障的同时采集数据:
#!/bin/bash
# monitor_cpu.sh
# 用法:./monitor_cpu.sh [持续时间(秒)]
DURATION=${1:-60}
INTERVAL=5
END=$((SECONDS + DURATION))
echo "时间,user,sys,iowait,idle,load1,load5,load15,cs" > cpu_metrics.csv
while [ $SECONDS -lt $END ]; do
TIMESTAMP=$(date +%H:%M:%S)
# CPU使用率
CPU_LINE=$(top -bn1 | grep "Cpu(s)")
USER=$(echo $CPU_LINE | awk '{print $2}' | cut -d'%' -f1)
SYS=$(echo $CPU_LINE | awk '{print $4}' | cut -d'%' -f1)
IOWAIT=$(echo $CPU_LINE | awk '{print $8}' | cut -d'%' -f1)
IDLE=$(echo $CPU_LINE | awk '{print $12}' | cut -d'%' -f1)
# 平均负载
LOAD=$(cat /proc/loadavg | awk '{print $1","$2","$3}')
# 上下文切换
CS=$(vmstat 1 2 | tail -1 | awk '{print $12}')
echo "$TIMESTAMP,$USER,$SYS,$IOWAIT,$IDLE,$LOAD,$CS" >> cpu_metrics.csv
sleep $INTERVAL
done
echo "监控完成,数据已保存到 cpu_metrics.csv"
跑完故障注入后,用Excel或Python把CSV画成折线图。你会看到:
- CPU使用率瞬间飙升
- 平均负载缓慢爬升(因为进程在排队)
- 上下文切换可能先升后降(CPU饱和后,切换反而减少)
3.5 本章知识体系
下面这张图,帮你把本章的核心逻辑串起来:
3.6 避坑指南
最后,分享几个我亲身踩过的坑:
- 曾经有一次,我在生产环境的一台8核机器上跑stress --cpu 8,结果把数据库连接池打满了。为什么?因为CPU满载导致数据库驱动里的心跳检测超时,连接池不断重建连接。所以,CPU故障注入一定要考虑连带影响。
- 还有一次,我用ChaosBlade注入CPU满载,但忘了检查机器的CPU亲和性设置。结果注入只对部分核生效,监控曲线看起来只有50%负载。排查了半天才发现,有个进程绑定了特定CPU核心。
- 另一个教训:stress和ChaosBlade的CPU worker都是纯用户态计算。如果你的业务是IO密集型(比如大量读写磁盘),CPU满载可能不会触发你预期的限流策略。这时候,你得用
stress --io或者fio来模拟IO压力。
好了,这一章的内容就到这里。stress和ChaosBlade这两个工具,你可以在自己的测试环境里多练练。记住:故障注入不是目的,验证系统的韧性才是。