inode 枯渇の対処 - 容量はあるのに書き込めない

inode 枯渇の対処 - 容量はあるのに書き込めない

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

  • df -h には空きがあるのに No space left on device で書き込めない症状の正体が分かる
  • df -i で inode 使用率を確認し、犯人ディレクトリを特定できる
  • 原因別(セッション・メール・Docker・ログ)の安全な削除と再発防止策が分かる

結論(最短 3 ステップ)

  1. df -iinode 使用率 100% のマウントポイント を特定
  2. 該当マウントポイントで find ... -xdev | cut ... | sort | uniq -c | sort -rn により ファイルが集中しているディレクトリ を絞り込む
  3. 原因に応じて削除(セッション・古いログ・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 newfileNo space left on device
  • df -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 -20
 432105 /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 -Ufind を使う

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/phpsessionclean を回しているか確認する。

3-2. Postfix メールキュー(/var/spool/postfix

外向きメール失敗 / バウンス停滞でキューが膨らむ。

$ sudo mailq | tail -1            # キュー件数
$ sudo postqueue -p | tail -1     # 同上
$ sudo postsuper -d ALL           # 全削除(影響大、確認後に実行)
$ sudo postsuper -d ALL deferred  # 配送遅延分のみ削除

3-3. systemd journal の細切れログ

$ journalctl --disk-usage
$ sudo journalctl --vacuum-time=7d   # 7 日より古いを削除
$ sudo journalctl --vacuum-size=200M # 200MB 以下に圧縮

/etc/systemd/journald.confSystemMaxFiles= を設定し再発防止。

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 -iIUse% を Prometheus node_exporter (node_filesystem_files, node_filesystem_files_free) などで取得し、80% 超過でアラートを上げる。

6. やってはいけないこと

次に読む