問題の発生
システム管理者にとって古典的な悩みの一つです。監視ツールからディスク容量が100%に達したというアラートが届きます。巨大な80GBのログファイルを見つけ、rmで削除し、アラートが消えるのを待ちます。しかし、消えません。ファイルはなくなったはずなのに、df -hは依然としてディスクが一杯だと主張し、一方でdu -shは十分な空き容量があることを示します。
この不一致は、Linuxが他のオペレーティングシステムとは異なる方法でファイルを処理するために発生します。プロセスがファイルへの書き込みを行っている最中にそのファイルを削除しても、領域はすぐには解放されません。その結果、ディレクトリ一覧には表示されないものの、容量を消費し続ける「ゴースト」ファイルが発生します。
TL;DR: クイック解決策
再起動せずに今すぐ領域を回収する必要がある場合:
- 削除されたファイルを保持しているプロセスを特定する:
sudo lsof +L1 - リストに表示されたサービスを再起動する。
- サービスを停止できない場合は、ファイル記述子を切り詰める(truncate):
: > /proc/PID/fd/FD_NUMBER
なぜ領域がロックされたままなのか
Linuxファイルシステムでは、以下の2つの条件が満たされたときにのみ、ファイルが完全に消去されます。
- リンクカウントがゼロになる(そのデータを指すファイル名がなくなる)。
- アクティブなプロセスが、そのファイルを指すオープンなファイル記述子(file descriptor)を持っていない。
rmを実行すると、リンクは削除されますが、必ずしもファイルが閉じられるわけではありません。Nginxやデータベース、あるいは自作のJavaアプリケーションがそのファイルを開いたままにしている場合、カーネルはデータブロックを予約したまま保持します。dfはファイルシステムの合計使用ブロック数を確認するため、その80GBは依然として使用中として認識されます。
トラブルシューティングのステップ
1. 不一致の確認
まず、ファイルシステムとディレクトリツリーで使用状況が食い違っていることを確認します。以下を実行してください:
df -h
次に、仮想ファイルシステムを除外して、既存ファイルの実際のサイズを確認します:
sudo du -sh / --exclude=/proc --exclude=/sys --exclude=/dev
もしdfが100GB使用中と報告しているのに、duでは20GBしか見つからない場合、削除されたデータを保持し続けているオープンなファイル記述子が存在するのは確実です。
2. 「ゴースト」ファイルの特定
ここではlsof(List Open Files)ユーティリティが非常に役立ちます。+L1フラグを使用して、リンクカウントが1未満のファイル(削除済みファイル)を特定します:
sudo lsof +L1
SIZE列に注目して、容量を大きく占有しているものを探します。出力は通常以下のようになります:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
nginx 4567 root 3w REG 253,1 80GB 0 99887 /var/log/nginx/access.log (削除済み)
この実例では、Nginx(PID 4567)がファイル記述子3で80GBのファイルを開いたままにしています。
3. 標準的な解決策:サービスの再起動
最も確実な方法は、アプリケーションを再起動することです。これにより、すべてのファイル記述子が強制的に閉じられ、カーネルがようやくディスクブロックを解放できるようになります。
sudo systemctl restart nginx
プロセスがスタンドアロンのスクリプトである場合は、手動で終了させる必要があるかもしれません:
sudo kill -15 4567
4. プロの解決策:ダウンタイムなしで切り詰める
本番環境のデータベースやトラフィックの多いWebサーバーなど、サービスの再起動が許されない場合があります。その場合、プロセスを停止させずに/procファイルシステム経由でファイルを「空」にすることができます。
前のステップで確認したPID(4567)とFD(3)を使用して、以下を実行します:
sudo bash -c ': > /proc/4567/fd/3'
このコマンドは、特定のファイル記述子にNULLを送信します。これにより、ディスク上のファイルサイズが即座に0バイトに縮小されます。プロセスはアクティブなままでファイル記述子も有効ですが、ディスク容量はすぐに戻ります。
再発を防ぐ方法
稼働中のログを手動で削除することが、この問題の主な原因です。ディスクの状態を健全に保つために、以下の慣行に従ってください:
- logrotateの設定: すべてのログが
logrotateで管理されていることを確認してください。これはSIGHUPなどのシグナルを使用して、アプリに古いログを閉じさせ、新しいログを安全に開始させます。 - 稼働中のログを絶対に 'rm' しない: ログファイルを手動でクリアする必要がある場合は、ファイルを削除せず、代わりに内容を切り詰めてください:
/var/log/heavy-app.log
これにより、内容は消去されますがファイルハンドルは維持され、「ゴーストファイル」の問題を完全に防ぐことができます。
- **不一致の監視:** `df`と`du`の出力を比較するチェックを導入し、緊急事態になる前にこれらのリークを検知できるようにします。

