上海阿里云代理商:阿里云ECS CPU滿載診斷修復(fù)全指南
收到ECS CPU使用率驟升至100%的告警,登錄服務(wù)器卻發(fā)現(xiàn)連敲一條命令都卡頓數(shù)秒——這種場(chǎng)景在生產(chǎn)環(huán)境中幾乎不可避免。排查的關(guān)鍵不在于“有沒(méi)有高消耗的進(jìn)程”,而在于能否在系統(tǒng)瀕臨僵死時(shí)快速采集到一組時(shí)間戳對(duì)齊、維度完整的現(xiàn)場(chǎng)數(shù)據(jù)。一份設(shè)計(jì)得當(dāng)?shù)?strong>阿里云ECS CPU滿載排查腳本,能做到比人工敲命令更早觸及根因,且避免遺漏那些一閃而過(guò)的短命進(jìn)程。
一、如何快速定位CPU滿載原因
1. 核心監(jiān)控命令詳解
瓶頸分析不能只盯一個(gè)top。mpstat -P ALL能分別展示每顆邏輯核的占用,可以立刻判斷是全核打滿還是單核熱鎖。配合pidstat按進(jìn)程采樣,比top更適合抓取瞬間飆升的短時(shí)任務(wù)。多數(shù)人忽略的一點(diǎn):CPU使用率拆項(xiàng)中的%wa若持續(xù)超過(guò)30%,問(wèn)題根源常不在計(jì)算,而在磁盤I/O,此時(shí)進(jìn)一步查iostat -x比死磕進(jìn)程列表有效得多。
2. 進(jìn)程資源占用分析
排查腳本抓取進(jìn)程樣本時(shí),必須規(guī)避視覺(jué)欺騙。挖礦程序常偽裝成[kworker]或[kthread]這類系統(tǒng)線程名,僅靠名稱判斷極容易漏過(guò)。更可靠的做法是交叉比對(duì)進(jìn)程的可執(zhí)行文件路徑、父進(jìn)程關(guān)系和啟動(dòng)時(shí)間——異常進(jìn)程往往由crontab或隱蔽守護(hù)進(jìn)程拉起,啟動(dòng)時(shí)間與告警起始高度吻合。腳本在采集/proc/[pid]/exe和/proc/[pid]/status時(shí)若能標(biāo)記出這類特征,排查效率會(huì)有數(shù)量級(jí)的提升。
3. 系統(tǒng)日志排查要點(diǎn)
CPU滿載的誘因不止于進(jìn)程自身,內(nèi)核層面的OOM Killer活動(dòng)、硬件錯(cuò)誤(MCE)同樣會(huì)讓系統(tǒng)陷入資源爭(zhēng)搶。/var/log/messages或/var/log/syslog里記錄的Out of memory、mce: [Hardware Error]條目,往往是進(jìn)程異常飆升的前奏。腳本應(yīng)在前三秒內(nèi)從日志尾部截取最近500行關(guān)鍵字匹配結(jié)果,并與CPU飆升時(shí)間線對(duì)齊——這一步驟換作人工幾乎不可能在系統(tǒng)卡死的窗口期內(nèi)完成。
二、常見(jiàn)CPU滿載場(chǎng)景與根因
CPU滿載并非單一面孔的問(wèn)題。在云上運(yùn)維場(chǎng)景中,我們觀察到至少三類截然不同的觸發(fā)路徑,它們指向的根因與修復(fù)策略完全不同。將三者混為一談就急于重啟或擴(kuò)容,是被動(dòng)運(yùn)維的典型特征。
1. 應(yīng)用代碼死循環(huán)
這是最直觀但也最容易被誤判的場(chǎng)景。典型表現(xiàn)為單核或全部核心的 %us(用戶態(tài)CPU)持續(xù)接近100%,而 %wa(I/O等待)與 %sy(內(nèi)核態(tài))占比極低。負(fù)載值則取決于死循環(huán)發(fā)生在幾核——如果是單線程死鎖,8核實(shí)例的load average可能僅顯示1.0左右,這就容易制造“負(fù)載正常但系統(tǒng)卡死”的矛盾現(xiàn)場(chǎng)。
死循環(huán)的根因排查有一個(gè)經(jīng)典的“三無(wú)”困境:自研應(yīng)用無(wú)日志、無(wú)異常堆棧輸出、開(kāi)發(fā)人員無(wú)法第一時(shí)間到場(chǎng)。此時(shí) perf top 的價(jià)值就凸顯出來(lái)——它能繞開(kāi)應(yīng)用層的日志空白,實(shí)時(shí)抓取CPU熱點(diǎn)函數(shù)的符號(hào)名稱。某次生產(chǎn)事故中,我們正是通過(guò) perf top -g 抓到某Java應(yīng)用的 HashMap.get() 函數(shù)占用率異常飆升,才定位到是特定格式的入?yún)⒂|發(fā)了JDK 8早期版本在鏈表轉(zhuǎn)紅黑樹(shù)時(shí)的退化bug。沒(méi)有perf,那次排查至少要多耗掉兩個(gè)小時(shí)的推測(cè)與灰度驗(yàn)證。
另一個(gè)容易被忽略的點(diǎn)是:死循環(huán)不一定出在業(yè)務(wù)代碼本身。不合理的正則表達(dá)式回溯、JSON解析庫(kù)對(duì)畸形數(shù)據(jù)的災(zāi)難性回溯、ORM框架在特定關(guān)聯(lián)查詢下的笛卡爾積展開(kāi),都曾在生產(chǎn)環(huán)境制造過(guò)CPU滿載事故,而這些代碼路徑通常不會(huì)打印業(yè)務(wù)日志。
2. 內(nèi)存泄漏與交換
CPU滿載的第二條路徑繞了個(gè)彎——問(wèn)題本不在CPU,而在內(nèi)存。當(dāng)應(yīng)用發(fā)生漸進(jìn)式內(nèi)存泄漏,可用內(nèi)存被蠶食殆盡后,內(nèi)核被迫將很少使用的內(nèi)存頁(yè)交換到磁盤上的swap分區(qū)。接下來(lái)的連鎖反應(yīng)是:每次被交換出去的內(nèi)存頁(yè)需要重新訪問(wèn)時(shí),都會(huì)觸發(fā)一次磁盤I/O讀回操作,而磁盤的吞吐量比內(nèi)存低幾個(gè)數(shù)量級(jí),CPU的大量時(shí)間耗費(fèi)在等待I/O完成上,表現(xiàn)為 %wa 持續(xù)偏高。
此時(shí) top 命令看到的CPU使用率可能并不夸張,但系統(tǒng)實(shí)際已經(jīng)近乎癱瘓。更隱蔽的是,如果swap交換出去的剛好是某個(gè)守護(hù)進(jìn)程的核心內(nèi)存頁(yè),該進(jìn)程被調(diào)度時(shí)會(huì)反復(fù)觸發(fā)頁(yè)面換入,造成CPU使用率的周期性脈沖式?jīng)_高——這種波形在監(jiān)控圖上很容易被誤判為定時(shí)任務(wù)觸發(fā)。
在阿里云ECS上,這種場(chǎng)景有一個(gè)天然的“報(bào)警器”:當(dāng)系統(tǒng)日志中出現(xiàn) Out of memory: Kill process 的記錄時(shí),其實(shí)已經(jīng)晚了,說(shuō)明內(nèi)核的OOM Killer已經(jīng)介入。更早的信號(hào)藏在 /proc/meminfo 的 Committed_AS 字段與 swappiness 參數(shù)的偏離程度中。一個(gè)經(jīng)驗(yàn)值:如果 Committed_AS 超過(guò)物理內(nèi)存的1.5倍且swap使用率同時(shí)在增長(zhǎng),CPU滿載只是時(shí)間問(wèn)題。
3. 異常系統(tǒng)服務(wù)
第三類場(chǎng)景帶有更強(qiáng)的隱蔽性與破壞性——非法入侵或惡意程序。挖礦木馬是其中最常見(jiàn)的一類,它們通常具備三個(gè)特征:進(jìn)程名偽裝成 [kworker]、/sbin/init 等系統(tǒng)常見(jiàn)名但運(yùn)行路徑異常;連接已知礦池的TCP端口;以及使用 cgroup 或 taskset 綁定所有CPU核心。
這類進(jìn)程的排查難點(diǎn)在于“抓不住”。普通運(yùn)維人員登錄服務(wù)器后習(xí)慣執(zhí)行 top 或 htop 觀察,但部分挖礦程序會(huì)做反偵察——它們通過(guò)劫持 ld.so.preload 動(dòng)態(tài)鏈接庫(kù),替換系統(tǒng)的 readdir、fopen 等函數(shù),使得 ps、top、lsof 等命令對(duì)自身完全透明。此時(shí)腳本內(nèi)直接讀取 /proc/[pid]/ 下的原始文件是繞開(kāi)被篡改的libc庫(kù)的唯一可靠手段。
更令人頭疼的是“死后復(fù)生”機(jī)制。挖礦程序往往配置了多重?;睿篶rontab定時(shí)拉起、systemd service單元、甚至篡改 ~/.ssh/authorized_keys 預(yù)留后門。簡(jiǎn)單 kill -9 既清理不干凈,也無(wú)法阻斷重感染路徑。腳本排查的價(jià)值在于一次性輸出“進(jìn)程快照 + 網(wǎng)絡(luò)連接 + 啟動(dòng)項(xiàng) + 計(jì)劃任務(wù)”的四維關(guān)聯(lián)視圖,讓隱藏的鏈條暴露出來(lái),而不是逐條命令手工排查。
三、手動(dòng)排查步驟與工具
在自動(dòng)化腳本尚未覆蓋、或需要人工驗(yàn)證的緊急場(chǎng)景下,掌握幾件核心工具的使用邊界,往往比擁有一堆未經(jīng)驗(yàn)證的“一鍵修復(fù)”腳本更可靠。下面拆解三個(gè)高頻且常被誤用的手動(dòng)排查方向。
1. top/htop 診斷使用
許多人打開(kāi) top 后會(huì)下意識(shí)按 P 讓進(jìn)程按 CPU 使用率排序,然后盯著排第一的進(jìn)程準(zhǔn)備 kill。這種習(xí)慣省略掉了最關(guān)鍵的一步:區(qū)分 CPU 時(shí)間到底消耗在用戶態(tài)、內(nèi)核態(tài)還是等待 I/O 上。top 默認(rèn)顯示行里的 %Cpu(s) 會(huì)給出 us(用戶態(tài))、sy(內(nèi)核態(tài))、wa(等待 I/O)、st(虛擬機(jī)被宿主機(jī)偷走的時(shí)間)等拆項(xiàng),這些數(shù)字比排序輸出更能指明方向。一次典型誤判:某次業(yè)務(wù)接口響應(yīng)變慢,運(yùn)維看到 cc1plus 進(jìn)程占用 95% CPU 便以為是編譯任務(wù)失控,實(shí)際 %wa 已經(jīng)飆到 40%,根因是 NAS 掛載點(diǎn)超時(shí)導(dǎo)致大量 D 狀態(tài)進(jìn)程堆積,CPU 負(fù)載純粹由不可中斷睡眠疊加 I/O 等待引起,殺死編譯進(jìn)程對(duì)恢復(fù)服務(wù)毫無(wú)幫助,反而讓現(xiàn)場(chǎng)更快冷卻。
在阿里云 ECS 環(huán)境里,還應(yīng)留意 %st 字段。虛擬化平臺(tái)的 CPU 超賣或宿主機(jī)爭(zhēng)搶會(huì)導(dǎo)致“CPU 竊取”,此時(shí)實(shí)例內(nèi) top 顯示 CPU 滿載,但業(yè)務(wù)線程實(shí)際獲得的物理計(jì)算資源可能不足標(biāo)稱值的一半。如果 %st 持續(xù)超過(guò) 5%,排查方向應(yīng)該從應(yīng)用邏輯轉(zhuǎn)向規(guī)格升配或遷移宿主機(jī),而不是繼續(xù)優(yōu)化代碼。
top 本身也會(huì)因系統(tǒng)卡頓而無(wú)法啟動(dòng),所以手工排查的第一條命令應(yīng)該是 sysctl -w kernel.sysrq=1 預(yù)留后手,然后對(duì)排查用的 shell 執(zhí)行 renice -n -5 -p $$ 提升優(yōu)先級(jí),避免連診斷工具都被餓死。監(jiān)控采集必須一次成組:把時(shí)間戳、load average、top 前 20 進(jìn)程列表、/proc/meminfo 的 Swap 行和進(jìn)程總數(shù)寫(xiě)入一個(gè)帶時(shí)間標(biāo)記的文本文件,而不是分段敲命令,才能保證不同指標(biāo)在同一個(gè)時(shí)間斷面上,事后對(duì)比時(shí)不至于被錯(cuò)位的數(shù)據(jù)誤導(dǎo)。
2. strace 跟蹤進(jìn)程
當(dāng)一個(gè)進(jìn)程 CPU 占用很高但既無(wú)日志輸出、又不響應(yīng)請(qǐng)求時(shí),strace 能給出它在干什么的系統(tǒng)級(jí)答案。常見(jiàn)做法是對(duì)目標(biāo) PID 執(zhí)行 strace -f -p,觀察幾秒后 Ctrl+C 停止,然后檢查輸出中高頻重復(fù)的系統(tǒng)調(diào)用模式。比如某次排查一個(gè) Java 進(jìn)程突然吃滿 CPU,開(kāi)發(fā)團(tuán)隊(duì)堅(jiān)稱沒(méi)有修改代碼,但 strace 日志里每秒出現(xiàn)上千次 futex(FUTEX_WAIT_PRIVATE, ...) = -1 EAGAIN,結(jié)合 perf top 看到 pthread_mutex_lock 占比異常,最終定位到連接池回收線程在某種邊緣條件下陷入熱循環(huán)爭(zhēng)搶鎖。
strace 對(duì)短時(shí)進(jìn)程的捕獲同樣有效,但需要換成基于觸發(fā)器的思路:for i in $(seq 1 100); do strace -p 這種快速抽樣循環(huán)或借助 auditd 記錄進(jìn)程啟動(dòng)時(shí)的系統(tǒng)調(diào)用序列,比肉眼刷 top 更容易抓到那些執(zhí)行時(shí)間不足一秒的異常腳本,這類腳本常被挖礦程序用來(lái)偽裝為系統(tǒng)服務(wù)。
注意 strace 本身會(huì)讓被跟蹤進(jìn)程性能嚴(yán)重下降,在生產(chǎn)環(huán)境使用務(wù)必加 -e trace=file,network 預(yù)篩選,或者先嘗試 perf top -p 獲取函數(shù)級(jí)熱點(diǎn),敲定可疑范圍后再用 strace 深入確認(rèn)系統(tǒng)調(diào)用參數(shù)細(xì)節(jié),避免因附加跟蹤拖垮整個(gè)服務(wù)。
3. 定時(shí)任務(wù)檢查
進(jìn)程被殺后再次自動(dòng)復(fù)活,根因多數(shù)不在進(jìn)程本身,而在于拉起的機(jī)制。常規(guī)排查 crontab 之外,更要覆蓋 systemd timer、/etc/cron.hourly、/etc/cron.d 以及用戶級(jí)的 ~/.config/autostart 等隱蔽位置。實(shí)際案例中,一個(gè)看似無(wú)害的 */5 * * * * /usr/libexec/kswapd 隱藏在系統(tǒng) crontab 注釋行里,用偽裝的“kswapd”名字讓管理員誤以為是內(nèi)核線程,實(shí)際指向一個(gè)隨機(jī)字符串命名的腳本,該腳本每 5 分鐘從礦池域名拉取最新 payload。對(duì) crontab -l 的輸出應(yīng)全文檢索 curl、wget、base64 -d、/dev/tcp 等關(guān)鍵字,而不是憑肉眼掃過(guò)去。
對(duì)于用 nohup /tmp/.X11-unix/x 這類方式常駐的守護(hù)進(jìn)程,可以結(jié)合 systemctl list-timers --all 和 ls -l /proc/*/exe | grep deleted 來(lái)暴露已經(jīng)被刪除但仍在運(yùn)行的可執(zhí)行文件。如果懷疑問(wèn)題周期性復(fù)發(fā),在 /etc/crontab 或用戶 crontab 文件頭添加一行 MAILTO="" 以去重郵件噪音,再在腳本觸發(fā)點(diǎn)添加 (date; ps auxf) >> /var/log/cron_trace.log,將拉起的完整進(jìn)程樹(shù)與原任務(wù)時(shí)間點(diǎn)綁定,下一次異常復(fù)現(xiàn)時(shí)可以直接從日志回溯,而不必重新復(fù)現(xiàn)現(xiàn)場(chǎng)。
手動(dòng)排查不是為了代替腳本,而是為編寫(xiě)真正有效的腳本積累規(guī)則和閾值。只有把“人肉定位”的經(jīng)驗(yàn)固化為程序可執(zhí)行的檢測(cè)邏輯,下一部分要講的自動(dòng)診斷腳本才不會(huì)淪為一堆 grep 的堆砌。
四、自動(dòng)化排查腳本設(shè)計(jì)
在阿里云ECS的日常運(yùn)維中,CPU滿載的黃金排查窗口往往只有幾十秒。一旦終端卡死、SSH掉線,原本可用的現(xiàn)場(chǎng)數(shù)據(jù)就會(huì)隨著生產(chǎn)環(huán)境自愈或手動(dòng)重啟而丟失。正因如此,一份設(shè)計(jì)得當(dāng)?shù)淖詣?dòng)化排查腳本,本質(zhì)上是對(duì)運(yùn)維經(jīng)驗(yàn)的結(jié)構(gòu)化沉淀,而不是簡(jiǎn)單的命令堆砌。筆者在復(fù)盤多起頭部電商、游戲公司的大促事故時(shí)發(fā)現(xiàn),那些能借助腳本在1分鐘內(nèi)完成“證據(jù)固定——根因初判——建議輸出”閉環(huán)的團(tuán)隊(duì),平均排障耗時(shí)比依賴手動(dòng)逐項(xiàng)排查的團(tuán)隊(duì)縮短了約40%。以下從三個(gè)維度拆解這類腳本的設(shè)計(jì)邏輯。
1. 腳本功能規(guī)劃
腳本的第一優(yōu)先級(jí)不是采集數(shù)據(jù),而是保證自身不被系統(tǒng)滿載拖垮。常見(jiàn)的失誤是直接進(jìn)入采集邏輯,結(jié)果腳本進(jìn)程與其他高負(fù)載進(jìn)程爭(zhēng)搶CPU,輸出空文件或半截報(bào)告,反而給排查者造成“一切正?!钡腻e(cuò)誤假象。合理的做法是在腳本頭部迅速執(zhí)行renice -n -5 -p $$降低自己的nice值,同時(shí)對(duì)輸出文件描述符開(kāi)啟行緩沖或直接關(guān)閉緩沖,確保類似top -b -n1的快照數(shù)據(jù)實(shí)時(shí)落盤,即使后續(xù)系統(tǒng)徹底僵死,分析人員也能拿到最后時(shí)刻的完整記錄。
另一個(gè)容易被忽略的功能點(diǎn)是環(huán)境依賴審查。大量生產(chǎn)ECS基于最小化鏡像構(gòu)建,極有可能未安裝atop、sysstat、perf等診斷工具。腳本若在運(yùn)行中途因命令缺失退出,現(xiàn)場(chǎng)便再難復(fù)原。因此,腳本啟動(dòng)階段應(yīng)靜默檢查top、ps、pidstat等基礎(chǔ)工具存在性,對(duì)perf、iotop等高階工具僅作有條件采集,并將缺失信息如實(shí)寫(xiě)入報(bào)告頭部。這種防御式設(shè)計(jì),能讓腳本在“裸系統(tǒng)”上也能輸出至少80%的關(guān)鍵指標(biāo),避免運(yùn)維人員因緊急安裝軟件包而被既有的變更管理制度絆住手腳。
2. 關(guān)鍵指標(biāo)采集
對(duì)于CPU故障,最危險(xiǎn)的誤判并非“找不到問(wèn)題”,而是“找錯(cuò)方向”。常見(jiàn)的誤區(qū)是只盯著/proc/loadavg中的總CPU利用率,卻不去拆解%us(用戶態(tài))、%sy(內(nèi)核態(tài))、%wa(IO等待)和%st(虛機(jī)CPU竊?。?。實(shí)際上,筆者在阿里云ECS上遇到的數(shù)十起死循環(huán)案例,初段表現(xiàn)均為%us飆升至90%以上,而一次因磁盤超限流導(dǎo)致的“假CPU滿載”中,%wa占據(jù)了近70%的CPU時(shí)間,若按應(yīng)用代碼問(wèn)題排查會(huì)白白耗費(fèi)數(shù)小時(shí)。因此,腳本中的一個(gè)核心模塊必須是調(diào)用mpstat -P ALL 1 1或直接讀取/proc/stat,將這些拆項(xiàng)數(shù)據(jù)完整提取。
同時(shí),短時(shí)進(jìn)程一直是CPU排查的盲區(qū)。僅執(zhí)行一次ps aux --sort=-%cpu | head -20很可能漏掉那些瞬間啟動(dòng)、消耗完一個(gè)核心計(jì)算資源后立即退出的“爆沖進(jìn)程”。腳本設(shè)計(jì)需要采用一次成組的采樣策略:在同一秒內(nèi),通過(guò)管道串聯(lián)獲取系統(tǒng)負(fù)載、各CPU模式消耗、內(nèi)存與交換空間占用,并連續(xù)執(zhí)行兩次間隔1秒的進(jìn)程快照,隨后對(duì)快照做增量比對(duì),自動(dòng)標(biāo)記出新出現(xiàn)的或CPU占用瞬間躍升的可疑PID。在此基礎(chǔ)上,結(jié)合阿里云ECS可能發(fā)生的CPU竊取場(chǎng)景,腳本還應(yīng)專門采集/proc/stat中的steal字段,一旦%st超過(guò)5%,就在報(bào)告中直接觸發(fā)“宿主機(jī)資源爭(zhēng)搶”的可視化告警,避免應(yīng)用團(tuán)隊(duì)與基礎(chǔ)設(shè)施團(tuán)隊(duì)之間的無(wú)謂扯皮。
3. 輸出可讀報(bào)告
緊急時(shí)刻,運(yùn)維人員最需要的不是JSON或HTML格式的炫目圖表,而是一份在less甚至cat下就能快速閱覽的純文本報(bào)告。因此,報(bào)告結(jié)構(gòu)應(yīng)嚴(yán)格遵循“總分總”邏輯:頂部用時(shí)間戳和TL;DR給出整體結(jié)論——例如“懷疑Java進(jìn)程18291陷入計(jì)算死循環(huán),%us=96.2%”,中部按“系統(tǒng)概覽、CPU拆解、進(jìn)程Top 10、短時(shí)進(jìn)程標(biāo)記、威脅特征匹配”分塊陳列原始數(shù)據(jù),底部附上“下一步建議指令”。
對(duì)威脅特征的集成尤其能體現(xiàn)腳本的實(shí)用價(jià)值。腳本可在獲取進(jìn)程列表后,以靜默方式將進(jìn)程名、命令行參數(shù)與一組可維護(hù)的特征規(guī)則做匹配——例如進(jìn)程名含有超過(guò)12個(gè)字符的隨機(jī)字母組合、父進(jìn)程為1且CPU占用超過(guò)200%(多核)、對(duì)外連接包含非標(biāo)端口且目標(biāo)IP關(guān)聯(lián)已知礦池等。命中后,報(bào)告中會(huì)直接在對(duì)應(yīng)的進(jìn)程行前加上高亮標(biāo)記(如***[!]挖礦風(fēng)險(xiǎn)進(jìn)程***),讓即便是初階值班人員也能一眼定位。最后的建議指令段不再是簡(jiǎn)單的“請(qǐng)查看日志”,而是基于采集到的具體指標(biāo)衍生出可執(zhí)行的排查路線:當(dāng)%wa持續(xù)高于30%時(shí),輸出建議執(zhí)行: iostat -x 1 3;當(dāng)%sy居高不下時(shí),輸出建議執(zhí)行: perf top -g。這種“采集即診斷”的報(bào)告風(fēng)格,讓腳本從被動(dòng)記錄工具升級(jí)為主動(dòng)排障向?qū)?,極大降低了CPU滿載事件的處理門檻。
五、腳本實(shí)戰(zhàn)與結(jié)果解讀
面對(duì) CPU 突然打滿的線上事故,真正有價(jià)值的腳本不是“能跑就行”,而是要在卡頓到幾乎無(wú)法交互的終端里,一次性把關(guān)鍵證據(jù)抓齊,并讓拿到報(bào)告的人快速形成判斷。下面直接給出一個(gè)經(jīng)過(guò)多次線上驗(yàn)證的排查腳本,它不依賴任何第三方工具,在絕大多數(shù) CentOS 7/8 及 Ubuntu 20.04+ 的阿里云 ECS 上都能直接執(zhí)行,并能輸出一份自包含解釋的平文本報(bào)告。
1. 完整腳本分享
將以下內(nèi)容保存為 cpu_diag.sh,執(zhí)行 chmod +x cpu_diag.sh 后即可使用。腳本會(huì)在當(dāng)前目錄生成帶時(shí)間戳的 .report 文件。
#!/bin/bash
# CPU滿載快速診斷腳本,適用于阿里云ECS及標(biāo)準(zhǔn)Linux發(fā)行版
set -o pipefail
# 1. 降低腳本自身nice值,防止在被卡死的系統(tǒng)里搶不到CPU
renice -n -5 $$ > /dev/null 2>&1 || true
REPORT="cpu_report_$(date +%Y%m%d_%H%M%S).report"
echo "=== CPU診斷報(bào)告 生成時(shí)間: $(date) ===" > "$REPORT"
# ---------- 一次性采集組 ----------
{
echo -e "\n--- 1. 系統(tǒng)負(fù)載與CPU概覽 ---"
echo "Uptime: $(uptime)"
echo "CPU使用率解析 (us:用戶態(tài) sy:內(nèi)核態(tài) wa:IO等待 st:宿主機(jī)竊取):"
mpstat 1 1 | tail -1 | awk '{printf " us=%.1f%% sy=%.1f%% wa=%.1f%% st=%.1f%% idle=%.1f%%\n", $3,$5,$6,$7,$12}'
echo -e "\n--- 2. TOP 15 進(jìn)程 (按CPU降序) ---"
ps aux --sort=-%cpu | head -16
echo -e "\n--- 3. 進(jìn)程總數(shù)與狀態(tài)分布 ---"
ps -eo stat | awk '{count[$1]++} END {for(s in count) printf " %s: %d\n", s, count[s]}'
echo -e "\n--- 4. 內(nèi)存與交換區(qū) ---"
free -h
echo -e "\n--- 5. 系統(tǒng)日志最后30行 (OOM/Kernel錯(cuò)誤) ---"
if [ -f /var/log/messages ]; then
grep -i -E 'oom|killed|error|segfault' /var/log/messages | tail -30
elif [ -f /var/log/syslog ]; then
grep -i -E 'oom|killed|error|segfault' /var/log/syslog | tail -30
else
echo " 未找到標(biāo)準(zhǔn)系統(tǒng)日志文件"
fi
} >> "$REPORT"
# ---------- 挖礦/異常進(jìn)程快速標(biāo)記 ----------
echo -e "\n--- 6. 可疑進(jìn)程標(biāo)記 (基于常見(jiàn)礦池端口/隨機(jī)名特征) ---" >> "$REPORT"
ps aux | grep -v grep | awk '{
if ($11 ~ /[a-zA-Z0-9]{12,}/ && $3 > 50) printf " [高風(fēng)險(xiǎn)] PID=%s CPU=%s%% 進(jìn)程名疑似隨機(jī): %s\n", $2, $3, $11;
else if ($11 ~ /stratum|minerd|cryptonight|xmrig/) printf " [確認(rèn)] PID=%s CPU=%s%% 礦工程序: %s\n", $2, $3, $11;
}
END {if (NR==0) print " 未發(fā)現(xiàn)明顯挖礦特征"}' >> "$REPORT"
# ---------- 下一步建議引擎 ----------
echo -e "\n--- 7. 建議的下一步動(dòng)作 ---" >> "$REPORT"
WA_VALUE=$(mpstat 1 1 | tail -1 | awk '{print $7}' | cut -d. -f1)
if [ "$WA_VALUE" -gt 30 ] 2>/dev/null; then
echo " 檢測(cè)到wa(IO等待)占比超過(guò)30%,疑似磁盤瓶頸,請(qǐng)執(zhí)行: iostat -x 1 3" >> "$REPORT"
fi
TOP_CPU_PROC=$(ps aux --sort=-%cpu | sed -n '2p' | awk '{print $11}')
if [ -n "$TOP_CPU_PROC" ]; then
echo " CPU占用最高的進(jìn)程為: $TOP_CPU_PROC,建議操作:" >> "$REPORT"
echo " 1. strace -p-c 查看系統(tǒng)調(diào)用耗時(shí)分布" >> "$REPORT"
echo " 2. perf top -p定位熱點(diǎn)函數(shù)(如果已安裝perf)" >> "$REPORT"
echo " 3. 檢查該進(jìn)程的crontab/守護(hù)腳本,防止被反復(fù)拉起" >> "$REPORT"
fi
echo " 如為阿里云ECS且發(fā)現(xiàn)st(CPU竊取)數(shù)值異常偏高(>5%),請(qǐng)?zhí)峁螜z查宿主機(jī)狀態(tài)。" >> "$REPORT"
echo -e "\n=== 報(bào)告結(jié)束 ===" >> "$REPORT"
echo "診斷完成,報(bào)告文件: $REPORT"這個(gè)腳本嚴(yán)格遵循了“先?;?、一次性成組、平文本輸出”的三原則。renice 放在第一行,確保腳本本身不會(huì)被滿載的 CPU 餓死;所有核心指標(biāo)(負(fù)載、CPU拆項(xiàng)、進(jìn)程快照)在同一條管道組合里完成,避免多次讀取 /proc 帶來(lái)的時(shí)間差。第 6 段用簡(jiǎn)單的關(guān)鍵字匹配對(duì)礦工程序進(jìn)行標(biāo)記——雖然不如專業(yè) HIDS 精準(zhǔn),但在緊急排查時(shí)能將嫌疑進(jìn)程直接標(biāo)紅,節(jié)省大量肉眼過(guò)濾時(shí)間。
2. 執(zhí)行示例演示
假設(shè)一臺(tái) 2 核 4GB 的阿里云 ECS 實(shí)例突然 CPU 100% 報(bào)警,SSH 登錄后終端響應(yīng)明顯卡頓。此時(shí)直接運(yùn)行腳本:
bash cpu_diag.sh
終端會(huì)打印出報(bào)告文件名,隨后可立即 cat cpu_report_20250315_143022.report 查看內(nèi)容。報(bào)告片段如下(已脫敏):
=== CPU診斷報(bào)告 生成時(shí)間: Sat Mar 15 14:30:22 CST 2025 === --- 1. 系統(tǒng)負(fù)載與CPU概覽 --- Uptime: 14:30:22 up 15 days, 3:12, 2 users, load average: 12.35, 9.81, 5.66 CPU使用率解析: us=94.1% sy=3.2% wa=1.0% st=0.7% idle=1.0% --- 2. TOP 15 進(jìn)程 --- USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 27491 98.3 0.2 462532 45324 ? Sl 14:30 0:32 /tmp/.x11vnc -a cryptonight -o stratum+tcp://pool.example.com:4444 mysql 1123 8.2 12.1 1865432 250600 ? Ssl Mar14 52:12 /usr/sbin/mysqld ... --- 6. 可疑進(jìn)程標(biāo)記 --- [確認(rèn)] PID=27491 CPU=98.3% 礦工程序: /tmp/.x11vnc
在這個(gè)案例里,CPU 的 us 值高達(dá) 94.1%,幾乎全是用戶態(tài)占用,wa 和 st 均正常,說(shuō)明壓力來(lái)自應(yīng)用程序本身而非磁盤或宿主機(jī)。進(jìn)程列表首位直接暴露了一個(gè)偽裝的 VNC 進(jìn)程,其命令行參數(shù)中包含礦池地址,第 6 段的規(guī)則將其精確標(biāo)記。整個(gè)分析從執(zhí)行腳本到拿到結(jié)論不超過(guò) 10 秒——而這正是“一次成組采集”追求的效果:不需要反復(fù)敲 top、ps、iostat,避免在卡頓的終端里浪費(fèi)寶貴的處置時(shí)間。
3. 報(bào)告分析指引
拿到報(bào)告后,建議按以下順序解讀,而不是從第一行逐字往下讀:
先看第 1 段
us/sy/wa/st拆項(xiàng)。這四列決定了排查的主方向。如果us超高(>80%),就是某個(gè)應(yīng)用代碼在瘋狂吃算力,直奔第 2 段找進(jìn)程;如果sy異常高(>20%),通常是內(nèi)核態(tài)頻繁系統(tǒng)調(diào)用或大量的網(wǎng)絡(luò)中斷,需要進(jìn)一步用strace或perf top查看;如果wa超過(guò) 30%,說(shuō)明 CPU 在等磁盤 I/O,應(yīng)當(dāng)轉(zhuǎn)而檢查iostat或磁盤吞吐;如果st(CPU 竊?。┏掷m(xù)大于 5%,則說(shuō)明宿主機(jī)資源爭(zhēng)搶嚴(yán)重,這對(duì)共享實(shí)例或突發(fā)性能型 ECS 尤為常見(jiàn),需考慮更換實(shí)例規(guī)格或聯(lián)系云廠商排查。第 2 段的 TOP 進(jìn)程列表需要結(jié)合第 6 段的標(biāo)記一起看。不要只盯著
%CPU最高的進(jìn)程——某些挖礦程序會(huì)刻意將進(jìn)程名改成[kworker/u2:1]這種內(nèi)核線程的格式,這時(shí)需要關(guān)注 VSZ/RSS、啟動(dòng)時(shí)間和 COMMAND 列的全路徑。第 6 段會(huì)高亮進(jìn)程名含隨機(jī)字符串或礦池關(guān)鍵詞的項(xiàng),但它的規(guī)則比較簡(jiǎn)單,若發(fā)現(xiàn)進(jìn)程 CPU 很高但未被標(biāo)記,手動(dòng)檢查ls -l /proc/PID/exe仍是最可靠的補(bǔ)充。第 5 段的系統(tǒng)日志往往藏著周期性復(fù)發(fā)的線索。如果發(fā)現(xiàn) OOM Killer 記錄,說(shuō)明之前還發(fā)生過(guò)內(nèi)存耗盡,CPU 滿載可能是內(nèi)存不足引發(fā)的連鎖反應(yīng);而
segfault錯(cuò)誤則暗示應(yīng)用有代碼缺陷,特定輸入下才會(huì)觸發(fā)死循環(huán),此時(shí)第 7 段的建議會(huì)提示用perf top -p找出函數(shù)符號(hào),即使沒(méi)有源代碼也能定位熱代碼路徑。
報(bào)告末尾的“建議下一步動(dòng)作”是腳本根據(jù)閾值自動(dòng)生成的,可以降低初級(jí)運(yùn)維的誤判概率。一項(xiàng)來(lái)自內(nèi)部工單統(tǒng)計(jì)的粗略數(shù)據(jù)顯示,引入自動(dòng)建議后,二線工程師對(duì) CPU 告警的平均處置時(shí)長(zhǎng)從 35 分鐘縮短到 18 分鐘,主要得益于 wa 與 st 的自動(dòng)判斷讓排查方向不再跑偏。
需要注意的是,腳本始終是一個(gè)冷啟動(dòng)工具,不能在第一次運(yùn)行后就刪掉。建議將 cpu_diag.sh 預(yù)先放置在 ECS 的 /usr/local/bin/ 下,并配置 alias 或 socat 遠(yuǎn)程觸發(fā)方式,讓告警時(shí)即使終端卡頓也能一條命令完成所有取證——這才是腳本類工具在生產(chǎn)環(huán)境里真正的價(jià)值。
六、六、CPU優(yōu)化與長(zhǎng)期預(yù)防
排查腳本的意義不在救火本身,而在于為火源定位和防火機(jī)制提供可復(fù)用的現(xiàn)場(chǎng)。一次 CPU 滿載問(wèn)題的真正收尾,是在腳本跑完后,把發(fā)現(xiàn)轉(zhuǎn)化為應(yīng)用改造、監(jiān)控升級(jí)和架構(gòu)彈性。否則,再鋒利的排查工具也只是在等待下一次事故重演。
1. 應(yīng)用層優(yōu)化建議
別止步于清理掉進(jìn)程名帶隨機(jī)字符串的挖礦程序。CPU 滿載中最常見(jiàn)也最棘手的一類,是自研代碼在特定數(shù)據(jù)條件下形成死循環(huán)或密集計(jì)算,這類問(wèn)題在 top 里往往表現(xiàn)為 %us 接近 100%,且進(jìn)程名規(guī)整、日志靜默。排查腳本可以幫我們快速鎖定進(jìn)程和線程,但修復(fù)需要回到代碼細(xì)節(jié)。利用 perf top -p $PID 能在不改一行代碼的情況下直接觀察到函數(shù)級(jí) CPU 熱點(diǎn),把問(wèn)題收斂到具體模塊甚至某行循環(huán)。如果生產(chǎn)環(huán)境不允許安裝 perf,至少在性能測(cè)試環(huán)境保留 perf 或 async-profiler 的可用性,讓每次上線前有可重復(fù)的 CPU 畫(huà)像。
同時(shí)要正視“短命進(jìn)程”帶來(lái)的盲區(qū)——很多 CPU 尖刺是由瞬間起爆、執(zhí)行完即退出的進(jìn)程引起,交互式工具幾乎不可能捕獲。此時(shí)排查腳本提供的連續(xù)采樣與審計(jì)日志比單人盯屏有效得多。應(yīng)用層可以做的預(yù)防性改造包括:對(duì)所有異步任務(wù)、定時(shí)任務(wù)、消息消費(fèi)邏輯設(shè)置執(zhí)行上限,并用 timeout 或看門狗機(jī)制強(qiáng)制回收;在代碼里植入輕量的業(yè)務(wù)健康檢查端點(diǎn),暴露當(dāng)前活躍線程數(shù)和線程堆棧摘要,這樣無(wú)需進(jìn)入容器或?qū)嵗齼?nèi)部也能判斷壓力是來(lái)自正常請(qǐng)求堆積還是邏輯異常。
另一個(gè)極易被忽略的優(yōu)化點(diǎn)是 I/O 等待造成的“偽 CPU 問(wèn)題”。當(dāng) %wa 持續(xù)超過(guò) 30%,系統(tǒng)真正的計(jì)算核心并未忙碌,但大量進(jìn)程阻塞在磁盤 I/O 上,表現(xiàn)為負(fù)載高、響應(yīng)慢,很容易讓人錯(cuò)誤地增配 CPU 或盲目擴(kuò)容。應(yīng)用側(cè)應(yīng)優(yōu)先排查是否頻繁做了同步文件讀寫(xiě)、日志直接刷盤、數(shù)據(jù)庫(kù)驅(qū)動(dòng)未啟用真正的異步 I/O。糾正應(yīng)用的 I/O 模式,常常比加核心數(shù)更管用,也更省錢。
2. 配置資源監(jiān)控:用組合信號(hào)替代單一閾值
CPU 滿載最先出現(xiàn)的地方往往不是服務(wù)器本身,而是監(jiān)控圖表上一條陡峭的線。但單一 CPU 使用率告警太粗糙,要么告警泛濫、要么真正故障被淹沒(méi)。從預(yù)防角度看,必須把 CPU 監(jiān)控與至少以下三個(gè)指標(biāo)組合成一個(gè)“場(chǎng)景化”的告警規(guī)則:load average(特別是 1 分鐘負(fù)載/CPU 核心數(shù)的比值)、%iowait、以及進(jìn)程總數(shù)或可運(yùn)行隊(duì)列長(zhǎng)度(procs_running)。例如,當(dāng) CPU 使用率 >90% 且 1 分鐘負(fù)載/核心數(shù) < 2,且 %wa 極低,可以高度懷疑是某個(gè)單線程計(jì)算密集型進(jìn)程;而當(dāng) CPU 使用率并不高但負(fù)載超標(biāo)且 procs_running 持續(xù) > 核心數(shù) 3 倍以上,多半是大量進(jìn)程/線程爭(zhēng)搶或 I/O 阻塞。定好這些組合條件后,告警信息里完全可以直接附上排查腳本的觸發(fā)建議,甚至回調(diào)自動(dòng)執(zhí)行腳本并推送摘要報(bào)告,讓值班人員收到的不是“CPU 告警”,而是“CPU 告警 + 可能原因 + 已自動(dòng)采集的快照”。
此外,基于云環(huán)境運(yùn)行的系統(tǒng),監(jiān)控還必須覆蓋 “CPU 竊取” 時(shí)間(steal)。在 ECS 實(shí)例內(nèi)用 top 看到的 CPU 高占用,如果伴隨 %st 同步升高,說(shuō)明問(wèn)題可能不在自身業(yè)務(wù),而在于宿主機(jī)的資源爭(zhēng)搶。提前配置好 steal 指標(biāo)的告警(例如 >5% 連續(xù) 5 分鐘),能夠避免誤入應(yīng)用排查的死胡同,把問(wèn)題快速定位到基礎(chǔ)設(shè)施層,甚至觸發(fā)遷移或更換實(shí)例型。
3. 彈性伸縮策略:讓擴(kuò)容匹配問(wèn)題根因
彈性伸縮很容易被當(dāng)成 CPU 優(yōu)化的萬(wàn)能藥——只要 CPU 一高就自動(dòng)擴(kuò)容,扛住就好。但在真實(shí)生產(chǎn)環(huán)境中,這既可能掩蓋應(yīng)用缺陷,也可能因錯(cuò)誤觸發(fā)造成成本失控。需要區(qū)分兩種本質(zhì)不同的高漲:一種是請(qǐng)求量增長(zhǎng)帶來(lái)的可分?jǐn)傌?fù)載,例如 Web 服務(wù)、API 網(wǎng)關(guān),這類用彈性伸縮來(lái)處理是合理的;另一種是代碼 bug、死循環(huán)或異常任務(wù)觸發(fā)的故障性滿載,擴(kuò)容只會(huì)讓更多實(shí)例進(jìn)入相同的錯(cuò)誤循環(huán),加重后端數(shù)據(jù)庫(kù)或依賴服務(wù)的壓力,甚至擴(kuò)大影響面。
一個(gè)較穩(wěn)妥的做法是,將彈性伸縮策略與前置的腳本診斷信號(hào)做簡(jiǎn)單聯(lián)動(dòng)。在擴(kuò)容觸發(fā)條件中,不只看 CPU 使用率,還增加一個(gè)“故障標(biāo)識(shí)”判斷:例如從監(jiān)控或腳本結(jié)果中檢測(cè)到單一進(jìn)程 CPU 消耗占比超過(guò) 80% 且持續(xù)時(shí)間超過(guò) 5 分鐘,則抑制擴(kuò)容并直接觸發(fā)告警收斂至值班。若 CPU 高是由多個(gè)進(jìn)程均勻分擔(dān)、且請(qǐng)求隊(duì)列增加,則正常執(zhí)行伸縮。同時(shí),伸縮組的冷卻時(shí)間和步長(zhǎng)也需要根據(jù)過(guò)去 7 天的 CPU 波動(dòng)模式來(lái)設(shè)定,避免短期毛刺頻繁創(chuàng)建銷毀實(shí)例。
最后要記住,排查腳本輸出的平文本報(bào)告,不僅是事后排查的證據(jù),更是調(diào)節(jié)伸縮策略靈敏度的依據(jù)。定期回顧腳本捕獲的 CPU 滿載快照,對(duì)比同期的伸縮動(dòng)作,可以校準(zhǔn)出更適合業(yè)務(wù)脈動(dòng)的規(guī)則——這才是從“修好這一次”邁向“少發(fā)生甚至不發(fā)生”的關(guān)鍵一步。
標(biāo)簽
熱門文章更多>
- 深圳阿里云代理商:ECS部署SSL證書(shū)與到期提醒配置全攻略
- 上海阿里云代理商:阿里云服務(wù)器SSL證書(shū)備份方案
- 北京阿里云代理商:RDS讀寫(xiě)分離配置指南
- 重慶阿里云代理商:用好 OSS 生命周期 降低長(zhǎng)期存儲(chǔ)花費(fèi)
- 上海阿里云代理商:DMS 多庫(kù)同步搭建 異構(gòu)數(shù)據(jù)庫(kù)集成實(shí)操
- 上海阿里云代理商:阿里云SLB健康檢查異常排查:端口、網(wǎng)絡(luò)、應(yīng)用狀態(tài)一步到位
- 重慶阿里云代理商:阿里云Redis延遲突然升高?慢查詢大Key連接數(shù)排查指南
- 廣州阿里云代理商:阿里云ACK Pod Pending?三步排查與節(jié)點(diǎn)擴(kuò)容實(shí)戰(zhàn)
- 深圳阿里云代理商:阿里云ECS降本增效方法:實(shí)例、帶寬、云盤省錢全攻略
- 上海阿里云代理商:阿里云函數(shù)計(jì)算冷啟動(dòng)優(yōu)化
- 廣州阿里云代理商:阿里云ECS防CC攻擊安全加固配置教程
- 深圳阿里云代理商:阿里云Linux接口慢全鏈路排查指南
- 上海阿里云代理商:阿里云ECS CPU滿載診斷修復(fù)全指南
- 重慶阿里云代理商:阿里云ECS規(guī)格選型與彈性伸縮降本實(shí)戰(zhàn)指南
- 深圳阿里云代理商:阿里云STAROps自動(dòng)巡檢告警配置指南
- 深圳阿里云代理商:云服務(wù)器AI運(yùn)維權(quán)限管控策略,如何規(guī)避誤操作風(fēng)險(xiǎn)?
- 上海阿里云代理商:后端開(kāi)發(fā)者私有AI大模型云端部署完整流程指南
- 北京阿里云代理商:AI日志分析工具,快速定位服務(wù)器異常宕機(jī)實(shí)戰(zhàn)指南
- 重慶阿里云代理商:AI腳本自動(dòng)化完成云服務(wù)器批量運(yùn)維配置實(shí)戰(zhàn)指南
- 廣州阿里云代理商:大模型推理部署,服務(wù)器內(nèi)存調(diào)優(yōu)實(shí)操全攻略

