突然無法訪問? 阿里雲 ECS CPU 跑滿(100%)排查與優化教程
網站白天還好好的,傍晚突然卡死,瀏覽器一直轉圈,最後報個「504 Gateway Timeout」或者「無法連接」。
心裡一驚,趕緊連上阿里雲控制台,一看 ECS 實例監控:
CPU 滿載,拉了一條 100% 的紅線。
這種場景,絕大多數個人站長和運維開發都遇到過。 遇到這種情況先別慌,也別急著去重啟服務器(重啟只能治標,幾分鐘後 CPU 還是會爆)。 今天不聊虛的理論,直接給一套
在線生產環境的排查與優化軍規
,跟著步驟走,5分鐘揪出幕後黑手。
核心排查思路:三步定位法
當 CPU 跑滿時,我們的排查邏輯應該是:
看整體:到底是哪一個進程(Nginx、PHP、java 還是木馬)吞掉了資源?
看局部: 這個進程裡的哪段代碼、哪個執行緒 (Thread)或哪個 SQL 在瘋狂空轉?
下重手:定位後,是該優化代碼、加緩存,還是直接殺掉進程?
第一步:登錄服務器,揪出問題進程(1分鐘)
不管網站卡成什麼樣,只要 SSH 還能連上,立刻連進去。 如果本地 SSH 已經卡死連不上了,直接通過阿里雲控制台
$\Rightarrow$
ECS 實例
$\Rightarrow$
遠程連接(Workbench)強行登錄。
輸入以下命令,這是 Linux 排查性能的終極利器:
貝殼腳本
Top
進入
Top
界面後,按下大寫的
P
(按 CPU 使用率排序)。 你會看到類似下面的動態列表:
純文字
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 data
、
Sorting for group
或
Creating 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
。 輸入命令查看該進程下哪些線程最耗資源:
貝殼腳本
Top -Hp 12345
按
P
排序,假如抓到了最耗 CPU 的線程 PID 是
12366
。
2. 進製轉換
將線程 PID
12366
轉換成十六進製:
貝殼腳本
Printf "%x\n" 12366
# 輸出結果為:304e
3. 打印堆棧信息
利用 JDK 自帶的
Jstack
工具,直接定位到出問題的代碼行:
貝殼腳本
Jstack 12345 | grep "304e" -A 20
終端會直接打印出這一段線程正在執行的 Java 代碼類名和行號。 過去一看,絕對是個死循環或者沒設置邊界的遞歸。 改掉代碼,重新部署即可。
場景 C:Command 是
nginx
/
Php-fpm
(遭遇惡意刷量/CC攻擊)
如果平時流量很小,突然 CPU 爆了,看一眼 Nginx 的訪問日誌。
1. 統計近期訪問最高的 IP
貝殼腳本
# 假設你的 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 踢進黑名單:
貝殼腳本
# 使用 iptables 封禁
Iptables -I INPUT -s 惡意的IP地址 -j DROP
如果使用了阿里雲,直接去 ECS 的「安全組規則」里,加一條入方向的拒絕(Drop)規則。
場景 D:意外的陌生進程(服務器淪為肉雞/挖礦)
如果看到一些奇奇怪怪的進程,占用了 99% 的 CPU,且順著路徑找不到正規軟件。
順藤摸瓜:使用 ls -l /proc/進程PID/exe 查看這個惡意程序的藏身之處。
斬草除根:bashkill -9 進程PID # 強行殺死進程 rm -rf 惡意程序路徑 # 刪除病毒文件
檢查後門:黑客通常會寫定時任務。 輸入 crontab -l 查看有沒有自動下載病毒的定時腳本,有的話用 crontab -e 統統刪掉。
終極防範:如何避免下次再長紅線?
冷汗出過之後,我們需要做一些基礎的防禦和限流措施,別讓 CPU 再有機會當滿分選手。
利用阿里雲「雲監控」配置報警不要等用戶反饋打不開了才去排查。 在阿里云云監控里,設置一條規則:「當 ECS CPU 利用率大於 85% 持續 5 分鐘,立刻發送短信/釘釘報警」。 在剛有苗頭的時候就介入。
配置 PHP-FPM / Nginx 的最大工作進程
如果服務器是 2核4G,就把 php-fpm.conf 里的 max_children 限制在 30-40 左右。 這樣哪怕流量刷爆,也只是部分用戶提示 502,服務器底層不會因為內存和 CPU 被徹底榨乾而導緻 SSH 連不上。
合理利用「彈性伸縮」如果你的網站或應用確實是在搞活動、或者因為上熱搜迎來了真正的「潑天流量」,單機怎麼優化都沒用。 趕緊去阿里雲開通 彈性伸縮(ESS),配置一條規則:當 CPU 超過 80% 時,全自動幫你克隆並按量計費拉起第二台、第三台 ECS 分擔流量,活動結束後自動釋放。 用技術複利來對抗流量無常。

