做站长这些年,最怕半夜收到监控告警:某台云服务器 CPU 使用率持续 100%,网站打开慢如蜗牛,SSH 连上去都卡半天。很多朋友第一反应是重启,重启确实能续命几分钟,但过不了多久又飙红。治标不治本,今天笔者就把自己踩过的坑和一套完整的排查思路分享出来,从快速定位到彻底解决,手把手带你搞定这个疑难杂症。
先别慌,这几个问题你大概率遇到过
💡 推荐阅读:云服务器选型横评:个人博客与小型团队项目到底需要什么配置?
Q1:CPU 100% 但 top 里看不到高占用进程,怎么回事?
这是最让人抓狂的情况。你用 top 看,每个进程 CPU 都不高,但整体负载就是满的。常见原因有两个:一是 被挖矿木马做了进程隐藏,比如通过 rootkit 篡改 ps/top 的输出;二是 大量短生命周期进程,比如被 CC 攻击时每个请求 fork 一个进程,top 刷新间隔根本抓不到。这时候你需要用 htop 或者直接看 /proc 目录下的进程状态来交叉验证。
Q2:CPU 高但流量不大,是不是云厂商超售了?
有可能,但先别急着甩锅。笔者遇到过不少案例,其实是磁盘 I/O 等待(iowait)被算进了 CPU 负载。用 top 看 %wa 那一列,如果超过 20%,说明瓶颈在磁盘不在 CPU。另外,云服务器如果用的是共享型实例,确实存在资源争抢,但正规厂商的独享型实例基本不会。选服务器时别贪便宜买那种几块钱一个月的超售机器,后面有的是坑。
Q3:重启后正常,过一会又 100%,怎么根治?
这说明有定时任务或守护进程在反复拉起问题程序。挖矿木马通常会写 crontab、systemd service 或者 LD_PRELOAD 劫持。你只 kill 进程没用,必须找到它的持久化入口一并清理。后面我会给出具体的排查命令。
Q4:CPU 100% 会导致数据丢失吗?
会。如果数据库(MySQL/Redis)因为 CPU 抢占导致响应超时,连接池打满,严重时可能触发主从切换或数据写入失败。所以定位要快,但千万不要在没搞清楚状况前直接 kill -9 数据库进程,先做好快照或备份。
根因深度剖析:CPU 100% 的五大元凶
💡 延伸阅读:别再花冤枉钱!云服务器2核2G和2核4G真实跑分对比,看完再买不踩坑
根据笔者处理过的几十个案例,CPU 长期满载基本逃不出下面五类原因:
| 类型 | 典型特征 | 排查入口 |
|---|---|---|
| 恶意挖矿 | CPU 占满但进程名伪装成 kworker 等 | crontab、systemd、/tmp 目录 |
| CC/DDoS 攻击 | 大量 ESTABLISHED 连接,来源 IP 集中 | netstat、nginx 访问日志 |
| 代码死循环 | 单个进程 CPU 持续 90%+ | top -Hp、strace |
| 配置不当 | PHP-FPM 进程数过多、MySQL 全表扫描 | 慢查询日志、进程数配置 |
| 磁盘 I/O 瓶颈 | %wa 高,负载高但 CPU 空闲 | iostat、iotop |
实操步骤:从定位到清除
💡 深度技术指南:Docker 日志把磁盘吃满了?别慌,手把手教你清理并永久限制大小
第一步:快速锁定高占用进程
登录服务器后,先别急着操作,按顺序执行:
# 查看整体负载和 CPU 分布
top -c
# 按 CPU 排序,找出罪魁祸首
top -b -n 1 | head -20
# 如果怀疑进程隐藏,用 /proc 交叉验证
ls /proc/*/exe -l | grep deleted
如果发现某个进程的 exe 链接指向 (deleted),基本可以确定是恶意程序——正常服务不会把可执行文件删掉还在跑。
第二步:深挖进程背后的东西
假设你锁定了一个可疑 PID,比如 12345:
# 查看该进程打开的文件和网络连接
lsof -p 12345
# 查看进程的启动命令和父进程
cat /proc/12345/cmdline | tr '\0' ' '
ps -ef | grep 12345
# 查看该进程的所有线程 CPU 占用
top -Hp 12345
如果发现它连接了一个陌生的境外 IP,或者打开的是一堆 /tmp/xxx 文件,那不用怀疑,就是挖矿木马。
第三步:清除持久化后门
直接 kill 进程只是第一步,必须清理它的自启动入口:
# 检查所有用户的 crontab
for user in $(cut -f1 -d: /etc/passwd); do crontab -u $user -l 2>/dev/null; done
# 检查系统级定时任务
cat /etc/crontab
ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/
# 检查 systemd 服务
systemctl list-units --type=service | grep -v systemd
# 检查 LD_PRELOAD 劫持
cat /etc/ld.so.preload
发现可疑条目后,先注释掉或删除,再 kill 进程,最后重启服务器观察。如果条件允许,重装系统是最彻底的方案,但重装前务必确认数据已备份。
第四步:如果是攻击导致的高负载
检查连接数:
# 统计 ESTABLISHED 连接来源 IP
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20
如果某个 IP 连接数上百,直接封禁:
iptables -I INPUT -s 恶意IP -j DROP
同时检查 Nginx 访问日志,看看是不是被 CC 攻击了。如果是,建议上 CDN 或者配置 limit_req 限流模块。这里多说一句,选云服务器时尽量选带基础 DDoS 防护的,别等被打穿了才后悔。
第五步:如果是配置问题
PHP 站点重点看 PHP-FPM 的 pm.max_children,设太大容易把 CPU 吃满;MySQL 重点看慢查询:
# 开启慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
找到慢 SQL 后加索引或优化查询,CPU 负载通常能降一半。
预防建议:别等出事再救火
- 装个靠谱的监控:云厂商自带的监控够用,但建议再配个
netdata或Prometheus,能看历史趋势,方便定位突发峰值。 - 定期检查 crontab 和 systemd:笔者习惯每周扫一眼,花不了两分钟,能省大麻烦。
- SSH 别用弱密码:挖矿木马九成是通过暴力破解进来的,密钥登录 + 改端口 + fail2ban 是标配。
- 选服务器别只看价格:CPU 长期跑满的另一个隐藏原因是实例本身性能太差,共享型实例跑生产环境就是给自己找罪受。预算允许的话,直接上计算型或通用型独享实例,省下的排查时间远比差价值钱。
- 做好快照和备份:不管是云厂商的快照还是自己 rsync,关键时刻能救命。
CPU 100% 这事,说难也难,说简单也简单。核心就一句话:先定位,再清除,最后堵住入口。最怕的是一上来就重启,把现场破坏了,问题反复出现却找不到根因。希望这篇排查指南能帮你少走弯路,下次再遇到告警,从容打开终端,一步步搞定它。