一次磁碟佔用率 100% 的排查記錄

2022-10-10 12:00:37

你好,我是悟空。

最近遇到一個伺服器的問題:磁碟滿了,佔用率 100%~

這個問題太常見了,於是先來排查一波是哪些檔案佔用了大量磁碟。

一、排查磁碟佔用率100%

1.1 檢視磁碟使用的大致情況

第一個命令就是 df -h,來檢視磁碟的佔用情況。df 是 disk free 的縮寫,用於顯示目前在 Linux 系統上的檔案系統磁碟的使用情況統計。

如下圖所示,可以看到磁碟佔用率 100%。

第一列 Filesystem 代表檔案系統的名稱。

第二列 Size 表示檔案系統的大小。

第三列 Used 表示已佔用多大的磁碟空間。

第四列 Avail 表示可用磁碟空間的大小。

第五列 Use% 表示磁碟使用率多大,100% 表示磁碟佔滿了。

第六列 Mounted On 表示掛載在哪個目錄。

從上面的結果可以看到有個檔案系統佔用率為 100%,為/dev/sda2,掛載的目錄為 /,那我們就來看下這個目錄下都存放哪些大檔案吧。

1.2 檢視目錄下的大檔案

使用 du 命令來顯示目錄或檔案所佔用的磁碟空間大小。

# 先進入到根目錄 `/`
cd /
# 列出當前目錄或檔案的總大小,並按倒敘排序
du -sh /* | sort -nr

找到最大的目錄 var,佔用 100 多個 G。進入到這個目錄中,然後再次執行 du 命令

du -sh /var/* | sort -nr

這樣一級一級的找,就能找到佔用空間最大的檔案了。

最後發現是 Logstash 容器的紀錄檔檔案佔用太大,截圖如下。大家看到的佔用 4.8G,這是我清理紀錄檔之後的大小,之前這個容器佔用 90 多個 G。

1.3 Logstash 容器為什麼佔用磁碟這麼大

為啥 Logstash 容器會有這麼多紀錄檔???

我們看下這個紀錄檔裡面的內容就知道了。使用 tail 命令檢視檔案的最後 100 行資料。

tail -n 100 <容器 id>-json.log
# 也可以通過 docker logs 來檢視紀錄檔
docker logs  --tail=100 159

發現全都是 Logstash 解析紀錄檔時列印的資訊。如下圖所示:

每次 Filebeat 採集紀錄檔後,傳輸給 Logstash 後,Logstash 都會列印一條解析後的紀錄檔。

而我們的後端服務會列印很多紀錄檔,傳輸給 Logstash 的紀錄檔會越來越多,Logstash 又會瘋狂的寫自己的解析紀錄檔,久而久之,磁碟就被佔滿了

問題原因找到了,那解決方案是什麼呢?

二、容器紀錄檔清理方案

  • 方案一:手動清理紀錄檔檔案,可解燃眉之急,治標不治本。

  • 方案二:指令碼定期清理紀錄檔檔案,缺點是紀錄檔檔案全丟了,無法追溯。

  • 方案三:限制所有容器的紀錄檔檔案大小,治本,缺點是需要重新建立容器和啟動 docker 映象。

2.1 方案一:手動清理方案

cat /dev/null &gt; /var/lib/docker/containers/容器id/容器id-json.log

注意:這裡沒有使用 rm 方式來刪除檔案。使用 rm -rf 方式刪除紀錄檔後,通過 df -h 會發現磁碟空間並沒有釋放。原因是在Linux或者Unix系統中,通過 rm -rf 或者檔案管理器刪除檔案,將會從檔案系統的目錄結構上解除連結(unlink)。如果檔案是被開啟的(有一個程序正在使用),那麼程序將仍然可以讀取該檔案,磁碟空間也一直被佔用。正確姿勢是cat /dev/null > *-json.log,當然你也可以通過rm -rf刪除後重啟docker。

2.2 方案二:指令碼定期清理

提供一個清理指令碼

#!/bin/sh 

echo "======== start clean docker containers logs ========"  

logs=$(find /var/lib/docker/containers/ -name *-json.log)  

for log in $logs  
        do  
                echo "clean logs : $log"  
                cat /dev/null &gt; $log  
        done  

echo "======== end clean docker containers logs ========"

給指令碼新增許可權

chmod +x clean_docker_log.sh

執行指令碼的命令

./clean_docker_log.sh

大家可以把執行指令碼的命令加到 Linux 的定時任務中就可以了,這裡不做展開。

下面要說的是我目前使用的方案。

2.3 方案三:限制 Docker 容器紀錄檔大小

新建 /etc/docker/daemon.json,若有就不用新建了。

vim /etc/docker/daemon.json

設定內容如下:

{
"log-driver":"json-file",
"log-opts": {"max-size":"500m", "max-file":"3"}
}

max-size=500m,表示容器的紀錄檔檔案大小上限是 500M,
max-file=3,表示容器有三個紀錄檔,第一個滿了 500M之後就寫第二個,第二個滿了 500M就寫第三個,如果第三個滿了,就清空第一個紀錄檔檔案,重新寫第一個紀錄檔檔案。如下圖所示:

!](https://img-blog.csdnimg.cn/img_convert/92874089e5414db50649cd6065a4c0c4.png)

改完之後需要重啟 docker 守護行程

systemctl daemon-reload
systemctl restart docker

另外這種方式只對新建的容器有效的,之前的容器不生效,所以我又把之前的 Logstash 容器刪除了,然後重新啟動了一個 Logstash 容器。

參考資料:

https://www.cnblogs.com/gcgc/p/10521005.html

Linux df 命令 https://www.runoob.com/linux/linux-comm-df.html

Linux du 命令 https://www.runoob.com/linux/linux-comm-du.html

關於我

8 年網際網路開發經驗,擅長微服務、分散式、架構設計。目前在一家大型上市公司從事基礎架構和效能優化工作。

InfoQ 簽約作者、藍橋簽約作者、阿里雲專家博主、51CTO 紅人。

我的所有文章都彙總到這裡了 http://www.passjava.cn</容器>