實體主機日誌容量管理:控制增長,驗證空間回收

實體主機日誌容量管理:控制增長,驗證空間回收

資料庫寫入失敗,磁碟上卻沒有新增業務檔案,常見原因是錯誤日誌反覆輸出,先把系統分割區寫滿。管理日誌容量並不是定期刪除目錄,而是分別控制寫入、輪替與保留,並驗證空間確實已回收。本文以 Debian 12 為例,處理系統 journal 與短時間任務的檔案日誌,不修改資料庫檔案或既有的儲存配置。

CloudSFP 的實體主機標配兩顆各自直連的 1TB NVMe,沒有硬體 RAID。不能將兩顆磁碟的容量直接視為日誌所在檔案系統的可用空間;日誌容量預算必須以實際掛載點為準。以下容量皆為操作範例,不是服務的預設設定。

一、先找出空間占用,區分磁碟區塊與 inode

付款後,在控制台查看登入憑證,透過 SSH 登入。主機有多個位址時,使用第一個位址。先執行不會變更資料的唯讀檢查:

df -hT / /var/log
df -i / /var/log
findmnt -T /var/log
sudo du -xhd1 /var/log
sudo journalctl --disk-usage

df 顯示檔案系統的使用量,du 則統計目錄中可見的檔案,兩者不一定相等。-x 可避免跨越其他檔案系統,降低將獨立掛載的磁碟區混入結果的風險。若 /var/log 不是獨立分割區,日誌就會與系統檔案共同消耗根分割區的空間。

空間充足但 inode 即將耗盡時,應檢查是否有大量小檔案,而不是只找最大的日誌。先記錄檔案系統、可用空間、inode 使用量,以及增長最快的目錄,再決定處理對象。不要將診斷紀錄繼續寫入已接近滿載的分割區。

不要直接刪除整個 /var/log,也不要截斷尚未妥善保存的故障日誌。清理前應保存相關時間範圍的佐證資料,並確認目標不是資料庫資料、交易日誌或備份檔案。

二、為 journald 設定獨立的容量預算

持久儲存的 journal 通常位於 /var/log/journal。透過 drop-in 設定檔管理限制,比直接覆寫主設定檔更容易稽核與撤銷。以下範例設定日誌容量預算,以及檔案系統應保留的可用空間:

sudo install -d -m 0755 /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/60-capacity.conf >/dev/null <<'EOF'
[Journal]
Storage=persistent
SystemMaxUse=1G
SystemKeepFree=4G
MaxFileSec=1day
EOF

sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
sudo journalctl --flush
sudo journalctl --disk-usage
sudo systemctl status systemd-journald --no-pager

SystemMaxUse 控制 journal 的使用容量預算,SystemKeepFree 要求在同一個檔案系統中保留可用空間,實際行為也會受當前剩餘容量影響。這些設定不會限制應用程式自行寫出的檔案。MaxFileSec 控制作用中檔案所涵蓋的最長時間範圍,不代表只保留一天的日誌。

若其他程式同時將相同訊息寫入文字日誌,限制 journal 並不會消除那份副本。需要檢查日誌的輸出流程,分別管理,不要未經確認就停用系統日誌服務。

清理前先輪替,再核對結果

sudo journalctl --rotate
sudo journalctl --vacuum-size=1G
sudo journalctl --disk-usage
df -h /var/log

vacuum 只會刪除封存檔案,不會直接刪除作用中的檔案,因此最終使用量不保證剛好等於指定值。重新啟動 journald 屬於設定變更,應先確認目前的服務狀態,執行後再檢查是否出現新錯誤,以及業務日誌是否持續寫入 journal。

三、明確劃分檔案日誌的輪替責任

先確認由哪個程式寫入日誌、是否長時間保持檔案開啟,以及既有規則是否已涵蓋該路徑。同一個檔案不能同時交由應用程式內建的輪替機制與 logrotate 管理,也不要新增涵蓋範圍過廣的比對規則,與系統既有規則重複。

安裝檢查工具:

sudo apt-get update
sudo apt-get install logrotate lsof

假設短時間任務每次啟動時都會重新開啟 /var/log/local-batch/run.log,並在結束後關閉檔案,可在 /etc/logrotate.d/local-batch 寫入:

