Linuxのdfとduの数字が合わない謎と格闘した日の話|削除済みなのにディスクが解放されない理由と現役講師が今も使う確認コマンド

HOMEリナックスマスター.JP 公式ブログLinux学習ガイド > Linuxのdfとduの数字が合わない謎と格闘した日の話|削除済みなのにディスクが解放されない理由と現役講師が今も使う確認コマンド
宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
「dfではディスクが99%なのに、duで調べても大きなファイルが見当たらない。ファイルを削除したはずなのに、なぜかディスク使用量が減らない。」

こんな状況に陥ったことはないだろうか。私がSE時代(2001年~2006年)に初めてこの問題に直面したのは、受け持ちの本番Webサーバーから深夜にディスクフル警告が届いた日だった。「どこかに巨大なファイルが溜まっているはずだ」とdu -shで片っ端から調べても原因が見つからず、3時間以上を費やす羽目になった苦い記憶がある。

この記事では、Linuxのdfコマンドとduコマンドの数字が食い違う仕組みと、「ファイルを削除したはずなのにディスクが解放されない」という問題の根本原因を、20年以上サーバーを運用してきた経験をもとに解説する。仕組みを理解すれば、次に同じ状況が起きても迷わず原因にたどり着けるようになる。

この記事のポイント

・ dfとduが合わない最大の原因は「削除済みでもプロセスが開き続けているファイル」
・ lsof +L1 で削除済み未解放ファイルを一覧できる
・ 解放するにはファイルを開いているプロセスを特定して再起動するのが正解
・ logrotate の postrotate 設定とセットにすることで再発を防げる


Linuxのdfとduの数字が合わない謎と格闘した日の話|削除済みなのにディスクが解放されない理由と現役講師が今も使う確認コマンド
「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜdfとduの数字は合わないのか

Linuxのdfコマンドはファイルシステム全体の使用量をカーネルから直接取得する。一方のduコマンドはディレクトリツリーを実際に歩いてファイルのサイズを積み上げていく。この2つは「見ている視点」が違うため、特定の状況下では大きくズレることがある。

その最大の原因が「削除済みファイルへのオープンファイルハンドル」だ。

Linuxではファイルを削除してもディレクトリエントリが消えるだけで、そのファイルの実データはディスク上にまだ残っている。正確に言うと、そのinode(ファイルの実体を指す管理情報)を誰かが開いている(ファイルハンドルを保持している)限り、カーネルはデータを解放しない。つまり、ls -laで見えなくなっても、プロセスがそのファイルを開いていればディスクを占有し続けるのだ。

duはディレクトリエントリを歩くため、既に削除されたファイルを数えない。しかしdfはカーネルの視点で「実際に使用中の領域」を返すため、解放されていないデータ分が計上される。この差がdfとduのズレとして現れる。

よくある発生パターンを整理しておく。
ログローテーション後にアプリが再起動されていない(最頻出): logrotateが古いログを削除してもアプリが旧ファイルのハンドルを保持し続ける
tmpファイルを手動削除した: そのファイルを開いたプロセスがまだ動いている
バックアップアーカイブを生成中に手動削除した: バックアッププロセスが書き込み中のため解放されない
DBのデータファイルを削除したがDB停止せず: MySQLやPostgreSQLが保持しているケース

inode枯渇による「空き容量があるのにファイルが作れない」問題とは別の現象だが、どちらも「dfの数字を見て焦る」という状況は同じだ。inode枯渇については Linuxでinodeが枯渇した日の話 も参照してほしい。

SE時代に「消したのに解放されない」に初めて遭遇した話

2003年夏、私が担当していた本番Webサーバー(当時はRed Hat Linux 7.3)から深夜にディスクフル(使用率95%超)の警告が届いた。

「どこかに巨大なファイルが溜まっているはず」と判断してfindコマンドで大きなファイルを探し、du -shで各ディレクトリを確認したが、目立った原因が見当たらない。/var/log/httpdが怪しいと思って確認したが、logrotateで前日に圧縮・削除済みだった。「消したのに…」と30分以上ハマっていた。

先輩エンジニアに相談した時に返ってきた一言が「lsofを使えよ」だった。

lsofでApacheのプロセスが開いているファイルを調べると、既にディレクトリ上には存在しないaccess_logを「(deleted)」の状態でまだ開いていることが分かった。logrotateで削除されたログファイルを、Apacheが再起動されないまま開き続けていたのだ。ファイルサイズは当時8GBを超えていた。Apacheをgracefulリスタートした瞬間にdfの数字が一気に下がり、警告が消えた。

