服務(wù)被干爆了!竟然是日志的鍋!!
本文要介紹的是一個發(fā)生在我們線上環(huán)境的真實(shí)案例,問題發(fā)生在某次大促期間,對我們的線上集群造成了比較大的影響,這篇文章簡單復(fù)盤一下這個問題。
為了方便大家理解,實(shí)際排查和解決過程可能和本文描述的并不完全一致,但是思路是一樣的。
問題過程
某次大促期間,某一個線上應(yīng)用突然發(fā)生大量報警,提示磁盤占用率過高,一度達(dá)到了80%多。
這種情況我們第一時間登錄線上機(jī)器,查看線上機(jī)器的磁盤使用情況。使用命令:df查看磁盤占用情況。
- $df
- Filesystem 1K-blocks Used Available Use% Mounted on
- / 62914560 58911440 4003120 93% /
- /dev/sda2 62914560 58911440 4003120 93% /home/admin
發(fā)現(xiàn)機(jī)器磁盤確實(shí)耗費(fèi)的比較嚴(yán)重,因?yàn)榇蟠倨陂g請求量比較多,于是我們最先開始懷疑是不是日志太多了,導(dǎo)致磁盤耗盡。
這里插播一個背景,我們的線上機(jī)器是配置了日志的自動壓縮和清理的,單個文件達(dá)到一定的大小,或者機(jī)器內(nèi)容達(dá)到一定的閾值之后,就會自動觸發(fā)。
但是大促當(dāng)天并沒有觸發(fā)日志的清理,導(dǎo)致機(jī)器磁盤一度被耗盡。
經(jīng)過排查,我們發(fā)現(xiàn)是應(yīng)用的某一些Log文件,占用了極大的磁盤空間,并且還在不斷的增大。
- du -sm * | sort -nr
- 512 service.log.20201105193331
- 256 service.log
- 428 service.log.20201106151311
- 286 service.log.20201107195011
- 356 service.log.20201108155838
- du -sm * | sort -nr :統(tǒng)計(jì)當(dāng)前目錄下文件大小,并按照大小排序
于是經(jīng)過和運(yùn)維同學(xué)溝通,我們決定進(jìn)行緊急處理。
首先采取的手段就是手動清理日志文件,運(yùn)維同學(xué)登錄到服務(wù)器上面之后,手動的清理了一些不太重要的日志文件。
- rm service.log.20201105193331
但是執(zhí)行了清理命令之后,發(fā)現(xiàn)機(jī)器上面的磁盤使用率并沒有減少,而且還是在不斷的增加。
- $df
- Filesystem 1K-blocks Used Available Use% Mounted on
- / 62914560 58911440 4003120 93% /
- /dev/sda2 62914560 58911440 4003120 93% /home/admin
于是我們開始排查為什么日志被刪除之后,內(nèi)存空間沒有被釋放,通過命令,我們查到了是有一個進(jìn)程還在對文件進(jìn)行讀取。
- lsof |grep deleted
- SLS 11526 root 3r REG 253,0 2665433605 104181296 /home/admin/****/service.log.20201205193331 (deleted)
- lsof |grep deleted 的作用是:查看所有已打開文件并篩選出其中已刪除狀態(tài)的文件
經(jīng)過排查,這個進(jìn)程是一個SLS進(jìn)程,在不斷的從機(jī)器上讀取日志內(nèi)容。
SLS是阿里的一個日志服務(wù),提供一站式提供數(shù)據(jù)收集、清洗、分析、可視化和告警功能。簡單點(diǎn)說就是會把服務(wù)器上面的日志采集到,持久化,然后供查詢、分析等。
我們線上日志都會通過SLS進(jìn)行采集,所以,通過分析,我們發(fā)現(xiàn)磁盤空間沒釋放,和SLS的日志讀取有關(guān)。
到這里,問題基本已經(jīng)定位到了,那么我們插播一下原理,介紹一下這背后的背景知識。
背景知識
Linux系統(tǒng)中是通過link的數(shù)量來控制文件刪除的,只有當(dāng)一個文件不存在任何link的時候,這個文件才會被刪除。
一般來說,每個文件都有2個link計(jì)數(shù)器:i_count 和 i_nlink,也就是說:Linux系統(tǒng)中只有i_nlink及i_count都為0的時候,這個文件才會真正被刪除。
- i_count表示當(dāng)前文件使用者(或被調(diào)用)的數(shù)量,
- i_nlink表示介質(zhì)連接的數(shù)量(硬鏈接的數(shù)量);
- 可以理解為i_count是內(nèi)存引用計(jì)數(shù)器,i_nlink是磁盤的引用計(jì)數(shù)器。
當(dāng)一個文件被某一個進(jìn)程引用時,對應(yīng)i_count數(shù)就會增加;當(dāng)創(chuàng)建文件的硬鏈接的時候,對應(yīng)i_nlink數(shù)就會增加。
在Linux或者Unix系統(tǒng)中,通過rm或者文件管理器刪除文件,只是將它會從文件系統(tǒng)的目錄結(jié)構(gòu)上解除鏈接(unlink),實(shí)際上就是減少磁盤引用計(jì)數(shù)i_nlink,但是并不會減少i_count數(shù)。
如果一個文件正在被某個進(jìn)程調(diào)用,用戶使用rm命令把文件"刪除"了,這時候通過ls等文件管理命令就無法找到這個文件了,但是并不意味著這個文件真正的從磁盤上刪除了。
因?yàn)檫€有一個進(jìn)程在正常的執(zhí)行,在向文件中讀取或?qū)懭?,也就是說文件其實(shí)并沒有被真正的"刪除",所以磁盤空間也就會一直被占用。
而我們的線上問題就是這個原理,因?yàn)橛幸粋€進(jìn)程正在對日志文件進(jìn)行操作,所以其實(shí)rm操作并沒有將文件真正的刪除,所以磁盤空間并未釋放。
問題解決
在了解了線上的問題現(xiàn)象以及以上的相關(guān)背景知識之后,我們就可以想到辦法來解決這個問題了。
那就是想辦法把SLS進(jìn)程對這個日志文件的引用干掉,文件就可以真正的被刪除,磁盤空間就能真正的被釋放掉了。
- kill -9 11526
- $df
- Filesystem 1K-blocks Used Available Use% Mounted on
- / 62914560 50331648 12582912 80% /
- /dev/sda2 62914560 50331648 12582912 80% /home/admin
特別提醒下,在執(zhí)行kill -9 之前,一定要考慮下執(zhí)行的后果是什么,背后原理可以參考:我到服務(wù)器執(zhí)行kill -9后,就被通知第二天別來了!
事后,我們經(jīng)過復(fù)盤,發(fā)現(xiàn)之所以出現(xiàn)這樣的問題,主要有兩個原因:
- 1、線上日志打印太多,太頻繁
- 2、SLS日志拉取速度太慢
深入分析后我們發(fā)現(xiàn),這個應(yīng)用打印了很多過程日志,最初日志打印是為了方便排查線上的問題,或者做數(shù)據(jù)分析用的,但是大促期間日志量激增,導(dǎo)致磁盤空間占用極速上升。
另外,因?yàn)樵搼?yīng)用和幾個其他的大應(yīng)用共用了一份SLS的project,導(dǎo)致SLS拉取速度被拉低,進(jìn)而導(dǎo)致進(jìn)程一直無法結(jié)束。
事后,我們也總結(jié)了一些改進(jìn)項(xiàng),對于第二個問題,我們對于該應(yīng)用的SLS配置做拆分,獨(dú)立出來進(jìn)行管理。
對于第一個問題,那就是大促期間引入日志降級的策略,一旦發(fā)生磁盤爆滿,就是將日志降級掉。
關(guān)于日志降級,我開發(fā)了一個通用的工具,就是通過配置的方式,動態(tài)推送日志級別,動態(tài)修改線上的日志輸出級別。并且把這份配置的修改配置到我們的預(yù)案平臺上,大促期間進(jìn)行定時或者緊急預(yù)案處理,即可避免這個問題。
關(guān)于日志降級工具的開發(fā)思路和相關(guān)代碼,下一篇文章中給大家分享。
思考
每次大促之后我們復(fù)盤,都會發(fā)現(xiàn)其實(shí)大多數(shù)問題都是由幾個不起眼的小問題堆積到一起而引發(fā)的。
在問題分析過程中往往會需要運(yùn)用到很多非開發(fā)技能相關(guān)的知識,如操作系統(tǒng)、計(jì)算機(jī)網(wǎng)絡(luò)、數(shù)據(jù)庫、甚至硬件相關(guān)的知識。
所以我一直認(rèn)為,判斷一個程序員是否牛X,就看他的解決問題的能力!