【logrotate講座 第3回】「ログが消えた!?」を防ぐ。copytruncateとcreateの決定的な違いとiノードの秘密

ファイル名を変えたのに、なぜ書き込み続けるのか?

こんにちは!「LINUX工房」管理人の「リナックス先生」です。
前回は、logrotateの設定オプションを駆使して、期間やサイズで自在にローテーションする方法を学びました。

しかし、設定ファイルが完璧に見えても、実際に動かしてみると「恐ろしい現象」に遭遇することがあります。
「ローテーションされたはずの新しいログファイルが、いつまで経っても空っぽのまま(0バイト)」
「リネームされて access.log.1 になったはずのファイルに、まだ最新のログが書き込まれ続けている」
そして最悪の場合、「一瞬の隙にログの一部が消滅している」ことさえあります。

コウ君

先生、まさにその現象に悩まされてます!
自作したPythonのプログラムのログをローテートさせようとしたら、ファイル名は変わってるのに、プログラムは頑固に古いファイルに書き込み続けてるんです。
新しいファイルの方に書いてほしいのに、どうやって教えればいいんですか?

リナックス先生

それはね、Linuxがファイルを「名前」ではなく「番号(iノード)」で管理しているからよ。
人間にとっては「名前が変わった=別のファイル」だけど、プログラムにとっては「同じファイル」のままなの。
この仕組みを理解せずに logrotate を使うのは、目隠しで手術をするようなもの。
今回は、Linuxの深淵「iノード」を理解して、適切な切り替え方式(create / copytruncate)を選べるようになりましょう!

本記事では、Linuxファイルシステムの基礎知識から、logrotateの2つの主要な動作モード「create」「copytruncate」の内部挙動、そしてそれぞれのメリット・デメリットを徹底的に解剖します。

🔄 logrotate完全攻略講座(バックナンバー)

現在地:【第3回】「ログが消えた!?」を防ぐ。copytruncateとcreateの決定的な違いとiノードの秘密


第1章:なぜ「ファイル名を変えただけ」ではダメなのか?

logrotateの挙動を理解する前に、Linux(Unix系OS)がファイルをどう扱っているかを知る必要があります。
キーワードは「iノード(inode)」です。

ファイル名と「実体」は別物

私たちは普段、ファイル名(例:access.log)を見てファイルを識別しています。
しかし、OS(カーネル)はファイル名を見ていません。
OSが見ているのは、ディスク上のデータの場所や属性を管理する「iノード番号」というID番号です。

  • iノード: データの本体(実体)。ID番号、サイズ、権限、ディスク上の位置などの情報を持つ。
  • ファイル名: iノードへの「リンク(名札)」。ディレクトリの中に「ファイル名 ⇔ iノード番号」の対応表があるだけ。

実験:リネームしても書き込みは止まらない

この仕組みを証明する簡単な実験をしてみましょう。
ターミナルを2つ開いてください。

ターミナル1(書き込み役):

# test.log にずっと書き込み続ける
while true; do echo "Writing..." >> test.log; sleep 1; done

ターミナル2(操作役):

# iノード番号を確認(-i オプション)
ls -i test.log
# 出力例: 123456 test.log

# ファイル名を変更する(ログローテートの動作)
mv test.log test.log.1

# 再度iノードを確認
ls -i test.log.1
# 出力例: 123456 test.log.1

ファイル名を変えても、iノード番号(123456)は変わりません。
そして重要なのは、「ターミナル1のプログラムは、ファイルを開いた時点でのiノード(123456)を掴み続けている」ということです。

そのため、いくら外側で test.log.1 に名前を変えても、プログラムは相変わらず(名前が変わった)そのファイルにログを書き込み続けます。
新しく test.log を作成しても、プログラムはそちらを知る由もありません。

これが、「新しいログファイルが空っぽのまま」という現象の正体です。
logrotateには、この問題を解決するための2つのアプローチ(モード)があります。


第2章:createモード(リネーム&新規作成)の全貌

一つ目は、デフォルトの動作である「create」モードです。
ApacheやNginx、MySQLなど、一般的なミドルウェアでよく使われます。