「ファイルの削除とは何か」を本当の意味で理解したのは、あの夜が初めてだったと思う。それ以来、ディスクがフルに近づいた時はまずlsof +L1を確認することが私のルーティンになった。

セミナーで受講生からよく聞かれる質問のひとつが「dfとduの値が違うんですが、なぜですか?」だ。3,100名以上を指導してきた中で、この疑問を持つ人は珍しくない。逆に言えば、ここを理解できているエンジニアはディスク管理を本当に分かっている人だと思う。

原因を突き止める3ステップ

dfとduの数字が合わない時の調査手順を紹介する。私が現在も実務で使っている順番だ。

1. dfとduで差を数値化する

まずdfで全体の使用量を確認し、duで主要ディレクトリのサイズを積み上げる。dfのUsedとduの合計に数GB以上の差があれば、削除済み未解放ファイルを疑う。

# df -h でファイルシステムの使用量確認(人が読みやすい単位で表示) $ df -h Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 48G 2.0G 96% / tmpfs 7.8G 0 7.8G 0% /dev/shm # /var 以下のディレクトリ別サイズを確認 $ du -sh /var/* 1.2G /var/cache 4.3G /var/log 84M /var/spool 512M /var/www # du の合計は約6GB だが df では 48GB 使用中 → 約42GBの差がある # この差が「削除済み未解放ファイル」の可能性を示すサイン

この段階でduの合計がdfのUsedより大幅に少なければ、削除済みファイルが解放されていない可能性が高い。実際にどのプロセスが何を開いているかを次のステップで調べる。

2. lsof +L1 で削除済みファイルを一覧する

lsof(List Open Files)に+L1オプションを付けると、ハードリンク数が0(つまり削除済み)のファイルを開いているプロセスを一覧できる。

# lsof +L1 で削除済み未解放ファイルの一覧(sudo 権限が必要) $ sudo lsof +L1 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME httpd 2041 root 4u REG 8,1 8589934592 0 12345 /var/log/httpd/access_log (deleted) mysqld 1823 mysql 6u REG 8,1 3221225472 0 23456 /var/lib/mysql/tmp/sql_abc (deleted) # サイズが大きいもの順に並べて上位20件を確認 # 7列目(SIZE/OFF)を数値降順でソート $ sudo lsof +L1 | sort -k7 -rn | head -20

「(deleted)」と表示されているファイルが、削除済みなのに解放されていないファイルだ。SIZE/OFFの列が実際にディスクを占有しているサイズを示す。上の例ではhttpdが8GB、mysqldが3GBを占有したままになっている。

3. 対象プロセスを再起動して解放する

COMMAND列のプロセス名とPID列を確認し、そのサービスをreloadまたはrestartする。本番環境ではgraceful reload(Apacheなら systemctl reload httpd)を使い、接続中のリクエストを切らずに再起動するのが鉄則だ。

# systemctl reload httpd でgracefulリロード # (接続中セッションを維持したまま新しいファイルハンドルに切り替え) $ sudo systemctl reload httpd # リロード後に解放されたか lsof で確認 $ sudo lsof +L1 | grep httpd # 出力がなければ解放完了 # df -h で使用量が下がったことを目視確認 $ df -h Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 40G 10G 80% / # Used が 48G → 40G に下がり空き容量が回復した

「変更したら必ず確認する」のはLinuxの本番作業の鉄則だ。systemctlの基本的な使い方は chkconfig・systemctlでサービスを管理する方法 も参照してほしい。

トラブル事例と対処|「消したのに空かない」4パターン

セミナーで3,100名以上を指導してきた経験から、「dfとduが合わない」相談でよく見かける4パターンをまとめた。

パターン1: logrotate後にアプリが古いファイルを開いたまま(最頻出)

最も多いケースだ。logrotateがログを圧縮・削除しても、Apacheやnginx、Java製アプリが再起動されずに旧ファイルを書き続ける。ログが大きいほど(数GBになるケースも珍しくない)ディスクを長期間占有する。対処は対象サービスの systemctl reload。再発防止はlogrotateのpostrotateセクションにreloadコマンドを追加することだ。logrotateの正しい設定方法については Linuxのlogrotate設定を放置してディスクが溢れた日の話 も参照してほしい。

パターン2: MySQLやPostgreSQLの一時ファイル

