突然无法访问?阿里云 ECS CPU 跑满(100%)排查与优化教程

cloud 2026-05-28 阅读 9
cloud


网站白天还好好的,傍晚突然卡死,浏览器一直转圈,最后报个“504 Gateway Timeout”或者“无法连接”。

心里一惊,赶紧连上阿里云控制台,一看 ECS 实例监控:CPU 满载,拉了一条 100% 的红线。

这种场景,绝大多数个人站长和运维开发都遇到过。遇到这种情况先别慌,也别急着去重启服务器(重启只能治标,几分钟后 CPU 还是会爆)。今天不聊虚的理论,直接给一套在线生产环境的排查与优化军规,跟着步骤走,5分钟揪出幕后黑手。

核心排查思路:三步定位法

当 CPU 跑满时,我们的排查逻辑应该是:

  1. 看整体:到底是哪一个进程(Nginx、PHP、Java 还是木马)吞掉了资源?
  2. 看局部:这个进程里的哪段代码、哪个线程(Thread)或哪个 SQL 在疯狂空转?
  3. 下重手:定位后,是该优化代码、加缓存,还是直接杀掉进程?

第一步:登录服务器,揪出问题进程(1分钟)

不管网站卡成什么样,只要 SSH 还能连上,立刻连进去。如果本地 SSH 已经卡死连不上了,直接通过阿里云控制台 $\rightarrow$ ECS 实例 $\rightarrow$ 远程连接(Workbench)强行登录。

输入以下命令,这是 Linux 排查性能的终极利器:

Bash


top

进入 top 界面后,按下大写的 P(按 CPU 使用率排序)。你会看到类似下面的动态列表:

Plaintext


PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
12345 nginx     20   0  354m  45m  12m R  98.5   2.3   12:34.56 php-fpm
 6789 mysql     20   0 2.5g 1.2g  24m S   1.5  60.2   45:12.89 mysqld

结果分析:

看看排在第一行的 COMMAND 是什么:

  • 如果是 php-fpm 或 node 或 java:说明是你网站的业务代码遭遇了死循环、或者是突发大流量导致性能扛不住。
  • 如果是 mysqld:说明是数据库遭遇了慢查询、索引缺失,或者高并发锁死。
  • 如果是 nginx 或 httpd:大概率是遭遇了恶意刷量、CC 攻击或爬虫疯狂抓取。
  • 如果是字母数字乱码(如 kdevtmpfsi、miner):别想了,服务器被黑了,被抓去当矿工挖矿了。

第二步:深入细分场景,精准拆弹(3分钟)

根据你在 top 里看到的结果,对号入座选择下面的解决路径。

场景 A:Command 是 mysqld(数据库卡死)

这是高频发生的场景。通常是因为某段业务代码写得太垃圾,查了几十万行的数据没加索引。

1. 登录数据库查看当前执行的 SQL

在终端登录你的 MySQL:

SQL


mysql -u root -p
-- 登录后执行
SHOW PROCESSLIST;

如果提示列表太长显示不全,可以用:

SQL


SHOW FULL PROCESSLIST;

2. 抓出内鬼

在输出的列表里,观察 Time(执行时间)很长,且 State 显式为 Sending dataSorting for groupCreating tmp table 的那一行。看看它的 Info 栏写的是什么 SQL 语句。

  • 紧急避险:看到那条让人吐血的慢 SQL,记住它的 Id,直接运行 KILL Id;(例如 KILL 142;),先把数据库释放出来,网站立刻就能恢复访问。
  • 根治方案:拿着这条 SQL 去代码里找原因,赶紧给 WHERE 或 JOIN 后面的字段加上索引;如果是大表关联,考虑加 Redis 缓存。

场景 B:Command 是 java(程序内部死循环/OOM)

Java 应用 CPU 飙高,通常是某个线程陷入了 while(true) 的死循环,或者在频繁进行垃圾回收(Full GC)。

1. 揪出最耗 CPU 的线程

假设 Java 的进程 PID 是 12345。输入命令查看该进程下哪些线程最耗资源:

Bash


top -Hp 12345

P 排序,假如抓到了最耗 CPU 的线程 PID 是 12366

2. 进制转换

将线程 PID 12366 转换成十六进制:

Bash


printf "%x\n" 12366
# 输出结果为:304e

3. 打印堆栈信息

利用 JDK 自带的 jstack 工具,直接定位到出问题的代码行:

Bash


jstack 12345 | grep "304e" -A 20

终端会直接打印出这一段线程正在执行的 Java 代码类名和行号。过去一看,绝对是个死循环或者没设置边界的递归。改掉代码,重新部署即可。

场景 C:Command 是 nginx / php-fpm(遭遇恶意刷量/CC攻击)

如果平时流量很小,突然 CPU 爆了,看一眼 Nginx 的访问日志。

1. 统计近期访问最高的 IP

Bash


# 假设你的 Nginx 日志在 /var/log/nginx/access.log
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -n 20

如果发现某个陌生 IP 在几分钟内刷了成万上万次,毫无疑问,你被盯上了。

2. 紧急封禁 IP

直接利用 Linux 自带的防火墙或者阿里云安全组,把这个 IP 踢进黑名单:

Bash


# 使用 iptables 封禁
iptables -I INPUT -s 恶意的IP地址 -j DROP

如果使用了阿里云,直接去 ECS 的“安全组规则”里,加一条入方向的拒绝(Drop)规则。

场景 D:意外的陌生进程(服务器沦为肉鸡/挖矿)

如果看到一些奇奇怪怪的进程,占用了 99% 的 CPU,且顺着路径找不到正规软件。

  1. 顺藤摸瓜:使用 ls -l /proc/进程PID/exe 查看这个恶意程序的藏身之处。
  2. 斩草除根:Bashkill -9 进程PID # 强行杀死进程 rm -rf 恶意程序路径 # 删除病毒文件
  3. 检查后门:黑客通常会写定时任务。输入 crontab -l 查看有没有自动下载病毒的定时脚本,有的话用 crontab -e 统统删掉。

终极防范:如何避免下次再长红线?

冷汗出过之后,我们需要做一些基础的防御和限流措施,别让 CPU 再有机会当满分选手。

  1. 利用阿里云“云监控”配置报警不要等用户反馈打不开了才去排查。在阿里云云监控里,设置一条规则:“当 ECS CPU 利用率大于 85% 持续 5 分钟,立刻发送短信/钉钉报警”。在刚有苗头的时候就介入。
  2. 配置 PHP-FPM / Nginx 的最大工作进程如果服务器是 2核4G,就把 php-fpm.conf 里的 max_children 限制在 30-40 左右。这样哪怕流量刷爆,也只是部分用户提示 502,服务器底层不会因为内存和 CPU 被彻底榨干而导致 SSH 连不上。
  3. 合理利用“弹性伸缩”如果你的网站或应用确实是在搞活动、或者因为上热搜迎来了真正的“泼天流量”,单机怎么优化都没用。赶紧去阿里云开通 弹性伸缩(ESS),配置一条规则:当 CPU 超过 80% 时,全自动帮你克隆并按量计费拉起第二台、第三台 ECS 分担流量,活动结束后自动释放。用技术复利来对抗流量无常。


1
← 返回新闻中心