故障排查實(shí)戰(zhàn):從CPU到網(wǎng)絡(luò)的全鏈路方法論)
1. 先搞清楚故障排查到底在排什么干了這么多年運(yùn)維我見過太多同事在系統(tǒng)出問題時(shí)直接抄起鍵盤就開始一通操作看到CPU高就kill進(jìn)程看到磁盤滿就rm文件折騰半天發(fā)現(xiàn)故障還在甚至把業(yè)務(wù)搞得更糟。問題出在哪出在缺少一套系統(tǒng)性的排查思路。Linux系統(tǒng)故障排查核心不在于你會(huì)用多少命令而在于你如何把“現(xiàn)象”還原成“鏈路”。一臺(tái)服務(wù)器從硬件到內(nèi)核再到文件系統(tǒng)、進(jìn)程調(diào)度、網(wǎng)絡(luò)協(xié)議棧每一層都可能出問題而現(xiàn)象往往是最表面的一層。比如用戶反饋“網(wǎng)站打開很慢”可能的根因有幾十種CPU爭(zhēng)搶、磁盤I/O、網(wǎng)絡(luò)丟包、DNS解析慢、后端應(yīng)用線程阻塞、甚至是物理機(jī)鄰居搶占帶寬。如果沒有一套成體系的排查思路你就是在瞎猜。我傾向把排查思路拆成六個(gè)固定動(dòng)作確認(rèn)現(xiàn)象、評(píng)估影響、收集信息、形成假設(shè)、驗(yàn)證根因、修復(fù)復(fù)盤。這六步看起來簡(jiǎn)單但每一步都有講究。確認(rèn)現(xiàn)象不是聽用戶說“卡了”就完事要量化到底什么指標(biāo)異常評(píng)估影響決定了你的處理優(yōu)先級(jí)——是保業(yè)務(wù)優(yōu)先還是定位根因優(yōu)先收集信息要求你在動(dòng)手“修”之前先把現(xiàn)場(chǎng)保留下來否則進(jìn)程一重啟、緩存一清空證據(jù)就沒了。這套方法論適合誰剛?cè)胄械倪\(yùn)維新手、從開發(fā)轉(zhuǎn)到運(yùn)維的工程師、以及想要標(biāo)準(zhǔn)化團(tuán)隊(duì)故障處理流程的運(yùn)維負(fù)責(zé)人。后面我會(huì)用實(shí)際案例把每一步掰開來講包括我這些年踩過的坑和總結(jié)出的經(jīng)驗(yàn)。提示故障排查的第一原則是“先恢復(fù)后定位”。如果業(yè)務(wù)正在受損優(yōu)先通過重啟服務(wù)、切換流量、擴(kuò)容節(jié)點(diǎn)等手段止損再慢慢分析根因。別為了?,F(xiàn)場(chǎng)而讓故障持續(xù)發(fā)酵。2. CPU與系統(tǒng)負(fù)載最容易被誤判的一類問題2.1 先看懂負(fù)載和CPU使用率的關(guān)系很多人一看到load average超過CPU核數(shù)就慌了認(rèn)定是CPU不夠用。這是個(gè)非常大的誤區(qū)。我做個(gè)類比CPU使用率是“正在干活的比例”而負(fù)載是“排隊(duì)等干活的人數(shù)”。想象一家銀行柜臺(tái)一個(gè)柜臺(tái)一個(gè)CPU核同時(shí)只能服務(wù)一個(gè)客戶使用率是說這個(gè)柜員有多忙而負(fù)載則統(tǒng)計(jì)著正在辦業(yè)務(wù)加上排隊(duì)等著的所有人。如果一個(gè)柜員同時(shí)在處理大量的事務(wù)但每個(gè)事務(wù)都在等磁盤返回?cái)?shù)據(jù)也就是I/O等待他手上沒閑著可也沒在真正計(jì)算——這時(shí)候CPU使用率可能不高但負(fù)載飆升。所以排查的第一步永遠(yuǎn)是先區(qū)分負(fù)載升高到底是CPU計(jì)算密集、I/O等待還是進(jìn)程阻塞。實(shí)操上我會(huì)依次執(zhí)行三條命令uptime top vmstat 1 5uptime看過去1/5/15分鐘的負(fù)載趨勢(shì)判斷是突發(fā)還是持續(xù)。top看整體CPU占用分布重點(diǎn)看us用戶態(tài)、sy內(nèi)核態(tài)、waI/O等待和id空閑幾個(gè)值。vmstat 1 5每隔1秒采樣5次看r運(yùn)行隊(duì)列、b阻塞進(jìn)程數(shù)、wa、cs上下文切換這幾列。如果us高說明有進(jìn)程在瘋狂消耗CPU下一步用top -Hp pid找到具體線程或者用pidstat -t -p pid 1觀察哪個(gè)線程占CPU。如果wa高而us低那問題在磁盤I/O別在CPU層面浪費(fèi)時(shí)間直接跳到磁盤排查。如果r隊(duì)列長(zhǎng)期大于CPU核數(shù)的數(shù)倍但us/sy并不高多半是進(jìn)程在鎖等待或者內(nèi)核態(tài)出了問題。2.2 CPU排查的實(shí)戰(zhàn)路徑定位到CPU消耗大戶之后接下來要回答“它在干什么”。我常用的組合是perf top或者pidstat配合/proc/pid/status查看線程狀態(tài)。生產(chǎn)環(huán)境不一定有perf權(quán)限這時(shí)候strace -p pid可以看系統(tǒng)調(diào)用但注意strace在高頻進(jìn)程上有嚴(yán)重性能開銷使用前要評(píng)估影響。排查CPU問題時(shí)的三個(gè)容易翻車的點(diǎn)第一別忽略內(nèi)核態(tài)sy過高。如果sy持續(xù)超過30%多半是有系統(tǒng)調(diào)用風(fēng)暴比如頻繁的網(wǎng)絡(luò)收發(fā)、鎖競(jìng)爭(zhēng)、或硬件中斷處理過多。用top按CPU排序時(shí)如果排前面的都是ksoftirqd或kworker這類內(nèi)核線程就得檢查是不是網(wǎng)卡軟中斷在某個(gè)CPU上集中觸發(fā)。查看/proc/interrupts能確認(rèn)中斷分布是否均勻必要時(shí)調(diào)整網(wǎng)卡隊(duì)列的RPSReceive Packet Steering設(shè)置。第二當(dāng)心上下文切換過高。vmstat的cs列持續(xù)幾十萬說明系統(tǒng)在瘋狂切換進(jìn)程/線程CPU資源大量消耗在“換人”而不是“干活”上。常見誘因是線程池開得過大、鎖競(jìng)爭(zhēng)嚴(yán)重或者定時(shí)器頻繁觸發(fā)。我處理過一個(gè)案例應(yīng)用線程池配置了200個(gè)線程但活躍線程只有幾個(gè)大量線程在鎖上等待上下文切換高得嚇人把CPU全部浪費(fèi)完了。把線程池調(diào)小之后性能反而上去了。第三CPU綁核相關(guān)的隱蔽問題。多核環(huán)境下如果進(jìn)程頻繁在不同CPU核之間遷移緩存命中率下降也會(huì)造成CPU使用率偏高。NUMA架構(gòu)下跨節(jié)點(diǎn)訪問內(nèi)存更是性能殺手。排查看/proc/pid/numa_maps能發(fā)現(xiàn)內(nèi)存是否跨節(jié)點(diǎn)分布必要時(shí)調(diào)整進(jìn)程的NUMA策略。3. 內(nèi)存與SwapOOM不是終點(diǎn)只是起點(diǎn)3.1 內(nèi)存問題的排查順序內(nèi)存排查我先看三條命令的輸出free -h vmstat 1 5 dmesg -T | tail -50free -h看總量、已用、可用和Swap使用情況。有個(gè)細(xì)節(jié)必須說清楚Linux的free輸出里available才是真正可分配給新進(jìn)程的內(nèi)存量而不是free列。因?yàn)閎uff/cache在內(nèi)存壓力下能回收所以一臺(tái)內(nèi)存用了90%的機(jī)器如果available還有30%系統(tǒng)并不會(huì)觸發(fā)OOM。vmstat重點(diǎn)看siswap in和soswap out。如果這兩個(gè)值持續(xù)不為0說明物理內(nèi)存已經(jīng)不夠用系統(tǒng)在靠Swap硬撐。注從磁盤換進(jìn)換出內(nèi)存頁(yè)的性能損耗極大比直接物理內(nèi)存訪問慢好幾個(gè)數(shù)量級(jí)。我見過一臺(tái)數(shù)據(jù)庫(kù)服務(wù)器Swap使用了8GB查詢延遲翻了幾十倍就是這個(gè)原因。dmesg一定要看因?yàn)镺OM Killer殺進(jìn)程時(shí)會(huì)在內(nèi)核日志里留下完整記錄包括誰觸發(fā)了OOM、當(dāng)時(shí)內(nèi)存最緊張的進(jìn)程是誰、回收了多少內(nèi)存。日志里通常還有這樣一行Out of memory: Killed process 12345 (java) total-vm:...。這是定位“誰殺了我的進(jìn)程”的最直接證據(jù)。3.2 OOM的深層原因很多人以為OOM就是“內(nèi)存不夠了”其實(shí)背后還有幾種情況。一是內(nèi)存碎片化。物理內(nèi)存總量還有不少但連續(xù)的“大塊”內(nèi)存滿足不了應(yīng)用的需求而Linux默認(rèn)啟用的是虛存管理可能無法有效處理大塊連續(xù)分配。密集的內(nèi)核對(duì)于這類請(qǐng)求會(huì)失敗、觸發(fā)OOM。這種情況跟應(yīng)用申請(qǐng)大塊內(nèi)存比如JVM堆、搜索引擎倒排索引加載有關(guān)系。/proc/buddyinfo能看到內(nèi)存碎片狀況如果高階塊全為0基本可以判斷是碎片問題。二是內(nèi)存泄漏。應(yīng)用長(zhǎng)時(shí)間運(yùn)行內(nèi)存只漲不降最終觸發(fā)OOM。排內(nèi)存泄漏我通常用ps aux --sort-rss看RSS排序鎖定增長(zhǎng)異常的那個(gè)進(jìn)程然后配合監(jiān)控確認(rèn)它的內(nèi)存趨勢(shì)。我實(shí)習(xí)帶過一個(gè)案例一個(gè)后端服務(wù)每處理一次請(qǐng)求就多占用幾MB內(nèi)存跑三天就OOM最后定位是代碼里某個(gè)緩存在無限增長(zhǎng)沒有清理。這種問題排查的難度不在定位而在于需要長(zhǎng)時(shí)間觀察所以建議監(jiān)控告警里一定要有進(jìn)程內(nèi)存的趨勢(shì)曲線。三是Cgroup限制。容器環(huán)境里最常見宿主機(jī)還有大把內(nèi)存但容器內(nèi)的Cgroup內(nèi)存限制到了一樣觸發(fā)OOM。排查時(shí)不要只看宿主機(jī)free要檢查容器的Cgroup使用量cat /sys/fs/cgroup/memory/memory.usage_in_bytes cat /sys/fs/cgroup/memory/memory.limit_in_bytes3.3 Swap的正確配置姿勢(shì)關(guān)于Swap不同的場(chǎng)景有不同的處理方式。數(shù)據(jù)庫(kù)這類延遲敏感型的服務(wù)我通常建議降低swappiness——這個(gè)內(nèi)核參數(shù)控制內(nèi)核傾向于換出內(nèi)存頁(yè)的程度取值范圍0~100默認(rèn)通常是60。數(shù)字越大內(nèi)核越積極使用Swap。降低到10甚至個(gè)別場(chǎng)景直接關(guān)掉Swap能讓數(shù)據(jù)庫(kù)盡量留在物理內(nèi)存里。但對(duì)于一些批處理型任務(wù)或者內(nèi)存峰值明顯的應(yīng)用留部分Swap做兜底其實(shí)是保護(hù)機(jī)制防止運(yùn)營(yíng)高峰期直接OOM。這就需要在監(jiān)控上看到 Swap 使用明顯上升時(shí)就要發(fā)出預(yù)警因?yàn)橥ǔ?yīng)用程序會(huì)因內(nèi)存壓力劇烈波動(dòng)而性能急劇下降。在修改swappiness參數(shù)時(shí)用sysctl vm.swappiness10可以臨時(shí)生效要持久化需要寫到/etc/sysctl.conf里。修改之前先看當(dāng)前值改完之后用sysctl -p重載。有一點(diǎn)要記牢這個(gè)參數(shù)只影響內(nèi)核換出的“積極性”不會(huì)適配應(yīng)用都禁用Swap。在計(jì)劃禁用時(shí)你得保證應(yīng)用內(nèi)存峰值確確實(shí)實(shí)低于物理內(nèi)存容量否則就是在賭運(yùn)氣。4. 磁盤與文件系統(tǒng)空間滿只是最淺的一層4.1 兩個(gè)容易搞混的“滿”磁盤滿分兩種容量滿和inode滿。前者好理解后者是很多新手沒概念的東西。inode可以理解為文件系統(tǒng)的“戶口本”每個(gè)文件都要占一條記錄。如果小文件特別多可能容量還有剩但inode已經(jīng)用完表現(xiàn)為“No space left on device”。排查命令分別用df -h和df -i。如果df -h顯示還有幾十GB但寫入文件報(bào)錯(cuò)磁盤滿第一反應(yīng)就該查df -i。我在一個(gè)日志采集服務(wù)器上遇到過這個(gè)問題每天產(chǎn)生上千萬個(gè)小日志文件日志輪轉(zhuǎn)配置沒做好一個(gè)月之后inode耗盡。當(dāng)時(shí)df -h才用了60%但整個(gè)分區(qū)再也寫不進(jìn)任何數(shù)據(jù)。處理inode耗盡的思路找到哪個(gè)目錄文件數(shù)量最多通常用for d in /var/log /tmp /home /data; do echo $d; find $d -xdev -type f | wc -l; done先定位再清理。事后一定要把日志輪轉(zhuǎn)logrotate策略修正甚至把應(yīng)用改為按時(shí)間分批存儲(chǔ)大文件減少小文件數(shù)量。這里多說一句很多 NoSQL 存儲(chǔ)和消息隊(duì)列產(chǎn)生的“小文件堆積”問題根子都出在業(yè)務(wù)設(shè)計(jì)時(shí)沒考慮文件數(shù)量而不只是運(yùn)維配置的鍋。4.2 找出誰在偷偷占空間du -sh /*從根往下逐層定位是排查“容量滿”的最高效方式。小技巧定位到具體目錄后用du -sh /目錄/* | sort -hr | head -20列出最大的一批文件。注意du在大目錄上會(huì)很慢特別是包含大量小文件的情況優(yōu)先用--max-depth1限制層級(jí)。可能有朋友遇到過這種情況du -sh /加總算出來的總量遠(yuǎn)小于df -h顯示的已用空間。這不是統(tǒng)計(jì)誤差最常見的原因是“文件被刪除但進(jìn)程還持有句柄”。進(jìn)程打開了一個(gè)文件rm刪除了它但從內(nèi)核的角度這個(gè)文件仍然存在直到持有它的進(jìn)程把句柄關(guān)閉才會(huì)真正釋放磁盤塊。這就是為什么df顯示空間滿但du找不到大文件。排查命令lsof L1它會(huì)列出所有“被刪除但仍被進(jìn)程打開”的文件包括大小。看到結(jié)果后找到占空間最大的那個(gè)評(píng)估它屬于哪個(gè)進(jìn)程。如果進(jìn)程沒法重啟可以嘗試用/proc/pid/fd/下的軟鏈接配合truncate -s 0清空文件內(nèi)容這招在遇到磁盤阻塞的情況下特別管用——不是真正截?cái)嗟讓右褎h除文件的內(nèi)容而是讓磁盤塊能被系統(tǒng)回收。磁盤空間滿還有一個(gè)容易被忽視的點(diǎn)root用戶的保留空間。Ext4文件系統(tǒng)默認(rèn)給root預(yù)留5%的空間這是為了避免普通用戶把磁盤寫滿后系統(tǒng)無法正常運(yùn)作。對(duì)于大容量分區(qū)5%可能浪費(fèi)幾十GB對(duì)于家目錄這類分區(qū)可以調(diào)小但對(duì)根分區(qū)我建議保留。調(diào)預(yù)留比例用tune2fs -m 2 /dev/mapper/xx-root修改前先用dumpe2fs查看當(dāng)前保留塊設(shè)置。4.3 I/O瓶頸用iostat和iotop精準(zhǔn)定位當(dāng)vmstat的wa值高時(shí)I/O層面要立刻轉(zhuǎn)場(chǎng)排查。第一步iostat -x 1 5看%util、await、svctm這幾個(gè)參數(shù)。很多人有個(gè)誤解認(rèn)為%util達(dá)到100%就是磁盤“滿了”其實(shí)%util表示設(shè)備忙但不代表無法服務(wù)更多請(qǐng)求。如果%util只有30%但await已經(jīng)很高可能是有請(qǐng)求在隊(duì)列里排隊(duì)或者底層設(shè)備比如磁盤陣列控制器出現(xiàn)了性能瓶頸又或者請(qǐng)求大小和模式不合理導(dǎo)致單次I/O延遲高。定位到具體是哪些進(jìn)程在刷盤用iotop或pidstat -d 1。這類工具需要權(quán)限如果沒有可以先把block_dump監(jiān)測(cè)器打開或者用bcc工具庫(kù)里的biosnoop追蹤I/O事件。生產(chǎn)環(huán)境里我定位過幾次I/O相關(guān)的場(chǎng)景數(shù)據(jù)庫(kù)頻繁小寫刷新每次事務(wù)都要刷日志文件典型的同步寫模式在機(jī)械盤上延遲很大。優(yōu)化方向是緩存/批次寫入、獨(dú)立日志盤、或換SSD。日志收集工具并發(fā)讀大量文件緩存基本無法命中隨機(jī)讀I/O壓力巨大。某應(yīng)用 fork 了大量子進(jìn)程后同時(shí)啟動(dòng)每個(gè)子進(jìn)程都要讀取共用的配置文件瞬間讀放大把I/O打滿。解決方案是把共享配置做成動(dòng)態(tài)加載避免啟動(dòng)時(shí)全量讀取。5. 網(wǎng)絡(luò)層故障連接問題的背后5.1 先分清是“不通”還是“拒絕”用戶說“連不上”背后可能是完全不同的故障。我要求團(tuán)隊(duì)先跑三條命令ping 目標(biāo)IP ss -lntp | grep 端口 telnet 目標(biāo)IP 端口 或 nc -vz -w 3 目標(biāo)IP 端口ping不通說明網(wǎng)絡(luò)鏈路或?qū)Χ酥鳈C(jī)有問題也可能是防火墻攔截ICMP。但注意ping通不代表端口可用ping不通也不代表端口不可用——有些服務(wù)器出于安全考慮禁ICMP但業(yè)務(wù)端口正常。所以連通性測(cè)試只是第一步端口測(cè)試才是關(guān)鍵。telnet/nc連不上時(shí)要區(qū)分兩種情況連接超時(shí)timeout和拒絕連接refused。timeout通常意味著中間鏈路不通或者對(duì)端防火墻把包丟了用的是DROP策略refused說明包已經(jīng)送到對(duì)端但對(duì)端沒有進(jìn)程監(jiān)聽該端口或者本機(jī)防火墻用的是REJECT策略。一個(gè)常見場(chǎng)景服務(wù)剛啟動(dòng)還沒完全監(jiān)聽端口或者進(jìn)程已經(jīng)掛掉但健康檢查還在探測(cè)這時(shí)候?qū)Χ藭?huì)回RST表現(xiàn)就是refused。5.2 細(xì)看監(jiān)聽與連接狀態(tài)ss是現(xiàn)在的主流工具我之前的老同事還在用netstat但生產(chǎn)上ss信息更全、速度更快??幢O(jiān)聽和連接狀態(tài)ss -lntp ss -s ss -ant | awk {print $1} | sort | uniq -css -s做總覽——當(dāng)前建了哪些連接、在等待的、在排隊(duì)的一目了然。重點(diǎn)看兩個(gè)異常狀態(tài)SYN-SENT大量堆積說明本機(jī)發(fā)出去的連接請(qǐng)求沒得到應(yīng)答多半是對(duì)端或中間網(wǎng)絡(luò)出問題TIME-WAIT堆積是正?,F(xiàn)象但如果量大到讓端口耗盡就需要調(diào)整內(nèi)核參數(shù)。針對(duì)高并發(fā)的短連接應(yīng)用場(chǎng)景比如網(wǎng)關(guān)TIME-WAIT過多是個(gè)經(jīng)典問題內(nèi)核參數(shù)調(diào)整要小心權(quán)衡net.ipv4.tcp_tw_reuse搭配tcp_timestamps可以在出站連接上復(fù)用TIME-WAIT連接但亂改可能引入連接復(fù)用的異常風(fēng)險(xiǎn)。如果ss -lntp顯示監(jiān)聽隊(duì)列出現(xiàn)溢出Recv-Q堆積這是半連接隊(duì)列或全連接隊(duì)列滿的表現(xiàn)。全連接隊(duì)列溢出可以通過ss -lnt看到 Recv-Q 超過Send-Q來判斷原因通常是應(yīng)用處理連接速度跟不上新連接到達(dá)速度。排查時(shí)先看這幾點(diǎn)應(yīng)用線程數(shù)是否足夠、listener backlog設(shè)置是否偏小很多部署默認(rèn)值往往是128對(duì)于高并發(fā)場(chǎng)景明顯不夠調(diào)整為2048或以后、系統(tǒng)net.core.somaxconn是否同步調(diào)大。5.3 抓包定位問題鏈路網(wǎng)絡(luò)排查里最有力的終極大招就是抓包。tcpdump是必會(huì)工具簡(jiǎn)單場(chǎng)景看三次握手階段是否有遺漏比如SYN發(fā)出去了對(duì)方不回SYN-ACK問題多半在網(wǎng)絡(luò)路徑或?qū)Χ朔阑饓θ绻荢YN-ACK回過來了但ACK不回問題可能出在本機(jī)的協(xié)議?;騭ocket隊(duì)列。tcpdump -i eth0 host 目標(biāo)IP and port 8080 -w /tmp/capture.pcap抓包文件用wireshark分析或者直接tcpdump -r看。抓包一般建議在問題的兩端分別抓對(duì)照時(shí)間戳。舉個(gè)例子一次偶發(fā)訪問變慢單機(jī)抓包看到TCP重傳頻繁換網(wǎng)絡(luò)設(shè)備側(cè)一起抓結(jié)果發(fā)現(xiàn)是中間交換機(jī)的鏈路聚合hash策略不均衡導(dǎo)致的單鏈路擁塞。這個(gè)案例說明網(wǎng)絡(luò)層問題不一定能通過看本機(jī)指標(biāo)定位出來跨節(jié)點(diǎn)抓包對(duì)照才是正解。DNS問題也是網(wǎng)絡(luò)排查中極高頻的痛點(diǎn)。表現(xiàn)為“某些域名時(shí)通時(shí)不通”、“首次訪問特別慢”。用dig trace可以看到解析鏈路的耗時(shí)在哪個(gè)環(huán)節(jié)/etc/resolv.conf檢查配置的DNS服務(wù)器是否合理。我排過的一個(gè)案例應(yīng)用配置的DNS服務(wù)器指向一個(gè)早已下線的內(nèi)網(wǎng)DNS解析超時(shí)后才走備用每次請(qǐng)求白白消耗5秒。把DNS配置改對(duì)后延遲驟降。6. 常用排查工具組合與監(jiān)控基線6.1 建立一套工具組合和排查字段用哪個(gè)工具不關(guān)鍵關(guān)鍵是你對(duì)輸出字段的含義是否了然于心。我給自己定了一套排查順序也推薦給團(tuán)隊(duì)第一梯隊(duì)發(fā)現(xiàn)異常uptime、top、free、df、ss、dmesg第二梯隊(duì)關(guān)聯(lián)分析vmstat、iostat、sar、pidstat第三梯隊(duì)深挖定位strace、perf、tcpdump、lsof、gdbsar在我心里的地位比較特殊。新接手一臺(tái)機(jī)器我會(huì)先看它的歷史數(shù)據(jù)來確定“正常態(tài)”長(zhǎng)什么樣。它能返回歷史某段時(shí)間的CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)數(shù)據(jù)這是其他工具替代不了的——你沒有歷史數(shù)據(jù)怎么判斷現(xiàn)在的“異常”是真的異常呢有些年頭久遠(yuǎn)的監(jiān)控工具已經(jīng)部署好了但還是建議把sysstat裝好定時(shí)采樣默認(rèn)配置覆蓋范圍很廣別隨意關(guān)閉。排查時(shí)我習(xí)慣把“時(shí)間線”拉出來把系統(tǒng)報(bào)障時(shí)間、監(jiān)控告警時(shí)間、應(yīng)用日志報(bào)錯(cuò)時(shí)間、內(nèi)核日志異常時(shí)間對(duì)齊。故障排查和刑偵差不多時(shí)間線對(duì)不上很大概率走彎路。舉例監(jiān)控顯示磁盤I/O 3點(diǎn)開始飆高但應(yīng)用發(fā)版是3點(diǎn)半業(yè)務(wù)量也是3點(diǎn)半才開始漲的說明I/O問題是先發(fā)生的業(yè)務(wù)變慢可能是I/O導(dǎo)致的結(jié)果而不是原因。這么一捋排查方向馬上糾正過來。6.2 監(jiān)控基線與告警閾值的設(shè)計(jì)思路運(yùn)維工作一半在故障發(fā)生后的排查另外一半在故障發(fā)生之前的預(yù)警。我給客戶做方案時(shí)經(jīng)常強(qiáng)調(diào)“監(jiān)控基線”的概念。每臺(tái)服務(wù)器都有自己的“性格”有的業(yè)務(wù)本身就在夜間跑批CPU占滿很正常有的服務(wù)內(nèi)存就是會(huì)緩慢增長(zhǎng)兩三個(gè)月重啟一次屬于正常節(jié)奏。盲目套用通用告警閾值只會(huì)讓告警失去可信度久而久之成了狼來了。建立基線的做法至少收集各個(gè)核心指標(biāo)的最近30天數(shù)據(jù)用百分比線進(jìn)行建模。例如CPU使用率的P95第95百分位是60%那告警閾值設(shè)在80%比較合理——既不會(huì)頻繁誤報(bào)又能在真實(shí)異常出現(xiàn)時(shí)及時(shí)提醒。內(nèi)存監(jiān)控要重點(diǎn)關(guān)注available而非used磁盤監(jiān)控要同時(shí)看容量和inode網(wǎng)絡(luò)監(jiān)控除了流量還要看重傳率、丟包率。這些從故障處理中學(xué)到的經(jīng)驗(yàn)應(yīng)該沉淀成監(jiān)控配置的標(biāo)準(zhǔn)。注意監(jiān)控不是越多越好過多的曲線只是在告警時(shí)增加噪音。先覆蓋能反映問題的關(guān)鍵指標(biāo)逐步迭代補(bǔ)齊。7. 典型故障復(fù)盤與避坑經(jīng)驗(yàn)7.1 一個(gè)“空轉(zhuǎn)”CPU問題的完整復(fù)盤有一次同事反饋一臺(tái)應(yīng)用服務(wù)器響應(yīng)變慢我按順序排查top看到us30%sy40%負(fù)載卻不低。當(dāng)時(shí)第一反應(yīng)是系統(tǒng)調(diào)用異常。用pidstat找出占用高的進(jìn)程再strace -c -p pid統(tǒng)計(jì)系統(tǒng)調(diào)用分布發(fā)現(xiàn)futex用戶態(tài)鎖調(diào)用次數(shù)高得離譜。繼續(xù)看線程棧發(fā)現(xiàn)大量線程堵在pthread_cond_wait同時(shí)業(yè)務(wù)線程和回收線程在共享資源上有極高的鎖競(jìng)爭(zhēng)。確認(rèn)根因后修復(fù)方案是把原來的單鎖拆分成分段鎖同時(shí)調(diào)整了線程池大小。這個(gè)案例的關(guān)鍵點(diǎn)是如果只停在“CPU 使用率高”這個(gè)層面就盲目重啟下次故障很快又會(huì)出現(xiàn)。鎖競(jìng)爭(zhēng)問題靠重啟是掩蓋不掉的。這個(gè)案例也說明有時(shí)候CPU的sy高并不只與內(nèi)核代碼路徑有關(guān)應(yīng)用層不合理的鎖使用同樣會(huì)引發(fā)內(nèi)核態(tài)的高開銷因?yàn)閒utex的實(shí)現(xiàn)正是大量動(dòng)用內(nèi)核的系統(tǒng)調(diào)用。7.2 一次磁盤“離奇”占滿的排查記錄一個(gè)分區(qū)df顯示用量100%du -sh卻只統(tǒng)計(jì)出幾GB這一看就猜到有文件被刪了但進(jìn)程沒釋放。執(zhí)行l(wèi)sof L1后看到一堆被日志進(jìn)程打開、但文件名已經(jīng)帶(deleted)標(biāo)記的大文件總大小幾十GB。這些句柄屬于一個(gè)常駐進(jìn)程理論上重啟進(jìn)程能解決但生產(chǎn)環(huán)境不允許隨意重啟。當(dāng)時(shí)我用了一種更輕量的方式找到lsof顯示對(duì)應(yīng)進(jìn)程的/proc/pid/fd/數(shù)字軟鏈接對(duì)其中一個(gè) fd 執(zhí)行: /proc/pid/fd/數(shù)字或truncate -s 0把它的文件內(nèi)容清零讓磁盤塊被回收。這個(gè)辦法可以應(yīng)急但要注意直接對(duì)服務(wù)進(jìn)行清空文件內(nèi)容操作雖然能快速釋放磁盤但后續(xù)日志寫入可能因?yàn)槲募屏慨惓6鰡栴}穩(wěn)妥方案還是盡快通知應(yīng)用方重啟、恢復(fù)日志輪轉(zhuǎn)配置。這個(gè)坑告訴我的經(jīng)驗(yàn)是日志文件被刪除的現(xiàn)象遠(yuǎn)比想象中頻繁add 一條帶大小排序的lsof L1巡檢腳本到運(yùn)維工具箱里能節(jié)省很多救火時(shí)間。7.3 網(wǎng)絡(luò)“間歇性”超時(shí)的迷惑行為一個(gè)內(nèi)部服務(wù)偶發(fā)超時(shí)一個(gè)月被投訴兩次每次都是規(guī)律性地在業(yè)務(wù)高峰期出現(xiàn)。單查服務(wù)端負(fù)載不高網(wǎng)絡(luò)流量也不大往返延遲看起來正常。加了抓包后看到規(guī)律抓包客戶端在特定時(shí)間點(diǎn)出現(xiàn)多個(gè)TCP Dup ACK和重傳包。因?yàn)樵L問鏈路上有一跳負(fù)載均衡設(shè)備它的連接跟蹤表老化機(jī)制在高連接數(shù)下會(huì)誤殺部分存活連接導(dǎo)致對(duì)端不斷重傳。繞過或調(diào)整硬件的會(huì)話保持參數(shù)后問題消失。這個(gè)案例值得記住的是網(wǎng)絡(luò)問題有鏈路設(shè)備和主機(jī)兩層很多偶發(fā)問題只在本機(jī)排查永遠(yuǎn)找不到根本原因。要勇于跳出單機(jī)的視角沿著數(shù)據(jù)路徑從應(yīng)用端到服務(wù)端一步步驗(yàn)證包括中間的所有設(shè)備。7.4 幾個(gè)可以直接“抄作業(yè)”的排查口訣帶新人時(shí)我常給幾句口訣也寫在這里供參考負(fù)載高先分三類計(jì)算型看usI/O型看wa鎖型看cs別繞彎。內(nèi)存不足看available別糾結(jié)free太小。磁盤報(bào)滿查兩處空間df -hinode 用df -i都滿了才叫滿。連接異常先分四類超時(shí)、拒絕、重置、卡住——對(duì)應(yīng)不同的排查方向。事前不理解事后不看記錄排查一萬次也就停留在表面。把每次故障的時(shí)間線、根因和修復(fù)方案寫進(jìn)文檔才是成長(zhǎng)最快的方式。7.5 關(guān)于工具權(quán)限與安全操作的提醒最后提一點(diǎn)安全事項(xiàng)。生產(chǎn)環(huán)境使用系統(tǒng)排查工具必須嚴(yán)格遵守“只讀優(yōu)先”的原則。像top、vmstat、iostat、ss、dmesg這些只讀命令可以放心使用但strace會(huì)停住目標(biāo)進(jìn)程尤其高頻場(chǎng)景gdb介入更是停進(jìn)程級(jí)操作使用前必須經(jīng)過審批并在低峰期執(zhí)行。另外涉及修改內(nèi)核參數(shù)的命令每次盡量只改一個(gè)參數(shù)并用sysctl -a驗(yàn)證避免批量改動(dòng)后無法確定問題來源。我自己有過一次教訓(xùn)為了一次性解決某個(gè)網(wǎng)絡(luò)問題我同時(shí)改了五六個(gè)TCP相關(guān)參數(shù)結(jié)果問題沒解決反而引入了新問題——最后花了一下午逐項(xiàng)調(diào)回去才恢復(fù)。從那以后我給自己立了一條規(guī)矩生產(chǎn)環(huán)境的任何調(diào)整都要“小步快走、可回退、留記錄”。寫在這個(gè)內(nèi)容之后做運(yùn)維這些年我最大的體會(huì)是故障排查能力不是靠背命令練出來的而是靠一次次故障后的復(fù)盤積累出來的。工具是固定的系統(tǒng)的機(jī)制是清晰的難的是如何在緊急情況下冷靜地按著鏈路走不被表象帶偏。如果你剛?cè)胄薪ㄗh從今天開始做一個(gè)記錄每次遇到故障把時(shí)間線、現(xiàn)象、排查動(dòng)作、根因、修復(fù)方案寫清楚。堅(jiān)持半年你再看故障視角會(huì)完全不同。還有一點(diǎn)多讀內(nèi)核文檔和系統(tǒng)源碼中對(duì)調(diào)度、內(nèi)存、網(wǎng)絡(luò)機(jī)制的解釋本質(zhì)上排查功夫的深淺其實(shí)就看你對(duì)系統(tǒng)機(jī)制理解的深淺。