上周五凌晨1点20分,运维群炸了。
"生产集群3台服务器CPU都100%了,业务响应超时。"
我从床上弹起来,SSH上去
top一看——几十个进程都在跑,全都占用CPU,看不出谁是元凶。更绝望的是,机器已经卡到
top命令都要转5秒才刷新一次。那晚我用一套AI辅助的排障流程,从上机器到定位根因,用时6分钟。这篇把完整流程拆开讲。
从业8年,我处理过上百次CPU飙高故障。这个场景为什么让人抓狂?
| 进程太多分不清 | |
| 机器卡到没法排查 | top |
| 表象和根因不一致 | |
| 压力越大越难查 | |
| 凌晨状态减半 |
这不是个技术问题,是一个"混乱信息里找信号"的问题。
而**"从混乱信息里找信号",恰恰是LLM最擅长的**。
# 凌晨1点20分,你的操作序列:
top # 看谁占CPU(结果:一堆进程都在跑)
ps aux | grep java # 找Java进程
jstack <pid> > /tmp/j.txt # 抓线程栈(但不知道抓哪个)
cat /tmp/j.txt | grep RUNNABLE # 找运行中的线程
# 20分钟过去了,还在翻线程栈......
iostat -x 1 # 看IO
vmstat 1 # 看系统整体
free -m # 看内存
netstat -antp | wc -l # 看连接数
# 40分钟过去了,还在猜......
# 最后运气好瞎猜到是Redis连接池打满导致
平均耗时: 30-60分钟
依赖: 老运维的直觉+运气
# 一键跑排障脚本
./cpu-diag.sh
# 5秒后拿到完整诊断信息
# 把信息喂给AI
# 30秒后AI输出:
# 🎯 根因:Java应用GC频繁导致CPU飙高
# 📍 具体进程:order-service (PID 12345)
# 📊 证据:GC占CPU 78%,Full GC 15次/分钟
# 🛠️ 处置:立即JVM扩堆,同时dump堆分析内存泄漏
平均耗时: 5-8分钟
依赖: 一个脚本 + 一个Prompt
每一步都有对应的脚本和Prompt。下面拆开讲。
cpu-diag.sh排障最大的时间黑洞,是命令一条条敲。 先把该收集的信息一次性收集全:
#!/bin/bash
# cpu-diag.sh - Linux CPU飙高一键诊断
# 用法:./cpu-diag.sh [duration_seconds]
DURATION=${1:-10}
OUTPUT="/tmp/cpu-diag-$(date +%s).txt"
{
echo"========== 1. 系统基本信息 =========="
echo"时间: $(date)"
echo"主机: $(hostname)"
echo"内核: $(uname -r)"
echo"运行时长: $(uptime)"
echo""
echo"CPU信息:"
lscpu | grep -E "^(Architecture|CPU\(s\)|Model name|CPU MHz)"
echo -e "\n========== 2. 整体负载 =========="
echo"--- Load Average ---"
uptime
echo -e "\n--- 内存使用 ---"
free -h
echo -e "\n--- 磁盘使用 ---"
df -h | head -20
echo -e "\n========== 3. Top 15 CPU消耗进程 =========="
ps aux --sort=-%cpu | head -16
echo -e "\n========== 4. Top 15 内存消耗进程 =========="
ps aux --sort=-%mem | head -16
echo -e "\n========== 5. CPU使用情况分解(用户态/内核态/IO等待)=========="
mpstat -P ALL 1 3 2>/dev/null || vmstat 1 3
echo -e "\n========== 6. 上下文切换和中断 =========="
vmstat 1 3
echo -e "\n========== 7. 磁盘IO情况 =========="
iostat -xz 1 3 2>/dev/null || echo"iostat未安装"
echo -e "\n========== 8. 网络连接统计 =========="
echo"--- 连接状态统计 ---"
ss -s
echo -e "\n--- TOP连接数进程 ---"
ss -tunp 2>/dev/null | awk 'NR>1 {print $NF}' | sort | uniq -c | sort -rn | head -10
echo -e "\n========== 9. 高CPU进程线程分析(Top 3进程)=========="
for pid in $(ps aux --sort=-%cpu | awk 'NR>1 && NR<=4 {print $2}'); do
echo -e "\n--- PID $pid 进程详情 ---"
ps -p $pid -o pid,ppid,cmd,%cpu,%mem 2>/dev/null
echo -e "\n--- PID $pid 线程TOP 10 ---"
ps -T -p $pid --sort=-pcpu 2>/dev/null | head -11
done
echo -e "\n========== 10. 内核日志(最近异常)=========="
dmesg -T 2>/dev/null | tail -30 || dmesg | tail -30
echo -e "\n========== 11. 系统日志(最近错误)=========="
journalctl -p err -n 20 --no-pager 2>/dev/null || tail -20 /var/log/messages 2>/dev/null
echo -e "\n========== 12. 僵尸进程/D状态进程 =========="
ps aux | awk '$8 ~ /^[DZ]/ {print}' | head -20
echo -e "\n========== 13. 网络异常统计 =========="
netstat -s 2>/dev/null | grep -iE "(fail|drop|error|reset|retrans)" | head -20
} > "$OUTPUT" 2>&1
echo"✅ 诊断信息已保存到: $OUTPUT"
echo"📋 文件大小: $(du -h $OUTPUT | cut -f1)"
echo""
echo"下一步:"
echo"cat $OUTPUT # 查看"
echo"cat $OUTPUT | pbcopy # macOS复制"
echo"cat $OUTPUT | xclip -selection clipboard # Linux复制"
用法:
chmod +x cpu-diag.sh
./cpu-diag.sh 10
# 5秒后拿到全套诊断信息
2>/dev/null 兜底 |
信息收集完,接下来是核心——让AI输出准确的根因。
你是一位有15年经验的Linux性能专家,精通CPU/内存/IO/网络诊断。
请基于以下诊断信息,分析这台服务器CPU飙高的根因。
【CPU飙高的常见根因分类】
1. 应用层
- 死循环 / 死锁
- GC频繁(Java/Go/Node.js)
- 正则回溯 / 大数据处理
2. 数据库层
- 慢查询 / 全表扫描
- 连接池打满
3. 系统层
- 上下文切换过高(>50000/s)
- 软中断风暴
- iowait过高(磁盘IO瓶颈)
4. 网络层
- 大量TIME_WAIT / CLOSE_WAIT
- 网卡软中断集中在单核
5. 内核层
- 内存回收压力大(kswapd高)
- OOM边缘状态
- Kernel Bug
【输出要求】
按以下结构输出,必须引用诊断信息中的具体数据作为证据:
## 🎯 故障定性
(一句话:CPU飙高的类型和大致原因)
## 🔍 根因分析
### 主要证据链
- 证据1:xxx(引用原始数据)
- 证据2:xxx
- 证据3:xxx
### 根因推断
(基于证据推断的具体根因,越具体越好)
## 📊 关键指标解读
| 指标 | 当前值 | 正常范围 | 判断 |
|------|--------|---------|------|
| Load Average | xxx | <CPU核数 | 正常/异常 |
| iowait | xxx% | <10% | 正常/异常 |
| 上下文切换 | xxx/s | <10000 | 正常/异常 |
| ...其他关键指标 | | | |
## 🛠️ 处置建议(按优先级)
### 【紧急止血】(5分钟内执行)
(具体命令,可直接复制粘贴)
### 【根因排查】(明确根因后执行)
(进一步定位命令,如jstack/perf/strace等)
### 【长期修复】
(代码/配置/架构层面的修复方向)
## ⚠️ 需要补充确认的信息
(如果证据不足以100%确认,列出还需查什么)
【诊断信息】
(粘贴脚本输出)
来看上周五那次真实故障的完整过程。
凌晨1:20,告警群:
🚨 生产集群
order-clusterCPU使用率>95%,持续5分钟
# 1. SSH到告警最严重的节点
ssh prod-order-03
# 2. 直接跑诊断脚本
./cpu-diag.sh 10
# ✅ 诊断信息已保存到: /tmp/cpu-diag-1719000000.txt
# 3. 复制内容喂给AI(用上面的Prompt)
## 🎯 故障定性
Java应用GC频繁导致的用户态CPU飙高,怀疑存在内存泄漏或JVM堆配置不足。
## 🔍 根因分析
### 主要证据链
- 证据1:Top进程显示 `order-service` (Java, PID 8234)
单进程占用 CPU 385%(4核几乎跑满)
(出处:第3节 Top CPU进程,%cpu=385)
- 证据2:mpstat显示用户态CPU占比 89%,iowait仅 0.3%
排除IO瓶颈,问题在应用层
(出处:第5节 mpstat,%usr=89.2, %iowait=0.3)
- 证据3:Java线程分析中,Top 10线程都是名为
"GC Thread#0/1/2/3" 和 "G1 Young RemSet Sampling" 的GC线程
单线程CPU 78-95%
(出处:第9节 PID 8234线程TOP 10)
- 证据4:dmesg有多次 "OOM killer" 相关日志,
但被kill的是其他小进程,Java主进程未挂
(出处:第10节 dmesg)
- 证据5:free显示内存紧张(可用<500MB),
swap使用率78%
(出处:第2节 free -h)
### 根因推断
Java应用 order-service 正在经历持续的Full GC:
1. JVM堆内存不足或存在内存泄漏
2. GC线程占用大量CPU导致业务线程被挤压
3. 内存整体紧张,OS开始使用swap,进一步加剧性能问题
## 📊 关键指标解读
| 指标 | 当前值 | 正常范围 | 判断 |
|------|--------|---------|------|
| Load Average(1min) | 12.3 | <8 (8核) | ⚠️ 异常偏高 |
| CPU用户态 | 89% | <70% | 🔴 严重异常 |
| iowait | 0.3% | <10% | ✅ 正常 |
| 上下文切换 | 8500/s | <15000 | ✅ 正常 |
| 可用内存 | 480MB | >2GB | 🔴 严重不足 |
| Swap使用 | 78% | <10% | 🔴 严重异常 |
## 🛠️ 处置建议
### 【紧急止血】(5分钟内执行)
1. 立即抓取内存dump用于后续分析
jmap -dump:format=b,file=/tmp/heap-8234.hprof 8234
2. 抓取GC日志和线程栈
jstack 8234 > /tmp/jstack-8234.txt
jstat -gcutil 8234 1000 10 # 观察GC情况
3. 重启该Java进程恢复服务(业务优先)
systemctl restart order-service
### 【根因排查】
1. 分析heap dump找内存泄漏点
使用 Eclipse MAT 或 jhat 分析 /tmp/heap-8234.hprof
重点看 Dominator Tree 找到最大对象
2. 检查JVM启动参数
ps -f 8234 | grep -oE "\-X[a-zA-Z0-9:=]*"
确认 -Xmx 是否合理(当前推测<2G)
3. 分析近期变更
查看 order-service 最近3天的部署记录
是否有代码变更引入内存泄漏
### 【长期修复】
1. JVM参数调优
建议:-Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200
2. 加监控告警
Prometheus JVM exporter 采集:
- jvm_gc_pause_seconds (Full GC频次)
- jvm_memory_heap_used_bytes
告警阈值:Full GC >5次/分钟
3. 代码层
基于heap dump分析结果修复内存泄漏
常见:static Map无过期、监听器未注销、连接未关闭
## ⚠️ 需要补充确认的信息
1. 该应用近期是否有版本更新?
2. 是否为业务高峰期(QPS异常拉高)?
3. 其他两台节点是否同时出现类似问题?
如是 → 大概率代码问题
如否 → 可能是单机内存不足
实际处置:从告警到服务恢复,6分钟。
后续用heap dump定位到是一个静态Map缓存没设置过期时间,累积了2GB数据。
不同场景,Prompt里要加不同的引导词,让AI更精准。整理成速查表:
| Java GC频繁 | ||
| 数据库慢查询拖垮 | ||
| 死循环/热点代码 | ||
| 软中断风暴 | ||
| 磁盘IO瓶颈 | ||
| 上下文切换过多 |
每类场景的补充指令加到主Prompt里,AI输出会更精准。
对于死循环/热点代码类问题,光看进程不够,需要看具体是哪一行代码在跑。
火焰图是终极武器。一键生成脚本:
#!/bin/bash
# flamegraph.sh - 一键生成CPU火焰图
# 依赖:perf, FlameGraph工具集
PID=$1
DURATION=${2:-30}
if [ -z "$PID" ]; then
echo"用法: $0 <PID> [duration_seconds]"
exit 1
fi
# 检查依赖
which perf > /dev/null || { echo"请安装 perf: yum install perf"; exit 1; }
[ -d "/opt/FlameGraph" ] || {
echo"首次运行,安装FlameGraph..."
git clone https://github.com/brendangregg/FlameGraph /opt/FlameGraph
}
OUTPUT_DIR="/tmp/flamegraph-$(date +%s)"
mkdir -p $OUTPUT_DIR
cd$OUTPUT_DIR
echo"📊 正在采样 PID $PID,持续 ${DURATION}秒..."
perf record -F 99 -p $PID -g -- sleep $DURATION 2>/dev/null
echo"🎨 生成火焰图..."
perf script > perf.out
/opt/FlameGraph/stackcollapse-perf.pl perf.out > perf.folded
/opt/FlameGraph/flamegraph.pl perf.folded > flamegraph.svg
echo"✅ 火焰图已生成: $OUTPUT_DIR/flamegraph.svg"
echo""
echo"查看方法:"
echo"1. scp 到本地: scp $(hostname):$OUTPUT_DIR/flamegraph.svg ."
echo"2. 用浏览器打开svg文件(可点击交互)"
echo"3. 或用 Python 起http服务: cd $OUTPUT_DIR && python3 -m http.server 8888"
用法:
chmod +x flamegraph.sh
./flamegraph.sh 8234 30 # 采样PID 8234,30秒
把火焰图截图 + 描述发给AI:
以下是我用perf生成的火焰图,采样目标是XXX应用,
持续30秒。请分析:
1. CPU时间主要消耗在哪些函数?
2. 是否存在明显的热点或异常调用栈?
3. 结合调用链,推测可能的性能问题和优化方向。
(上传火焰图图片)
AI能读懂火焰图,直接告诉你热点函数在哪。
症状: 跑 cpu-diag.sh 时,系统更卡了
原因: 系统已经资源紧张,脚本内的命令继续加压
解法:
nice -n 19 降低脚本优先级timeout 防止卡死timeout 5 top -bn1 > top.txt # 5秒超时
nice -n 19 ./cpu-diag.sh # 最低优先级
症状: Java进程CPU 300%,AI说是Java应用问题
实际: 是MySQL慢查询导致Java线程阻塞在等待
解法: Prompt里明确要求**"分析根因,不要停留在表面进程"**
【重要】请分析问题根因,而不是只指出哪个进程CPU高。
考虑:这个进程是"元凶"还是"受害者"?
比如Java CPU高,可能是:
- 元凶:GC频繁、死循环
- 受害者:等待DB响应、等待网络IO
症状: 复制给AI后,AI只分析了一部分
原因: 诊断输出超过LLM上下文长度
解法:
# 精简版:只保留top 5进程 + 关键指标
./cpu-diag.sh | grep -A 5 -E "(Top|CPU|Load|Memory)" > diag-short.txt
症状: AI说可能是GC问题,但没证据
原因: 应用没开GC日志
解法: 预防性配置JVM
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=5,filesize=100M
之后诊断脚本可以直接读gc.log给AI。
症状: 容器内 top 显示宿主机的CPU核数
原因: 老版本容器/JVM没做资源隔离感知
解法:
nproc 看真实可用核数-XX:+UseContainerSupport(JDK8u191+)# 检测是否在容器中
if [ -f /.dockerenv ]; then
echo"⚠️ 检测到容器环境"
echo"容器CPU限制: $(cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 2>/dev/null)"
echo"容器内存限制: $(cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null)"
fi
手动排障还是慢。终极方案:CPU告警自动触发AI分析。
架构:
Prometheus告警 → Alertmanager → Webhook →
自动登录目标机器跑诊断 → 调AI分析 →
飞书推送诊断报告
核心代码 auto_cpu_diag.py:
import paramiko
import subprocess
from flask import Flask, request
from openai import OpenAI
import requests
import os
app = Flask(__name__)
ai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
FEISHU_WEBHOOK = os.getenv("FEISHU_WEBHOOK")
defssh_run_diag(host, user="root", key_path="~/.ssh/id_rsa"):
"""SSH到目标机器跑诊断脚本"""
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username=user, key_filename=os.path.expanduser(key_path))
# 上传诊断脚本
sftp = ssh.open_sftp()
sftp.put("cpu-diag.sh", "/tmp/cpu-diag.sh")
ssh.exec_command("chmod +x /tmp/cpu-diag.sh")
# 执行诊断
stdin, stdout, stderr = ssh.exec_command("/tmp/cpu-diag.sh")
diag_output = stdout.read().decode()
ssh.close()
return diag_output
defanalyze_with_ai(diag_info, host):
"""调AI分析"""
with open("cpu_prompt.md") as f:
prompt_template = f.read()
response = ai_client.chat.completions.create(
model="gpt-5.2-pro",
messages=[
{"role": "system", "content": "你是15年经验的Linux性能专家。"},
{"role": "user", "content": prompt_template + "\n\n【诊断信息】\n" + diag_info}
],
temperature=0.2,
)
return response.choices[0].message.content
defpush_to_feishu(analysis, host):
"""推送到飞书"""
card = {
"msg_type": "interactive",
"card": {
"header": {
"title": {"tag": "plain_text",
"content": f"🔥 CPU飙高AI诊断报告 - {host}"},
"template": "red"
},
"elements": [{
"tag": "div",
"text": {"tag": "lark_md", "content": analysis}
}]
}
}
requests.post(FEISHU_WEBHOOK, json=card)
@app.route("/alert", methods=["POST"])
defhandle_alert():
alert = request.json
for item in alert.get("alerts", []):
labels = item.get("labels", {})
host = labels.get("instance", "").split(":")[0]
alertname = labels.get("alertname")
if"CPU"in alertname and host:
try:
# 1. SSH跑诊断
diag = ssh_run_diag(host)
# 2. AI分析
analysis = analyze_with_ai(diag, host)
# 3. 推送飞书
push_to_feishu(analysis, host)
except Exception as e:
push_to_feishu(f"❌ 自动诊断失败: {e}", host)
return {"status": "ok"}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8082)
Alertmanager配置:
receivers:
-name:'cpu-ai-handler'
webhook_configs:
-url:'http://cpu-ai-handler:8082/alert'
route:
routes:
-match:
alertname:HighCPUUsage
receiver:'cpu-ai-handler'
效果:
告警触发 → 90秒后飞书自动收到AI诊断报告 → **值班人员看到的不再是"CPU 100%"这种废话,而是"根因+具体命令"**。
我把这套方案在团队推行3个月,数据如下:
| 6分钟 | |||
| 能独立完成 | |||
最爽的改变: 新入职3个月的同事,凌晨遇到CPU飙高,也能独立处理了。
我刚做运维那年,跟着师傅处理过一次CPU 100%故障。
当时我一脸懵,看着 top 一堆数字不知道从哪下手。师傅淡定地敲了5条命令,5分钟定位到是某个Python脚本正则回溯。
那时我心想:这就是老运维和新人的差距啊。
10年后的今天,AI + 一套脚本 + 一个Prompt,让新人也能有"老运维"的判断力。
这不是让老运维贬值,是让"经验"这件事可以规模化传递。
我把自己10年踩过的坑、总结的排障套路,全部沉淀到了这套Prompt里。每一个用它的人,都在借用我10年的经验。
这才是2026年AI + 运维最大的价值: 不是取代人,是把优秀运维的能力,变成每个人都能用的公共基础设施。
觉得有用?分享给更多运维同行看到 👇
转发给你身边的新同事,让他也能像老运维一样淡定处理故障。