dfは空きありなのに容量不足 — 削除済みファイル占有とinode枯渇の切り分け

dfは空きありなのに容量不足 — 削除済みファイル占有とinode枯渇の切り分け

この記事で解決できること

  • df には空きがあるのに No space left on device が出る理由が分かる
  • df は満杯なのに du の合計と合わない(削除済みファイル占有)を特定できる
  • lsofdf -i2 方向の乖離 を切り分け、安全に空きを回収できる

結論(最短)

  • df -h 満杯だが du は正常 → 削除済みファイルをプロセスが開いたままlsof +L1 で特定し、サービス再起動 or fd 切り詰めで回収
  • df -h に空きがあるのに書けない → inode 枯渇df -iIUse% 確認)か 予約ブロック(非 root 書き込み不可)

前提(対象環境)

  • OS:Ubuntu / Debian 系(ext4 想定、tune2fs 等)
  • シェル:bash
  • 権限:sudo が使える想定

なぜ df と実際の空き容量がズレるのか?

結論: df はファイルシステムのスーパーブロックが持つ集計値を読むだけで、開いたままの削除済み inode や inode テーブルの枯渇は容量(%)に現れないため乖離が起きる。

df はディレクトリを走査せず、ファイルシステムが保持する集計値(空きブロック数・空き inode 数)を読む。一方 du は実在するパスを辿ってサイズを合算する。この実装差が乖離の正体で、典型は次の 3 パターン。

症状 真因 確認コマンド
df 満杯・du 少ない 削除済みファイルをプロセスが open 中 lsof +L1
df に空き・書けない inode 枯渇 df -i
df に約 5% 空き・非 root が書けない 予約ブロック tune2fs -l

キーワードは 「容量(ブロック)」と「inode」は別カウンタ という点。どちらか一方でも尽きれば No space left on device になる。

削除済みファイルがディスクを占有しているか確認するには?

結論: lsof +L1 でリンク数 0(削除済み)かつ open 中のファイルを一覧でき、SIZE 列が大きいものが df と du の差を生んでいる正体。

ファイルは rm してもプロセスが開いている間は inode が解放されず、ブロックも回収されない。ログを rm したのにサービスが書き続けているケースが典型。

$ sudo lsof +L1

+L1 は「リンク数が 1 未満(= 0、削除済み)」のオープンファイルだけを表示する。NLINK 列が 0、SIZE が大きい行が原因。

deleted 文字列で探す方法も併用できる:

$ sudo lsof -nP / 2>/dev/null | grep '(deleted)'
COMMAND   PID  USER   FD   TYPE DEVICE     SIZE/OFF NODE NAME
nginx    1234  root    5w  REG  259,1   8589934592  131 /var/log/nginx/access.log (deleted)

この例では nginx が 8 GB の削除済みログを開いたまま握っている。df は満杯、du /var/log は小さい、という状態になる。

PID と FD(上記なら PID 1234 / FD 5)を控えておく。次の解放手順で使う。

占有を解放するには?

結論: 原則は該当プロセスの再起動(または reload)。即時に空きが要る場合は /proc/PID/fd/N をリダイレクトで 0 バイトに切り詰めれば再起動なしで回収できる。

方法 A:プロセスを再起動 / reload(推奨)

開いている fd が閉じれば inode が解放され、ブロックが即座に戻る。

$ sudo systemctl restart nginx

ログ系なら full restart でなく reload で開き直すサービスも多い(systemctl reload nginx)。

方法 B:再起動できないとき fd を切り詰める

サービスを落とせない場合、開いたままのファイル実体を /proc/<PID>/fd/<FD> 経由で 0 バイトに切り詰める。

$ sudo truncate -s 0 /proc/1234/fd/5

: > /proc/1234/fd/5 でも同じ。データは失われるがプロセスは生きたまま、容量が即時回収される。

inode 枯渇を確認するには?

結論: df -i で IUse% が 100% なら容量が残っていても書けない。小さいファイルが大量にあるディレクトリを find で特定して削減する。

df -h に空きがあるのに書けないなら inode を疑う。

$ df -i
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/sda1      6553600 6553600     0  100% /

IUse% が 100% なら inode 枯渇。小さいファイルが大量にある場所を探す:

$ sudo find / -xdev -type f 2>/dev/null | cut -d/ -f1-3 | sort | uniq -c | sort -n | tail

セッションファイル・メールキュー・キャッシュの断片(/var/lib/php/sessions/var/spool、大量の小ファイル)が候補。詳しい削減手順は inode 枯渇の対処 を参照。

df に約 5% の空きがあるのに書けないのはなぜ?

結論: ext4 は既定で 5% を root 専用の予約ブロックとして確保するため、非 root プロセスは df に空きが見えても書き込めない。tune2fs で予約率を確認・調整する。

ext4 は既定で全体の 5% を root 用に予約する(システム停止回避のため)。非 root のアプリは df に空きが見えても ENOSPC になる。

$ sudo tune2fs -l /dev/sda1 | grep -i 'reserved block'
Reserved block count:     6553600

データ専用パーティションなら予約率を下げて回収できる(root 領域では下げすぎ注意):

$ sudo tune2fs -m 1 /dev/sda1

-m 1 で予約を 1% に変更。/ では既定の 5% 維持を推奨。

再発を防ぐには?

結論: ログは logrotate の copytruncate か正しい reload 連携で開き直し、inode はファイル数を監視する。df だけでなく df -i と lsof +L1 を定期確認に組み込む。

  • ログ肥大logrotatecopytruncate、またはローテーション後に該当サービスへ正しくシグナルを送る設定にする(rm だけは禁物)
  • inode 監視df -i を監視対象に追加。容量(%)だけ見ていると inode 枯渇を見逃す
  • 削除前確認:大きなファイルを消す前に lsof <file> で誰が開いているか確認

切り分けの定石は「df -h で容量、df -i で inode、lsof +L1 で削除済み占有」の 3 点を必ずセットで見ること。

次に読む