文章总结: 本文提供Linux服务器CPU飙升至100%时的系统化排查方法,强调先确认告警信息而非盲目重启。通过mpstat、vmstat等工具分析CPU时间分布,定位高CPU进程并深入线程级别。针对用户态、内核态、I/Owait及虚拟机steal等不同场景,给出具体命令和证据链建立步骤。同时涵盖容器和Kubernetes环境的特殊注意事项,是一份实用的生产环境故障排查指南。 综合评分: 90 文章分类: 实战经验
Linux 服务器 CPU 飙到 100%,我是如何一步步定位的?
点击关注👉 点击关注👉
马哥Linux运维
2026年7月11日 13:00 广东
在小说阅读器读本章
去阅读
Linux 服务器 CPU 飙到 100%,我是如何一步步定位的?
CPU 使用率突然达到 100% 时,最容易犯的错误是看到一个高 CPU 进程就立即重启。重启可能暂时恢复服务,但也会清掉现场,并且无法回答三个关键问题:究竟是一个核心跑满还是整机跑满,CPU 时间消耗在用户态还是内核态,以及高 CPU 是根因还是其他故障的结果。
本文给出一套可以在生产环境执行的排查顺序。示例以 systemd 管理的 Linux 服务器为主,命令适用于常见的 RHEL、Rocky Linux、AlmaLinux、Ubuntu 和 Debian 系统。不同发行版的软件包名称可能不同:RHEL 系通常通过 dnf install sysstat procps-ng 安装工具,Ubuntu/Debian 通常通过 apt install sysstat procps 安装。安装软件会改变系统状态,生产环境应先确认软件源、变更窗口和主机管理规范。
一、先定义“CPU 100%”到底是什么
“CPU 100%”可能来自监控平台、top、容器面板或云厂商控制台,不同来源的分母并不一致。
-
在一台 8 核服务器上,单线程程序占满一个逻辑 CPU,
top中该进程可能显示约 100%,但整机平均使用率只有约 12.5%。 -
top默认的进程 CPU 百分比可能以单个逻辑 CPU 为 100%;按
I可在部分 procps-ng 版本中切换 Solaris/Irix 模式,实际行为应以本机top帮助为准。 -
Prometheus 中常用
node_cpu_seconds_total计算 CPU 忙碌比例,但最终指标名和标签要以实际 exporter 暴露的指标为准。 -
容器中的 100% 可能表示达到了 cgroup 配额,并不代表宿主机全部 CPU 已耗尽。
因此,第一步不是找进程,而是确认告警时间、持续时长、统计对象和采样周期。记录以下信息:主机名、业务实例、告警开始时间、是否仍在持续、监控查询语句、最近一次发布或配置变更时间。后续日志、指标和命令输出必须围绕同一时间窗口对齐。
二、现场保护与信息收集
先登录正确的主机并确认时间。不要只看终端提示符判断环境。
bash
hostnamectl
date --iso-8601=seconds
uptime
uptime 同时给出运行时长和 1、5、15 分钟 load average。负载不是 CPU 使用率:它通常包含正在运行以及等待不可中断资源的任务。高负载可能由 CPU 竞争引起,也可能由块设备、NFS 等 I/O 等待引起。
查看逻辑 CPU 数量和拓扑:
bash
lscpu
nproc
随后做短时间、低侵入采样。不要只截一张 top:瞬时峰值可能刚好落在采样点上,也可能被平均值掩盖。
bash
LC_ALL=C top -b -d 1 -n 10 > /tmp/top-$(date +%Y%m%d-%H%M%S).txt
vmstat 1 10
mpstat -P ALL 1 10
pidstat -u -r -d -w 1 10
这些命令主要是读取状态,通常风险较低,但采样工具自身也有开销。在已经严重过载的主机上,先执行 top、vmstat 等轻量命令,谨慎使用 perf record、长时间 strace 或高频采样。
如果 /tmp 空间紧张,应改用经过确认的诊断目录。写文件前先检查:
bash
df -h /tmp
df -i /tmp
不要为了采样直接删除历史日志。清理日志属于高风险操作,可能破坏证据和审计链;如必须处理,应先归档、校验备份、明确保留范围并使用日志系统既定的轮转流程。
三、用系统级指标做初步判断
1. 看 CPU 时间花在哪里
mpstat 常见字段包括:
-
%usr:普通用户态程序消耗的 CPU 时间。
-
%sys:内核态消耗的 CPU 时间。
-
%iowait:逻辑 CPU 空闲且系统存在未完成磁盘 I/O 请求的时间比例。它不是“磁盘使用率”,也不能独立证明磁盘是根因。
-
%irq、
%soft:硬中断和软中断时间。 -
%steal:虚拟机等待宿主机调度物理 CPU 的时间。
-
%idle:空闲时间。
下面只用于说明格式,不代表任何真实环境。
示例输出:
text
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %idle
Average: all 82.00 0.00 12.00 1.00 0.00 1.00 0.00 4.00
如果 %usr 长期很高,优先找业务进程和计算热点;如果 %sys 高,检查系统调用、锁竞争、网络包处理或频繁上下文切换;如果 %soft 高,结合网卡包率、丢包和软中断分布检查网络路径;虚拟机 %steal 高则需要结合云平台或宿主机侧指标,单靠来宾系统无法得出最终根因。
2. 看运行队列与上下文切换
vmstat 1 10 的第一行通常是自开机以来的平均值,分析实时问题时重点看后续行。
示例输出:
text
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
12 0 0 812344 24576 921600 0 0 0 20 9500 28000 86 12 2 0 0
关键字段的判断方式如下:
-
r是可运行任务数。持续明显高于逻辑 CPU 数,说明运行队列存在竞争,但短时超过并不一定异常。
-
b是处于不可中断睡眠状态的任务数,持续升高要检查 I/O 或内核等待。
-
cs是每秒上下文切换次数,必须与主机历史基线比较,不能凭一个绝对值定性。
-
us、
sy、id、wa、st分别对应用户态、内核态、空闲、I/O wait 和 steal。
3. 回看告警发生时的历史数据
如果 sysstat 已提前启用,可以使用 sar 回溯。当天数据通常位于 /var/log/sa/saDD 或 /var/log/sysstat/saDD,具体路径取决于发行版配置。
bash
sar -u -s 14:00:00 -e 14:30:00
sar -q -s 14:00:00 -e 14:30:00
如果读取指定文件:
bash
sar -u -f /var/log/sa/sa10
不要假设系统一定保留了历史数据。sar 未启用时无法事后恢复之前的 CPU 采样,应转向监控平台、应用日志和变更记录。
四、从进程定位到线程
先按 CPU 使用率列出进程,并保留完整参数:
bash
ps -eo pid,ppid,user,stat,psr,pcpu,pmem,etime,lstart,comm,args --sort=-pcpu | head -n 30
这里需要关注的不只是 %CPU:
-
PID、
PPID用于判断进程归属和父子关系。 -
PSR表示进程最近运行过的逻辑 CPU,不代表绑定关系。
-
STAT中
R表示正在运行或可运行,D表示不可中断睡眠,Z表示僵尸进程。 -
ETIME、
LSTART用于判断进程是否与故障窗口或发布操作对齐。 -
ARGS可帮助区分同名进程,但命令行可能包含敏感参数,归档和分享前需要脱敏。
假设已确认目标 PID 为 $PID,先把它替换成实际数字:
bash
PID=12345
ps -p "$PID" -o pid,ppid,user,stat,psr,pcpu,pmem,etime,lstart,cmd
readlink -f "/proc/$PID/exe"
tr '\0' ' ' < "/proc/$PID/cmdline"; printf '\n'
cat "/proc/$PID/status"
cat "/proc/$PID/limits"
PID=12345 是占位示例,必须替换为现场确认的 PID;不要复制后直接操作未知进程。
多线程程序还要继续定位线程。只看进程总 CPU,无法区分单线程死循环和线程池整体繁忙。
bash
top -H -p "$PID"
pidstat -t -u -p "$PID" 1 10
ps -L -p "$PID" -o pid,tid,psr,stat,pcpu,comm --sort=-pcpu
TID 是 Linux 线程 ID。Java 线程转储中的 nid 常以十六进制显示,可把十进制 TID 转换后匹配:
bash
TID=12367
printf '0x%x\n' "$TID"
对于 Java 应用,可在 JDK 工具可用且权限允许时采集线程转储:
bash
jcmd "$PID" Thread.print -l > "/tmp/jcmd-thread-$PID-$(date +%Y%m%d-%H%M%S).txt"
jcmd 必须以有权附加到目标 JVM 的用户运行。线程转储会增加短时开销,文件也可能包含业务类名、路径和锁信息,应按敏感诊断数据管理。不要默认用 kill -3,因为其输出位置取决于 JVM 标准错误流和服务配置,容易找不到或冲击日志。
五、围绕不同 CPU 类型建立证据链
场景一:用户态 CPU 高
用户态高通常来自计算密集逻辑、异常循环、请求量上升、序列化压缩、正则回溯、加密或垃圾回收等。不能只因为某个进程排在第一就认定代码有问题,还要同时回答:请求量是否增长、单请求耗时是否变化、线程热点是否稳定、变更时间是否吻合。
可以先观察进程级上下文切换和缺页:
bash
pidstat -u -r -w -p "$PID" 1 10
如需函数级热点,Linux perf 是常用工具,但它可能受 kernel.perf_event_paranoid、调试符号、容器权限和内核配置限制。先做短时、低频采样:
bash
perf top -p "$PID"
或保存 30 秒采样:
bash
perf record -F 99 -g -p "$PID" -- sleep 30
perf report
perf record 会写入当前目录下的 perf.data,执行前确认目录空间和数据合规要求。采样会带来额外开销,影响范围是目标进程及采样所在主机;应先在单实例灰度,限制频率和持续时间。若业务延迟、CPU 或系统稳定性进一步恶化,立即终止采样并删除本次诊断任务,而不是继续扩大采样范围。
场景二:内核态 CPU 高
%sys 或 %soft 高时,先看是否伴随高系统调用率、上下文切换、网络包率或内核日志异常。
bash
pidstat -w -p ALL 1 10
cat /proc/softirqs
cat /proc/interrupts
journalctl -k --since '-30 min' --no-pager
两次读取 /proc/softirqs 的差值比单次累计值更有意义。不要看到某个 CPU 的累计数字大就直接下结论,因为累计值受运行时长和中断亲和性影响。
如果怀疑系统调用风暴,可短时间统计目标进程:
bash
timeout 15s strace -f -c -p "$PID"
附加 strace 会影响目标进程时序,对高频系统调用程序影响尤其明显。影响范围是目标 PID 及其被跟踪线程;执行前应确认有可替代实例、当前错误率和延迟仍可接受,并先在灰度实例验证。生产主实例上不应长时间运行逐行 strace。出现延迟放大时可按 Ctrl-C 终止,或由 timeout 自动结束;终止跟踪不会终止被跟踪进程。
场景三:I/O wait 高但被误判成 CPU 高
如果 %iowait、b、块设备延迟同时上升,CPU 图表的“非空闲”比例可能很高,但根因应转向存储链路。
bash
iostat -xz 1 10
pidstat -d -p ALL 1 10
关注 iostat 中的 r/s、w/s、r_await、w_await、队列和设备忙碌程度,但字段会随 sysstat 版本和设备类型变化。%util 接近 100% 对传统单队列设备有参考意义,对现代并行存储、RAID 和虚拟块设备不能单独用于判定饱和。必须与应用延迟、吞吐、队列、内核日志和存储侧指标交叉验证。
场景四:虚拟机 steal 高
当 %steal 持续升高,而来宾系统没有对应的高 CPU 进程,说明虚拟 CPU 在等待宿主机调度。证据应包含 mpstat/sar 的 steal 时间、云监控或虚拟化平台的宿主资源争用信息,以及同一时间的业务延迟。修复可能涉及迁移实例或调整宿主资源,这属于有中断或容量风险的操作,必须走云平台或虚拟化平台的变更流程。
六、容器和 Kubernetes 场景不能只看宿主机
先确认进程是否位于 cgroup 中:
bash
cat "/proc/$PID/cgroup"
systemd-cgls
systemd-cgtop
cgroup v2 可读取目标 cgroup 下的 cpu.stat、cpu.max 和 cpu.pressure。路径必须根据 /proc/$PID/cgroup 实际结果确定,不能把示例路径原样复制:
bash
CGROUP_PATH=/sys/fs/cgroup/example.slice/example.service
cat "$CGROUP_PATH/cpu.stat"
cat "$CGROUP_PATH/cpu.max"
cat "$CGROUP_PATH/cpu.pressure"
CGROUP_PATH 是占位符,需要替换为目标进程实际所属的 cgroup 目录。cpu.max 的第一个值为配额、第二个值为周期;max 表示未设置配额。cpu.stat 中是否包含 nr_throttled、throttled_usec 等字段与 cgroup 版本和内核有关。
Kubernetes 中必须明确命名空间。以下以 $NAMESPACE 和 $POD 为占位符,先替换后执行:
bash
NAMESPACE=production
POD=example-pod
kubectl -n "$NAMESPACE" top pod "$POD" --containers
kubectl -n "$NAMESPACE" describe pod "$POD"
kubectl -n "$NAMESPACE" get pod "$POD" -o yaml
kubectl top 依赖 Metrics Server,只提供近期资源值,不等同于历史监控。检查容器 resources.requests.cpu 和 resources.limits.cpu,再结合运行时或监控中的节流指标。Prometheus 常见指标包括 container_cpu_usage_seconds_total、container_cpu_cfs_throttled_seconds_total 和 container_cpu_cfs_throttled_periods_total,但必须注明:以实际 exporter 暴露的指标为准。
高节流不一定说明节点 CPU 耗尽,它可能只是容器 CPU limit 太低。修改 Deployment 的资源限制会触发工作负载滚动更新,影响范围是该控制器管理的 Pod;执行前检查副本数、PodDisruptionBudget、可用容量、发布策略和当前错误率。先修改测试环境或单个灰度工作负载,验证吞吐、延迟、节流和节点余量后再扩大范围。回滚应恢复原清单或使用版本化发布系统回退到上一版本,不能依赖临时手工修改。
七、如何形成可复核的根因结论
一个合格结论至少包含四类证据:
- 时间证据:CPU 异常与发布、流量、任务或依赖异常发生在同一时间窗口。
- 资源证据:
mpstat、vmstat、pidstat或监控表明 CPU 时间具体消耗在哪一类。 - 对象证据:进程、线程、容器或函数热点稳定指向同一执行路径。
- 反证排除:存储等待、虚拟化 steal、CPU 配额节流或监控口径错误已被检查。
例如,不能写“某 Java 进程 CPU 最高,所以它是根因”。更完整的表达应是:在告警窗口内整机 %usr 持续升高,运行队列超过逻辑 CPU 数;目标 JVM 占用主要 CPU,固定线程在多次采样中保持高占用;线程转储和采样热点指向同一代码路径;同时请求量、I/O wait 和 steal 未出现同幅变化。是否进一步归因到具体代码缺陷,还要结合应用日志、调用参数和版本变更确认。
八、修复时先选最低风险动作
修复动作应与证据对应,常见顺序是:
- 异常流量导致:在已有网关或负载均衡机制中限流、降级或隔离特定入口。
- 后台任务导致:通过任务系统暂停或降低并发,而不是直接杀掉所有工作进程。
- CPU limit 过低:在确认节点容量后调整单个工作负载并灰度发布。
- 新版本代码热点:回滚到已验证版本,保留现场样本供后续修复。
- 单实例失控且无在线控制手段:先摘除流量,再考虑优雅停止或重启。
重启服务属于高风险操作,必须补齐以下控制项:
-
影响范围
:明确是单实例、整组服务还是有状态节点,确认是否会中断连接、丢失内存队列或触发选主。
-
执行前检查
:确认健康实例和剩余容量,检查负载均衡摘流、在途任务、依赖方重试策略及当前错误率。
-
备份方式
:保存诊断采样、服务日志、配置版本、线程转储和必要的核心转储;有状态服务按其官方流程备份数据。
-
灰度方法
:只处理一个已摘流实例,优先使用应用提供的优雅退出机制。systemd 服务可先查看
systemctl status SERVICE和单元配置,SERVICE必须替换为真实单元名。 -
验证方式
:检查进程启动状态、健康检查、错误率、P95/P99 延迟、吞吐、CPU、运行队列和业务功能。
-
回滚方案
:若重启包含版本或配置变更,应能恢复原制品和原配置;若实例无法恢复,重新摘流并将流量切回健康实例。
不要把 kill -9 当成常规修复。SIGKILL 不给进程清理资源、刷新缓冲区或完成优雅退出的机会。只有在优雅停止失败、影响范围明确并且数据一致性方案已确认时,才考虑强制终止。
九、修复后的验证不能只看 CPU 降了
修复后至少观察一个覆盖正常业务波动的窗口。短暂下降可能只是流量被切走或进程刚启动尚未预热。
bash
mpstat -P ALL 1 10
vmstat 1 10
pidstat -u -r -d -w 1 10
同时验证:
- CPU 的
%usr、%sys、%iowait、%steal是否恢复到同类时段基线。 - 运行队列和上下文切换是否回落。
- 目标进程或线程热点是否消失。
- 请求量是否恢复,避免用“没有流量”解释资源下降。
- 错误率、超时率、吞吐和 P95/P99 延迟是否正常。
- 容器 CPU 节流是否改善,节点是否仍有足够余量。
- 修复后是否出现内存、磁盘或下游依赖压力转移。
若修复无效,应按预案恢复原配置或原版本,并重新检查最初假设。回滚本身也要验证业务健康,不能以命令返回成功作为完成标准。
十、复盘与长期治理
复盘应保留原始证据和明确时间线,而不是只记录“重启后恢复”。建议补齐以下能力:
- 为 CPU 用户态、内核态、I/O wait、steal、运行队列和容器节流分别建立监控,避免一个总 CPU 图覆盖所有语义。Prometheus 指标以实际 exporter 暴露的指标为准。
- 保留足够时间的进程级或容器级历史数据,并统一主机、容器、应用和发布系统的时钟。
- 对定时任务、批处理和线程池配置建立变更审计,避免并发参数无上限增长。
- 为服务准备可验证的限流、降级、摘流、优雅退出和版本回滚流程。
- 在压测或预发布环境建立 CPU 基线,记录正常请求量下的 CPU、延迟、吞吐和运行队列。
- 对高 CPU 告警增加持续时间和业务指标关联,减少单个瞬时采样造成的误报。
CPU 100% 不是根因描述,而是调查入口。可靠的排查路径是先确认口径和时间窗口,再判断 CPU 时间类型,从系统定位到进程和线程,最后用日志、指标、采样热点与变更记录闭环。只有证据链能够被其他工程师复核,修复和复盘才真正有价值。
文末阅读福利
仅目前来说,无论是运维人转型提升,还是零基础想转行IT,最好的岗位就是云计算运维&SRE岗位。
为了帮助大家早日快速入门云计算运维领域,给大家整理了一套【最新运维资料】高级运维工程师必备技能资料包(文末一键免费领取),内容有多详实丰富看下图!
1.38张最全工程师技能图谱
2.面试大礼包
3.Linux书籍
内容比较多,就不一一展示了
以上所有资料获取请扫码:
识别上方二维码
备注:2026最新运维资料
100%免费领取
(是扫码领取,不是在公众号后台回复,别看错了哦)
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:马哥Linux运维 点击关注👉 点击关注👉《Linux 服务器 CPU 飙到 100%,我是如何一步步定位的?》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论