/var/log/local-batch/run.log {
    daily
    maxsize 100M
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 0640
}

目錄與原始日誌應事先存在,擁有者設定也應與任務執行帳號相符。create 0640 未指定擁有者時,會沿用原檔案的擁有者與群組。若目錄允許非特權帳號寫入,應另外評估 su 指令與目錄權限,不能直接套用這份規則。

sudo logrotate -d /etc/logrotate.conf
systemctl list-timers --all --no-pager

-d 只會輸出模擬結果,不會執行輪替。請檢查計時器或 cron 的實際執行頻率:maxsize 只在 logrotate 被呼叫時檢查,不是即時生效的硬性上限;rotate 7 表示保留七份封存檔,也不一定等於七天。

常駐程序需要使用應用程式支援的日誌重新開啟機制。檔案重新命名後,程序可能仍持續寫入舊檔案。不要猜測該傳送哪個訊號,也不要直接預設使用 copytruncate;後者在複製與截斷之間有一段可能遺失日誌的時間窗口。

四、在隔離目錄中測試輪替與權限

不要為了測試新規則,就對全域設定執行強制輪替。以下實驗使用獨立目錄與狀態檔,不會碰觸正式環境的日誌:

umask 077
test_dir=$(mktemp -d)
printf '%0200d\n' $(seq 1 20) >"$test_dir/run.log"

cat >"$test_dir/rules" <<EOF
"$test_dir/run.log" {
    size 1k
    rotate 2
    create 0640
}
EOF

logrotate -d -s "$test_dir/state" "$test_dir/rules"
logrotate -s "$test_dir/state" "$test_dir/rules"
ls -l "$test_dir"
printf 'after rotation\n' >>"$test_dir/run.log"
cat "$test_dir/run.log"

應能看到封存檔案、新建立的作用中檔案,以及符合預期的權限。最後一筆寫入只證明重新開啟該路徑可以成功,不能證明常駐應用程式會自動切換檔案。正式環境驗收時,還必須讓實際任務產生一筆可識別的紀錄,確認它出現在新的作用中檔案內。

保留實驗目錄以檢查結果,確認後再刪除。正式環境的規則仍由系統既有的狀態檔管理,不要讓手動呼叫與排程作業同時執行,也不要長期使用兩個狀態檔管理同一份日誌。

五、驗收空間回收結果,並處理重複錯誤

刪除檔案後,若可用空間沒有增加,先檢查仍被程序開啟並持有的檔案:

sudo lsof +L1
df -h /var/log
df -i /var/log
sudo du -xhd1 /var/log

lsof +L1 會列出連結數小於一的開啟檔案。應綜合路徑、大小與程序判斷,不要將所有結果都視為日誌。確認目標後,依應用程式文件重新開啟日誌,必要時重新啟動對應服務,再次檢查 df。不要藉由終止不明程序,或寫入其檔案描述元,強行回收空間。

驗收時至少保留四項結果:新紀錄進入作用中檔案、封存數量符合規則、帳號能持續寫入,以及檔案系統的可用空間沒有持續異常下降。若錯誤訊息仍大量重複出現,應修正失敗後重試、無效請求或偵錯輸出的源頭;輪替不能取代故障處理。

容量預算也無法保證保留固定時間範圍的歷史紀錄。尖峰期間日誌增長更快,同樣的預算會更早淘汰舊紀錄。需要長期留存的佐證資料,應另行設計傳輸、存取控制與備份機制,並實際驗證能否讀取;本機日誌輪替只負責限制本機使用量,不承擔備份責任。

常見問題

journalctl vacuum 能立即把全部日誌縮到指定大小嗎?

不能。vacuum 只清除已封存的日誌,不會直接刪除目前使用中的檔案。先執行 journalctl --rotate,再執行 journalctl --vacuum-size=1G,並以 journalctl --disk-usage 和 df 檢查使用量。

刪除日誌後,為何 df 顯示的可用空間沒有增加?

程序可能仍持有已刪除檔案的描述符。以 sudo lsof +L1 核對大小與程序,再依應用程式支援的方法重新開啟日誌,或重新啟動對應服務。

AMD EPYC 7543P 專屬實體主機每月 $441.4 起