3. CPU故障注入:从高负载到满载的实战演练

CPU故障注入,说白了就是让CPU“累趴下”,看看你的系统会怎么反应。我刚开始做稳定性测试时,总觉得CPU满载不是什么大事——不就是慢一点吗?直到有一次线上事故,一个死循环进程把整台机器的CPU打满,核心业务全部超时,我才意识到:CPU资源耗尽,往往是系统雪崩的起点。

这一章,我们聚焦两个工具:stress和ChaosBlade。前者轻量、直接,适合快速验证;后者是混沌工程领域的标准工具,适合纳入自动化故障注入体系。两种思路,你都得掌握。

3.1 先理解CPU故障注入的目标

做CPU故障注入,不是为了把机器搞死。我们的目标很明确:

  • 验证限流降级机制是否生效
  • 检查监控告警能否及时触发
  • 观察服务响应时间的退化曲线
  • 确认自动弹性伸缩策略是否合理

我个人习惯,在注入之前先问三个问题:

  1. 当前CPU基线是多少?(空闲时、正常负载时)
  2. 故障注入的预期目标是多少?(50%、80%、100%)
  3. 注入时长多久?恢复策略是什么?

这三个问题想清楚,再动手。否则你就是在瞎搞。

核心原则:CPU故障注入不是破坏性测试,而是可观测性验证。注入前必须记录基线,注入中必须持续监控,注入后必须自动恢复。

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
我的经验:stress的CPU worker是纯计算密集型,它不会产生网络IO或磁盘IO。如果你想模拟更真实的业务场景(比如Web服务),建议结合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集成
曾经踩过的坑:有一次我用ChaosBlade注入CPU满载,忘了设置--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饱和后,切换反而减少)
一个小技巧:如果你用Prometheus+Grafana,可以直接在Grafana上拉一个CPU故障注入的Dashboard。注入前打一个annotation,注入后看曲线变化,非常直观。

3.5 本章知识体系

下面这张图,帮你把本章的核心逻辑串起来:

CPU故障注入知识体系 CPU故障注入知识体系 故障注入目标 stress(轻量压测) ChaosBlade(混沌工程) --cpu N / --timeout T cpu fullload / --cpu-percent 监控指标:CPU使用率 / 平均负载 / 上下文切换 / 运行队列 结果分析:限流是否生效?告警是否触发?响应时间是否退化? 核心思路:目标驱动 → 工具选择 → 注入执行 → 监控采集 → 结果分析

3.6 避坑指南

最后,分享几个我亲身踩过的坑:

  • 曾经有一次,我在生产环境的一台8核机器上跑stress --cpu 8,结果把数据库连接池打满了。为什么?因为CPU满载导致数据库驱动里的心跳检测超时,连接池不断重建连接。所以,CPU故障注入一定要考虑连带影响。
  • 还有一次,我用ChaosBlade注入CPU满载,但忘了检查机器的CPU亲和性设置。结果注入只对部分核生效,监控曲线看起来只有50%负载。排查了半天才发现,有个进程绑定了特定CPU核心。
  • 另一个教训:stress和ChaosBlade的CPU worker都是纯用户态计算。如果你的业务是IO密集型(比如大量读写磁盘),CPU满载可能不会触发你预期的限流策略。这时候,你得用stress --io或者fio来模拟IO压力。
重要提醒:永远不要在未经授权的生产环境执行CPU故障注入。即使你有权限,也建议先走变更流程,通知所有相关方,并准备好回滚方案。我见过有人因为忘了通知DBA,结果CPU满载导致数据库主从延迟,差点酿成事故。

好了,这一章的内容就到这里。stress和ChaosBlade这两个工具,你可以在自己的测试环境里多练练。记住:故障注入不是目的,验证系统的韧性才是。