処理の流れ

  1. リネーム: access.logaccess.log.1 に名前変更します。
    (この時点では、アプリはまだ access.log.1 に書き込み続けています)
  2. 新規作成: 空の新しい access.log を作成します。
    (まだアプリはこのファイルの存在を知りません)
  3. 通知(重要): logrotateがアプリに対して「ログファイルを掴み直せ(Reopen)」という合図(シグナル)を送ります。
  4. 切り替え: シグナルを受け取ったアプリは、現在のファイルを閉じて、新しい access.log を開き直し、書き込みを再開します。

必須条件:postrotate設定

このモードを使うには、アプリケーション側が「シグナルを受け取ったらログを開き直す機能」を持っている必要があります。
そして、設定ファイルの postrotate セクションで、そのシグナルを送るコマンドを書く必要があります。

Apacheの設定例:

/var/log/httpd/*log {
    create 644 root root
    postrotate
        # Apacheに再読み込みシグナルを送るコマンド
        /bin/systemctl reload httpd.service > /dev/null 2>/dev/null || true
    endscript
}

メリットとデメリット

✅ メリット
ログの取りこぼしがない: ファイルの切り替えが一瞬で行われ、書き損じがほぼ発生しない。
アトミック性: データの整合性が保たれやすい。

❌ デメリット
設定が難しい: アプリごとに「どうやってリロードさせるか」を知る必要がある。
権限問題: logrotate(root)からアプリのプロセスにシグナルを送る権限が必要。
対応していないアプリが多い: 自作スクリプトやJavaアプリなどは、リロード機能がない場合が多い。


第3章:copytruncateモード(コピー&切り詰め)の全貌

二つ目は、シグナルを送れないアプリのための救世主、「copytruncate(コピートランケート)」モードです。
「truncate」とは「切り詰める(サイズをゼロにする)」という意味です。

処理の流れ

  1. コピー: 現在の access.log の中身を、access.log.1コピーします。
    (リネームではなくコピーなので、元のファイルは残っています)
  2. 切り詰め (Truncate): 元の access.log のファイルサイズを0バイト(空)にします。
    (プログラムはずっと同じファイルを掴んでいるので、そのまま書き込みを続けられます)

iノードが変わらない魔法

この方式の最大の特徴は、「iノード番号が変わらない」ことです。
ファイルの中身だけを抜き取って空にするため、アプリケーション側は何も気づかずに、同じファイルハンドルに対して書き込みを継続できます。
したがって、再起動やシグナル送信は不要です。

致命的な弱点:ログ消失のリスク

copytruncateは便利ですが、構造上の欠陥があります。
それは、「コピーが終わってから、空にするまでの一瞬の隙」です。

  1. logrotateがデータをコピー完了。
  2. (ここでアプリが新しいログ行を書き込む!)
  3. logrotateがファイルを空にする(truncate)。

このタイミングで書き込まれたログは、コピー先にも含まれず、元のファイルからも消去されるため、完全に消失します。
アクセスが多いサーバーでは、この現象が頻発する可能性があります。

メリットとデメリット

✅ メリット
どんなアプリでも使える: リロード機能がないプログラムでもログローテートが可能。
設定が簡単: postrotate を書かなくて良い。

❌ デメリット
ログ消失リスク: 上記の通り、切り替えの瞬間のログが消える可能性がある。
I/O負荷が高い: 巨大なファイル(数GB)をコピーするため、その間ディスクIOとCPUを消費する。
ディスク容量の一時的倍増: コピー中は元ファイルとコピーファイルが両方存在するため、一時的に2倍の容量が必要。


第4章:究極の選択!どちらを選ぶべきか?

「じゃあ結局どっちを使えばいいの?」
その答えは、対象のアプリケーションによって決まります。

比較マトリクス

項目 create + postrotate copytruncate
対象アプリ シグナル受信可能なもの
(Apache, Nginx, MySQL, Postfix)
シグナル非対応なもの
(Rails, Python, Java, 自作ツール)
信頼性 高い(消失ほぼなし) 低い(消失リスクあり)
負荷 低い(リネームだけ) 高い(データコピー発生)
iノード 変わる 変わらない
推奨度 基本はこちら やむを得ない場合のみ

アプリケーション別推奨設定

  • Apache / Nginx: 必ず create を使い、systemctl reload を行います。
  • MariaDB / MySQL: 必ず create を使い、mysqladmin flush-logs を行います。
  • Laravel / Rails / Django: 通常はアプリ自体にリロード機能がないため、copytruncate を使います。
    • ※ただし、uWSGIやGunicorn、Passenger経由で動かしている場合は、それらの管理プロセスにシグナルを送ることで create が使える場合もあります。
  • 自作のシェルスクリプト: >> で追記しているだけの単純なスクリプトなら copytruncate が無難です。

第5章:トラブルシューティング「ログが迷子になった時」

設定したはずなのにうまく動かない時の、具体的な調査手順です。

現象1:新しいログファイルが空っぽのまま

原因: create モードなのに、アプリにシグナルを送っていない(または失敗している)。
アプリは古いファイル(access.log.1)を掴んだまま書き込んでいます。

調査コマンド:lsof(List Open Files)

# httpdプロセスが開いているログファイルを確認
sudo lsof -p $(pgrep -d, httpd) | grep log

出力例:
httpd 1234 root 3w REG 8,1 1024 9999 /var/log/httpd/access.log.1

このように、ファイル名が access.log.1(古い方)になっている場合、リロードが行われていません。
postrotate の記述を見直しましょう。

現象2:謎のバイナリデータがログに含まれる

原因: compress(圧縮)と copytruncate を併用した場合、あるいは delaycompress なしで create した場合に起こります。

アプリが書き込んでいる最中に gzip 圧縮が始まってしまい、テキストデータと圧縮データが混ざってしまう現象です。
これを防ぐには、次章で解説する delaycompress が必須です。


第6章:応用編:delaycompressが必要な本当の理由

第2回でも少し触れましたが、iノードの仕組みを理解した今なら、delaycompress の重要性が深くわかるはずです。

圧縮のタイミング問題

compress オプションは、ローテーション直後に古いファイルを圧縮します。
しかし、create モードでシグナルを送っても、アプリが実際にファイルを切り替えるまでには数ミリ秒〜数秒のタイムラグがあります(処理中のリクエストが終わるのを待つためなど)。

その「切り替え待ち」の間に、logrotateが古いファイルを gzip してしまうとどうなるか?
アプリは「テキスト」を書き込みたいのに、ファイルは「gzip形式」に変わってしまっています。
結果、ファイルが破損したり、書き込みエラーになったりします。

delaycompress の役割

compress
delaycompress

これを指定すると、「最新の過去ログ(.1)」は圧縮せず、次のローテーションで「.2」になった時に初めて圧縮します。
これにより、アプリが古いファイルを掴んでいる期間は圧縮を回避でき、安全に切り替えが完了する時間を稼ぐことができます。

結論:compress を使うなら、脳死で delaycompress もセットで書くのが安全です。


まとめ:仕組みを知れば、恐るるに足らず

お疲れ様でした!
今回は少しディープなLinuxの内部挙動に踏み込みましたが、ここを理解しているかどうかが、プロのサーバー管理者への分かれ道です。

今回の重要ポイント:

  • Linuxはファイル名ではなく「iノード」でファイルを管理している。
  • create モードは、リロードシグナルを送らないと書き込み先が変わらない。
  • copytruncate モードは、アプリを選ばないが、ログ消失と高負荷のリスクがある。
  • lsof コマンドを使えば、アプリがどのファイルを掴んでいるか一目瞭然。

次回、第4回は「ディスク容量を極限まで節約!圧縮設定と日付付与のテクニック」です。
今回学んだ compress / delaycompress のさらなる詳細や、管理しやすさを劇的に向上させる dateext のカスタマイズ方法など、より実践的な設定テクニックを掘り下げます。
ディスク容量不足のアラートとおさらばしましょう!お楽しみに!

▼ エンジニアとしての「深み」を増すなら ▼

ファイルシステムを実験
「VPS」で自分専用環境

おすすめVPSを見る

OS内部の知識を年収に
「ITエンジニア転職」

転職エージェントを見る

コメント