大きなクエリや結合処理が/tmpや/var/lib/mysql/に一時ファイルを生成し、クエリ実行中に削除されると未解放ファイルが残る。lsof +L1でmysqldやpostgresのプロセスが一時ファイルを開いているケースを確認できる。DB側のサービス再起動で解放されるが、本番では停止計画が必要になる場合が多い。緊急度によっては夜間メンテナンス時間に合わせて対処することになる。

パターン3: Javaアプリのログ(log4j系)

Javaのwebアプリはlog4jなどのロギングライブラリが独自にファイルハンドルを保持するため、OSレベルのlogrotateだけでは古いファイルを手放さないことがある。アプリを再起動するかアプリ側のローリングポリシーを設定する必要がある。OSのlogrotateとアプリ側のログ設定を両方確認することが大切だ。

パターン4: コンテナ・VMイメージが予想外に溜まっている(duに映らない)

Dockerのbuildキャッシュや使われていないイメージ、KVMのスナップショットが/var/lib/docker/や/var/lib/libvirt/に溜まるケースだ。これはlsof +L1には映らないため、docker system dfdu -sh /var/lib/docker/*で個別確認が必要だ。コンテナ活用が増えている現場では、このパターンも増えている。

現役講師が実務で守るディスク管理の4習慣

20年以上のサーバー運用で習慣化したディスク管理の基本を共有する。これらは私が現場でやっていることであり、セミナー受講生にも繰り返し伝えていることだ。

習慣1: logrotate の postrotate に必ずサービス reload を入れる

httpd、nginx、mysqldなど主要サービスのlogrotate設定を作る時は必ずpostrotateセクションにreloadコマンドを仕込む。これだけで「消したのに空かない」の最頻出パターンがほぼなくなる。logrotateファイルを作ったらその場でpostrotateを確認するのが私の鉄則だ。

習慣2: 週1回 lsof +L1 | wc -l を定期確認する

行数が急増していたら何かが蓄積されているサインだ。cronで定期実行して、しきい値(たとえば100件超)を超えたらアラートを飛ばす仕組みを作っておくと安心だ。本番サーバーでは異変に気づくのが早いほど被害を最小化できる。

習慣3: ディスク削除後は必ず df -h で確認する

「消した→数字が減った」を目で確認してから作業を終える。感覚で「消えたはず」で完結させると、解放されていないケースを見逃す。Linuxの本番作業は「変更→確認」の2ステップが鉄則だ。私がセミナーでも何度も伝えている原則だが、忙しい時ほどこの確認を端折りたくなるので注意してほしい。

習慣4: df と du の「視点の違い」を常に意識する

dfは「ファイルシステム全体の使用量(カーネル視点)」、duは「存在するファイルの合計(ディレクトリ視点)」だ。どちらかだけを見て「問題ない」と判断せず、2つをセットで確認することで「なぜ違うのか」を必ず考える癖をつける。Linuxのディスク管理の基礎であるマウントとファイルシステムについては マウントとfstabの設定方法 も参照してほしい。

まとめ

「dfとduの数字が合わない」は、Linuxのファイルシステムの仕組みを知っていれば決して謎ではない。「ファイルを削除してもプロセスがファイルハンドルを保持している限りカーネルはデータを解放しない」という基本原理を理解することが出発点だ。

私がSE時代に3時間迷ったのは、この基本を知らなかったからだ。lsof +L1という1つのコマンドを知っていれば10分で解決できた問題だった。

次にディスクがフルに近づいた時は、まずlsof +L1で削除済み未解放ファイルを確認することをルーティンにしてほしい。

状況・やりたいこと コマンド・対処方法
ファイルシステムの使用量確認 df -h
ディレクトリ別サイズ確認 du -sh /var/*
削除済み未解放ファイルを一覧 sudo lsof +L1
lsof結果をサイズ降順で表示 sudo lsof +L1 | sort -k7 -rn | head -20
特定プロセスのオープンファイル確認 sudo lsof -p PID番号
httpdをgracefulリロード sudo systemctl reload httpd
logrotate後の未解放再発防止 logrotate設定にpostrotate + reload追加

「df 99%でパニック」から「3ステップで原因特定できる」へ、正しい型を最初から身につけませんか?

lsof +L1の使い方を知っていれば、私が3時間迷った問題を10分で解決できます。ネットの断片情報を拾い集めるより、現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、「Linuxサーバー構築入門マニュアル(図解60P)」を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。