inode 枯渇の対処 - 容量はあるのに書き込めない
この記事で解決できること
df -hには空きがあるのにNo space left on deviceで書き込めない症状の正体が分かるdf -iで inode 使用率を確認し、犯人ディレクトリを特定できる- 原因別(セッション・メール・Docker・ログ)の安全な削除と再発防止策が分かる
結論(最短 3 ステップ)
df -iで inode 使用率 100% のマウントポイント を特定- 該当マウントポイントで
find ... -xdev | cut ... | sort | uniq -c | sort -rnにより ファイルが集中しているディレクトリ を絞り込む - 原因に応じて削除(セッション・古いログ・mail キュー・Docker 不要レイヤ)し、cron / logrotate で 再発を止める
前提(対象環境)
- OS:Ubuntu / Debian / RHEL 系(ext4 想定)
- 権限:
sudoが使える前提 - XFS / Btrfs / ZFS は通常 inode が動的のため、本記事の症状は基本発生しない
inode 枯渇とは?なぜ容量が余っていても書けないのか
inode 枯渇とは、ファイルシステムの メタデータ管理領域(inode テーブル)が満杯になり、データブロックに空きがあっても新しいファイルが作れない 状態。
ファイルシステムは 2 種類の上限を持つ。
- データブロック: ファイルの中身を格納する領域 →
df -hで見える - inode: 1 ファイル = 1 inode のメタデータ(権限・所有者・サイズ・ブロック位置等) →
df -iで見える
ext4 のような 静的 inode 割り当て のファイルシステムでは、mkfs 時に inode 総数が固定される(デフォルトは約 16KiB ごとに 1 inode)。小さいファイルが大量に作られると、データ容量より先に inode が枯渇 する。
典型症状
touch newfile→No space left on devicedf -h→ 使用率 60% で「空きあり」表示df -i→ 使用率 100%(IUse% = 100%)
1. df -i で inode 使用率を確認する
最初にやることは df -i。マウントポイントごとに inode 使用率が出る。
$ df -ih
Filesystem Inodes IUsed IFree IUse% Mounted on /dev/sda1 3.8M 3.8M 12K 100% / /dev/sdb1 6.4M 12K 6.4M 1% /data tmpfs 1.0M 5 1.0M 1% /run
IUse% が 100%(または 99%)の行が 犯人のマウントポイント。上記例では / が満杯。
df の -i オプションはほぼ全ディストリ標準(GNU coreutils)。POSIX 必須ではないが Linux では事実上常用可能。
2. inode を消費しているディレクトリを特定する
該当マウントポイントの直下からファイル数の多いディレクトリを掘り下げる。
2-1. 配下のファイル数を素早く数える
$ sudo find /var -xdev -printf . | wc -c
-xdev を必ず付ける(他のマウントポイントを掘らない ため)。-printf . は 1 ファイル 1 ドット出力で wc -c が高速。
2-2. どのサブディレクトリにファイルが多いかを集計
$ sudo find / -xdev -type f -printf '%h\n' 2>/dev/null \
| cut -d/ -f1-4 \
| sort \
| uniq -c \
| sort -rn \
| head -20432105 /var/lib/php 187220 /var/spool/postfix 54221 /var/log/journal 8021 /var/lib/docker ...
cut -d/ -f1-4 で 深さ 3 まで集約 して全体傾向を把握。怪しいパスが見えたら -f1-6 などに変えて再度掘り下げる。
2-3. 単一ディレクトリ内のファイル数(直下のみ)
$ ls -1U /var/lib/php/sessions | wc -l
ls -1U は ソートしない ため大量ファイルでも高速。wc -l で行数(≒ ファイル数)。
ls でソートすると数百万ファイルでメモリ不足になる場合がある。ls -U か find を使う。
3. 原因別の対処
3-1. PHP セッション(/var/lib/php/sessions)
PHP の session.gc_probability が低い・cron clean が止まっている場合に堆積する。
# まず件数と古さを確認 $ sudo find /var/lib/php/sessions -xdev -type f | head $ sudo find /var/lib/php/sessions -xdev -type f -mtime +7 | wc -l # 7 日以上前のセッションを安全削除(dry-run 後に -delete) $ sudo find /var/lib/php/sessions -xdev -type f -mtime +7 -print $ sudo find /var/lib/php/sessions -xdev -type f -mtime +7 -delete
Debian/Ubuntu では /etc/cron.d/php が sessionclean を回しているか確認する。
3-2. Postfix メールキュー(/var/spool/postfix)
外向きメール失敗 / バウンス停滞でキューが膨らむ。
$ sudo mailq | tail -1 # キュー件数 $ sudo postqueue -p | tail -1 # 同上 $ sudo postsuper -d ALL # 全削除(影響大、確認後に実行) $ sudo postsuper -d ALL deferred # 配送遅延分のみ削除
postsuper -d ALL は 未配送メールを全消去 する。業務メールが含まれていないか必ず確認する。
3-3. systemd journal の細切れログ
$ journalctl --disk-usage $ sudo journalctl --vacuum-time=7d # 7 日より古いを削除 $ sudo journalctl --vacuum-size=200M # 200MB 以下に圧縮
/etc/systemd/journald.conf に SystemMaxFiles= を設定し再発防止。
3-4. Docker の overlay2 レイヤ
$ docker system df $ docker system prune -a --volumes # 未使用イメージ・ボリュームを削除(影響確認後)
詳細は Docker ディスク使用量の特定 を参照。
3-5. ログローテーション失敗
$ sudo ls -lh /var/log | head $ sudo logrotate -d /etc/logrotate.conf # dry-run $ sudo logrotate -f /etc/logrotate.conf # 強制実行
4. 削除前の安全策
4-1. 必ず dry-run
# まず -print で対象を確認 $ sudo find /tmp -xdev -type f -mtime +30 -print # 内容を確認してから -delete $ sudo find /tmp -xdev -type f -mtime +30 -delete
4-2. オープン中のファイルを掴んでいないか
削除しても unlinked but still open だと領域が解放されないことがある。
$ sudo lsof +L1 | head # link count 0 のオープンファイル
該当プロセスを restart すると inode と容量が同時に解放される。
4-3. xargs の引数オーバーフロー回避
# NG: 引数が長すぎてエラーまたは部分実行
$ rm $(find /var/lib/php/sessions -type f)
# OK: -print0 / xargs -0 で安全
$ sudo find /var/lib/php/sessions -xdev -type f -mtime +7 -print0 \
| sudo xargs -0 -r rm --詳細は find で安全にファイル削除する方法 を参照。
5. 再発防止:inode を恒久的に増やしたい場合
5-1. 現在の inode 比を確認
$ sudo tune2fs -l /dev/sda1 | grep -E 'Inode count|Block count|Inode size'
5-2. ext4 は再フォーマットでのみ inode 数を増やせる
# データ退避必須。-i は バイト/inode 比、小さくすると inode が増える $ sudo mkfs.ext4 -i 8192 /dev/sdb1 # 約 8KB ごとに 1 inode $ sudo mkfs.ext4 -N 10000000 /dev/sdb1 # 総数指定
mkfs はディスク内容を全消去する。実行前にスナップショット / バックアップ必須。本番系では事前検証環境で必ず手順確認。
5-3. 動的 inode の XFS / Btrfs に移行
新規ボリュームを切れるなら、小ファイルが大量発生するワークロードでは XFS や Btrfs を選ぶ と inode 上限の心配がほぼ消える。
5-4. 監視を入れる
df -i の IUse% を Prometheus node_exporter (node_filesystem_files, node_filesystem_files_free) などで取得し、80% 超過でアラートを上げる。
6. やってはいけないこと
rm -rf /var/spool/postfixのようにディレクトリごと削除(Postfix が起動できなくなる)find / -deleteのような 無条件大量削除tune2fsで inode 数を変えようとする(できない)- 確認なしの
postsuper -d ALL(業務メール消失リスク) - 大量ファイル削除中の
Ctrl+C連打(中途半端な状態で残る)