ファイル名を変えたのに、なぜ書き込み続けるのか?
こんにちは!「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回】サーバーの「時限爆弾」を解除せよ!ログローテーションの基礎と仕組み完全図解
- 【第2回】設定ファイルの魔術書!ローテーション周期・サイズ・保存期間の完全制御
- 【第3回】「ログが消えた!?」を防ぐ。copytruncateとcreateの決定的な違いとiノードの秘密
- 【第4回】ディスク容量を極限まで節約!圧縮設定と日付付与のテクニック
- 【第5回】再起動を自動化せよ!postrotateスクリプトによるプロセス制御
- 【第6回】権限トラブルを回避する!ユーザー指定とACL、出力先ディレクトリの管理
- 【第7回】動かない時の処方箋。デバッグモード活用とSELinux/ステータスファイルの罠
- 【第8回】最強のバックアップ構築。S3への自動転送と一元管理への応用
第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など、一般的なミドルウェアでよく使われます。
処理の流れ
- リネーム:
access.logをaccess.log.1に名前変更します。
(この時点では、アプリはまだaccess.log.1に書き込み続けています) - 新規作成: 空の新しい
access.logを作成します。
(まだアプリはこのファイルの存在を知りません) - 通知(重要): logrotateがアプリに対して「ログファイルを掴み直せ(Reopen)」という合図(シグナル)を送ります。
- 切り替え: シグナルを受け取ったアプリは、現在のファイルを閉じて、新しい
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」とは「切り詰める(サイズをゼロにする)」という意味です。
処理の流れ
- コピー: 現在の
access.logの中身を、access.log.1にコピーします。
(リネームではなくコピーなので、元のファイルは残っています) - 切り詰め (Truncate): 元の
access.logのファイルサイズを0バイト(空)にします。
(プログラムはずっと同じファイルを掴んでいるので、そのまま書き込みを続けられます)
iノードが変わらない魔法
この方式の最大の特徴は、「iノード番号が変わらない」ことです。
ファイルの中身だけを抜き取って空にするため、アプリケーション側は何も気づかずに、同じファイルハンドルに対して書き込みを継続できます。
したがって、再起動やシグナル送信は不要です。
致命的な弱点:ログ消失のリスク
copytruncateは便利ですが、構造上の欠陥があります。
それは、「コピーが終わってから、空にするまでの一瞬の隙」です。
- logrotateがデータをコピー完了。
- (ここでアプリが新しいログ行を書き込む!)
- 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」で自分専用環境
OS内部の知識を年収に
「ITエンジニア転職」

コメント