ログローテーション

ログは黙ってディスクを食い潰す

3章で専用のアクセスログを作りました。訪問のたびに1行ずつ増えます。1行はせいぜい 200 バイトですが、増え続けることに変わりはありません。

1日1万アクセスなら1日およそ 2MB、1年で 700MB です。放っておくとディスクが埋まります。ディスクが埋まったサーバーは、ログが書けないだけでは済みません。一時ファイルも作れず、データベースも書けず、全体が止まります。運用で最も多い停止原因のひとつです。

手で消すのは現実的ではありません。消すタイミングを忘れますし、消した瞬間に調査したい記録まで失います。そこで、古いものから順に畳んでいく仕組みを使います。

畳んで、圧縮して、捨てる

logrotate は次の順で処理します。

  1. いまのログに別の名前を付ける (mysite-access.logmysite-access.log-20260808 に)
  2. 空の mysite-access.log を作り直す
  3. 名前を変えたほうを gzip で圧縮する
  4. 決めた世代数を超えた古いものを消す

ログは文字ばかりなので、圧縮はよく効きます。10 分の 1 以下になることも珍しくありません。

設定はパッケージが置いている

Debian の nginx は、導入した時点で自分の設定を置いています。

cat /etc/logrotate.d/nginx
/var/log/nginx/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 www-data adm sharedscripts ... }

先頭が対象のファイルで、* が効くので3章で作った mysite-access.log も最初から含まれています。中括弧の中が指定です。

  • daily — 1日1回畳む。weeklymonthly も書ける
  • rotate 14 — 14 世代残す。15 個目が生まれると最も古いものが消える
  • compress — 畳んだものを gzip で圧縮する
  • delaycompress — 圧縮を1回ぶん先送りする
  • missingok — ファイルが無くてもエラーにしない
  • notifempty — 中身が空なら畳まない
  • create 0640 www-data adm — 作り直すファイルの権限と持ち主

create の行が効きます。4章で見たとおり、nginx の worker は www-data で動いています。作り直したファイルがその手に負えない権限だと、翌日からログが1行も書かれません。

delaycompress が何をしているか

分かりにくいのがこの1行です。付けると、畳んだ直後は圧縮せず、次に畳むときにまとめて圧縮します。

理由は、プロセスがまだ古いファイルを開いたまま書き続けている場合があるからです。書いている最中に圧縮すると、中身が壊れます。1回ぶん待てば安全というわけです。

引き換えに、直前の1世代だけは圧縮されないまま残ります。畳んだのに .gz が見当たらないと焦る原因はたいていこれです。

次の節で、畳んだ直後に nginx へ開き直させる設定を入れます。書き込み先が切り替わったあとなら圧縮しても壊れないので、この先送りは外して構いません。

名前を日付にする

既定では mysite-access.log.1mysite-access.log.2 と番号が付きます。番号は畳むたびにずれるので、「3日前のログ」を探すのに数える必要があります。

dateext

この1行で mysite-access.log-20260808 という日付つきの名前になります。ずれない名前になるので、後から探すのが楽です

畳んだあと、開き直させる

もう1つ、見落とすと痛い部分があります。畳むというのは、ファイルの名前を変えているだけです。nginx は名前ではなく開いた実体に書き続けるので、何もしなければ畳んだあとの書き込みは、名前を変えたほうへ入り続けます。作り直された新しいログはいつまでも空のままです。

そこで、畳んだあとにプロセスへ「開き直せ」と伝えます。

postrotate if [ -f /run/nginx.pid ]; then kill -USR1 $(cat /run/nginx.pid); fi endscript

postrotateendscript の間は、畳んだあとに走らせる処理です。kill は名前に反して、プロセスへ合図を送る命令です。-USR1 は nginx にとって「ログを開き直せ」の意味で、2章で systemctl reload が裏で送っていたものと同じ種類の合図です。

/run/nginx.pid には、動いている nginx の番号が書かれています。存在を確かめてから送るのは、nginx が止まっているときに空振りさせないためです。

Debian の nginx は既定で invoke-rc.d nginx rotate を呼びますが、systemd で動かしている環境では成功したように見えて何も起きません。畳んだ翌日からログが1行も増えていない、という形で気づくことになります。自分の手で確かめておく価値があります。

待たずに試す

設定を書いても、実際に畳まれるのは翌日です。それでは確かめられないので、強制的に走らせます。

logrotate -d /etc/logrotate.d/nginx # 何をするかだけ見る logrotate -f /etc/logrotate.d/nginx # 実際に畳む

-d は試し打ちで、実行せずに手順だけ出します。-f は、まだその時期でなくても強制的に畳ませます。まず -d で確かめてから -f。この順を守ると、設定を間違えたまま本物のログを畳んでしまう事故が防げます。

手を動かす

nginx の設定を、日付つきの名前で7世代だけ残す形に直します。畳んだあとにログを開き直させるところまで入れて、実際に畳まれること、そのあとも書き続けられることを確かめます。

ヒント

  • 1. curl でサイトに何度かアクセスし、ログに中身がある状態にする
  • 2. /etc/logrotate.d/nginx の rotate 14 を rotate 7 に直す
  • 3. dateext の行を足す
  • 4. delaycompress の行を消す
  • 5. postrotate の中身を if [ -f /run/nginx.pid ]; then kill -USR1 $(cat /run/nginx.pid); fi に書き換える
  • 6. logrotate -d /etc/logrotate.d/nginx で何をするか先に見る
  • 7. logrotate -f /etc/logrotate.d/nginx で実際に畳ませる
  • 8. ls -l /var/log/nginx/ > /root/rotated.txt で結果を保存し、もう一度 curl してログが書けることを確かめる

12 項目で採点します