這兩天客戶問了一個關於 Linux disk 的問題覺得蠻有趣,因為客戶維護的 Amazon EKS 服務發生 ephemeral storage 不足,查詢後卻發現 df
與 du
顯示的硬碟容量不同而求救作者。
在聽到這個問題時,就好像似曾相似曾經遇到過類似的狀況,翻了一下過去寫過的筆記果然找到一篇「Filebeat harvester 的 file handler close 與 clean 機制」裡面過程中講到提到這個案例發生的情形。
一開始我們先來看看在 man 裡面提到關於 df
以及 du
的描述:
df man pages
df – report file system disk space usagedf
displays the amount of disk space available on the file system containing each file name argument. If no file name is given, the space available on all currently mounted file systems is shown.
df 則是基於 file system 層級,磁碟容量是來自於 superblock 來取得大部分的資訊,這包含 metadata, index 和”被開啟”的檔案資訊。
du man pages
du – estimate file space usage
Summarize disk usage of each FILE, recursively for directories.
這意味者 du
是基於 file block 層級,並且 stat 遞迴所有檔案累積的空間。
所以能明顯發覺 du
和 df
本身在收集的方法不同,兩者所顯示的資訊基本上只能拿來參考但不一定完全正確,但 df
能夠這麼快的呈現容量資訊完全是依據 superblock
所提供的資料,而不是”當下”的實際佔用容量,所以有時候 df
和 du
所呈現的佔用容量會有差異,但如果希望確認當前 file system 佔用容量 du
會更可靠,但 du
卻無法計算已經被刪除卻正在打開的文件:
- A 用
vi
開啟 file.txt 未關閉 - B 用
rm
刪除了 file.txt - 使用
df
能計算 file.txt 的空間 - 使用
du
無法找到 file.txt
這導致在使用 du
時仍無法確定是哪個檔案佔用,在這個情況就必須依靠另外一個工具 lsof 來找到開啟卻標記刪除的檔案:
$ lsof / | grep '(deleted)'
沒意外的話,此時會找到許多標記已刪除的檔案,但不論用 ls
或是 du
都找不到這些檔案
/var/log/nginx/access.log (deleted)
這很容易在幾種情境下發生:
- 程式 open file 後並未進行 close file
- 程式對同一個 file 持續 write
當上述情形發生而又加入了 logrotate 機制就會殘留許多 (deleted) 檔案造成佔用系統空間又無法釋放的情況。
短解是將程式重啟釋放這些 file 的使用權,或者是 reboot 救百病。而根解當然是要讓程式不要持續寫同一個檔案,假設是 log 資訊則建議用 YYYY/mm/dd.log 來釋放過期的檔案。
References