ローテーションが何を失うのかを自分で見る
目標
ログローテーションの2つの方式がそれぞれ何を失うかを、実際に作って確かめます。説明を読むことと、ファイルが0バイトのまま残っているのを見ることは違います。
なぜ重要なのか
ローテーションの設定を誤ると、次の2つのうちどちらかが起きます。
createなのにSIGHUPを送らないと、新しいログファイルが永遠に0バイトのままになり、実際のログは.1ファイルへ流れ続けます。失いはしませんが、誰も見つけられませんcopytruncateでは、コピーと切り詰めの間のログが消えます
どちらも障害調査の最中に気づいたのでは、すでに手遅れです。
ステップ
- 書き込み続けるプロセス →
/root/log/01-pid.txt - 削除したのに空きが戻らないもの →
/root/log/02-deleted.txt - 再起動による後始末 →
/root/log/03-restart.txt - renameによる方式の再現 →
/root/log/04-rename.txt - copytruncateによる方式の再現 →
/root/log/05-truncate.txt - logrotate設定の作成 →
/root/log/06-app.conf - 何を失うか →
/root/log/07-tradeoff.md - 後始末 →
/root/log/08-clean.txt
参考
- バックグラウンドプロセスのPIDは
echo $!で得られます。 : > 파일(プレースホルダーはファイル名です)はファイルを0バイトに切り詰めます(truncate)。- コンテナでは通常logrotateを使いません。stdoutに出力すると、コンテナランタイムが代わりにローテーションします。それもこのラボで確認する概念です。
書き込み続けるプロセスを作る
書き込み続けるプロセス → /root/log/01-pid.txt
ログを書き込み続けるプロセスをバックグラウンドで起動します。ファイルを開いたまま書き込む必要があります。このラボが見せようとしている現象はすべて「開いているファイル」が原因で起きるためです。
mkdir -p /root/log
( i=0; while true; do i=$((i+1)); echo "line $i"; sleep 0.05; done ) >> /root/log/app.log &
echo $! > /root/log/01-pid.txt
リダイレクトをループの外側に置くことが核心です。echo ... >> 파일(プレースホルダーはファイル名です)のように内側に置くと、毎回開閉するため、ファイルを削除してもハンドルが残らず、renameしても新しいファイルに書き込むことになります。実際のデーモンはファイルを一度開いて書き込み続けます。
削除しても空きが戻らないことを確認する
削除したのに空きが戻らないもの → /root/log/02-deleted.txt
rm /root/log/app.logを実行してから、ls -l /proc/<PID>/fd | grep deleted(プレースホルダーはプロセスIDです)を見てください。結果を/root/log/02-deleted.txtに保存します。ファイルは消えたのにプロセスはまだ書き込んでおり、そのブロックは解放されません。
再起動して後始末する
再起動による後始末 → /root/log/03-restart.txt
そのプロセスをkill <PID>(プレースホルダーはプロセスIDです)で終了し、新しく起動してください。/root/log/03-restart.txtに2行書きます。終了後のls -l /proc/*/fd 2>/dev/null | grep -c deletedの結果と、なぜ再起動が解決策になるのかを説明する1行です。
renameによる方式を再現する
renameによる方式の再現 → /root/log/04-rename.txt
新しいwriterを起動してからmv /root/log/app.log /root/log/app.log.1を実行し、5秒待ってからwc -lで2つのファイルの行数を確認してください。/root/log/04-rename.txtに2つのファイルの行数を書きます。新しいapp.logは作成すらされていないはずです。それがSIGHUPが必要な理由です。
copytruncateによる方式を再現する
copytruncateによる方式の再現 → /root/log/05-truncate.txt
cp /root/log/app.log.1 /root/log/app.log.2 && : > /root/log/app.log.1でコピーしてから切り詰めを行います。そのあと5秒後にwc -l /root/log/app.log.1を確認してください。/root/log/05-truncate.txtに、切り詰め直後と5秒後の行数を書きます。プロセスが書き込み続けていることを確認するのが目的です。
logrotateの設定を書く
logrotate設定の作成 → /root/log/06-app.conf
/root/log/06-app.confにlogrotateの設定を書きます。必要なもの: パスのパターン、daily、rotate 14、compress、delaycompress、missingok、notifempty、そしてcreateまたはcopytruncateのどちらか1つ。logrotateコマンドがなくても構いません。設定を読み書きできるようになることが目的です。
何を失うかを書く
何を失うか → /root/log/07-tradeoff.md
/root/log/07-tradeoff.mdに4行書きます。createが失うもの / copytruncateが失うもの / 自分が選んだ方式 / その理由です。どちらもタダではないということが、このラボの結論です。
後始末をする
後始末 → /root/log/08-clean.txt
記録しておいたPIDで終了します: kill $(cat /root/log/01-pid.txt)。そのあとrm -f /root/log/app.log*でファイルを削除し、何も残っていないことをpgrep -f sleep | wc -lのようなもので確認して、/root/log/08-clean.txtに書いてください。
⚠️ pkill -f 'while true'は使わないでください。pkillの-fは自分自身のコマンドラインまで対象に含めるため、その文字列を含むシェル自身を終了させてしまいます。実務でもpkill -fで自分のセッションを切ってしまう事故はよくあります。PIDを記録しておき、それで終了するほうが常に安全です。