Linuxで「ディスクフル」エラーを解決してディスク容量を管理する方法

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

深夜2時にディスクが満杯になったとき

真夜中にアラートが届く。Webアプリケーションが500エラーを吐いている。SSHで接続すると、最初に目に飛び込んでくるのは No space left on device。データベースは書き込めない。Nginxは一時ファイルを作れない。何もかもが壊れている――暴走したログプロセスか、あるいは自分自身が、ディスクを埋め尽くしてしまったのだ。

私も経験した――午前3時、指を走らせながら、存在すら知らなかったプロセスに悪態をつく。3年間、十数台のVPSインスタンスを経た今では、新しいサーバーを構築したら他の何よりも先にディスク監視を設定するようにしている。しかし、すでにアラートが鳴り響いている状況では、実際に効果がある対処法を紹介しよう。

ディスクが埋まる原因を理解する

rm -rf で手当たり次第に削除する前に、Linuxサーバーで実際に何がディスク容量を消費しているのかを把握しておくと役に立つ。よくある犯人たちを挙げると:

  • ログファイル — アプリケーションログ、システムログ、ジャーナルファイルが際限なく肥大化する
  • Docker — 未使用のイメージ、停止済みコンテナ、孤立したボリュームが静かに蓄積される
  • パッケージキャッシュ — apt/yumがアップデート後に古いパッケージをキャッシュし続ける
  • 一時ファイル/tmp/var/tmp に書き込んだまま後処理をしないアプリがある
  • データベースダンプとバックアップ — 誰も削除していない古いバックアップファイル
  • コアダンプ — クラッシュしたプロセスがギガバイト単位のメモリをディスクに書き出すことがある

よくある落とし穴として覚えておきたいのは、df でディスクが満杯と表示されるのに du では該当ファイルが見つからないケースだ。これは通常、削除済みのファイルを実行中のプロセスがまだ開いているために起きる――inodeは生き続け、容量も消費されたままになる。そのプロセスがファイルハンドルを閉じるまで解放されない。よくある罠だ。

知っておくべき基本コマンド

全体的なディスク使用量を確認する

# 人間が読みやすい形式でディスク使用量を表示
df -h

# 特定のファイルシステムに絞り込む
df -h /var

容量を圧迫している原因を探す

# /var のトップレベルのディスク使用量をサイズ順に表示
du -sh /var/* 2>/dev/null | sort -rh | head -20

# さらに詳しく調べる
du -sh /var/log/* 2>/dev/null | sort -rh | head -10

sort -rh フラグを使うと、人間が読みやすいサイズを正しくソートできる――1.2G は 900M より上に表示される。-h を外すと並び順がおかしくなり、間違ったディレクトリを追いかけながらディスクは満杯のままになってしまう。

大きなファイルを直接探す

# システム上で100MBを超えるファイルを検索
find / -xdev -type f -size +100M 2>/dev/null | sort -k5 -rn

# サイズ付きで見やすく表示
find / -xdev -type f -size +100M -exec du -sh {} \; 2>/dev/null | sort -rh

-xdev フラグは検索を現在のファイルシステム内に限定し、マウントポイントをまたがない――/ に留まってNFSマウントを誤ってたどらないようにしたい場合に便利だ。

実践:素早くディスク容量を回復する

1. ジャーナルログをクリーンアップする

systemdベースのディストリビューション(Ubuntu、Debian、CentOS 7+、AlmaLinux)では、負荷の高いサーバーでjournaldが静かに2〜5GBに達することがある。ディスクがなくなって初めて気づくことが多い。

# ジャーナルが使用しているディスク量を確認
journalctl --disk-usage

# 最新の200MB分のログのみ保持
journalctl --vacuum-size=200M

# または過去2週間分のログのみ保持
journalctl --vacuum-time=2weeks

/etc/systemd/journald.conf で上限を永続的に設定する:

SystemMaxUse=500M
SystemKeepFree=1G

その後、journaldを再起動する:systemctl restart systemd-journald

2. アクティブなログファイルをトランケートする(削除ではなく)

プロセスがログファイルを開いている場合、削除しても容量は解放されない――inodeは生き続けるからだ。トランケートなら効果がある:

# アクティブに書き込まれているログファイルを安全にトランケートする
> /var/log/nginx/access.log

# truncateコマンドを使った同等の操作
truncate -s 0 /var/log/nginx/error.log

削除済みだがまだ開いているファイルの問題には、ハンドルを持つプロセスを探す:

# 削除済みだがまだ開いているファイルを持つプロセスを探す
lsof | grep '(deleted)'

そのプロセスを再起動すればハンドルが解放され、実際に容量が解放される。

3. Dockerの不要データをクリーンアップする

Dockerを実行しているサーバーでは、これが最大の効果をもたらすことが多い:

# Dockerが使用しているディスク量を確認
docker system df

# 停止済みコンテナ、未使用ネットワーク、未使用イメージ、ビルドキャッシュを削除
docker system prune

# 未使用ボリュームも削除(注意:事前に必ず確認すること)
docker system prune --volumes

# 未使用(dangling)イメージのみ削除
docker image prune

# どのコンテナにも使用されていないイメージをすべて削除
docker image prune -a

数ヶ月クリーンアップしていなかったビルドサーバーで docker system prune -a を一度実行して、20〜40GBを回収したことがある。削除しようとしているイメージをアクティブなコンテナが使用していないことを必ず確認してから実行しよう。

4. パッケージマネージャーのキャッシュをクリーンアップする

# Debian/Ubuntu
apt clean          # キャッシュされた .deb パッケージを削除
apt autoremove     # 孤立したパッケージを削除

# RHEL/CentOS/AlmaLinux
yum clean all
dnf clean all

最近アップデートしたUbuntuサーバーでは、apt clean だけで500MB〜1GBを回収できることがある。劇的ではないが、空き容量は空き容量だ――しかも2秒で終わる。

5. コアダンプを探して処理する

# コアダンプを確認
ls -lh /var/lib/systemd/coredump/

# 削除する
coredumpctl list
rm /var/lib/systemd/coredump/*

# /etc/security/limits.conf でコアダンプを完全に無効化することもできる
# * hard core 0

6. inode使用量を確認する

df -h では十分な空き容量があるのに、書き込みが失敗することがある。その原因はバイト数ではなく、inodeであることが多い:

df -i

# ファイルシステムごとのinode使用率を表示
# Use% が100%の場合、inodeを使い果たしている

膨大な数の小さなファイルが、ディスクのバイトが尽きる前にinodeテーブルを枯渇させることがある。メールキュー、PHPセッションファイル、アプリケーションキャッシュがよくある原因だ。どのディレクトリが原因か調べるには:

# ファイル数が最も多いディレクトリを探す
for i in /var/*; do echo $(find $i -type f 2>/dev/null | wc -l) $i; done | sort -rn | head -10

早期に検知するためのモニタリング設定

ディスクフルのインシデントから得られる本当の教訓は「どう修復するか」ではなく「なぜもっと早く気づかなかったのか」だ。80%でアラートを送る簡単なcronジョブを設定するのに10分もかからない。それだけで、まさにこういった状況を防げる:

#!/bin/bash
# /usr/local/bin/disk-check.sh
THRESHOLD=80
USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')

if [ "$USAGE" -ge "$THRESHOLD" ]; then
  echo "警告: $(hostname) のディスク使用率が ${USAGE}% に達しました" | \
    mail -s "ディスク警告: $(hostname)" [email protected]
fi
# 毎時チェックするためにcrontabに追加
crontab -e
# 追加: 0 * * * * /usr/local/bin/disk-check.sh

複数のサーバーを管理する場合は、Netdata、Prometheus + Grafana、またはZabbixエージェントがスケールに対応したディスク監視を提供する。VPSが1台だけなら?上記のcronスクリプトで十分だ。さっさと設定しよう。

緊急時のチェックリスト

アラートが鳴り響いていて今すぐ容量が必要なとき、この順番で実行しよう:

  1. df -h を実行して、どのファイルシステムが満杯かを確認する
  2. du -sh /var/* | sort -rh | head -20 を実行して、最も大きなディレクトリを特定する
  3. 削除済みだがまだ開いているファイルを確認する:lsof | grep deleted
  4. 最も大きなアクティブなログファイルをトランケートまたはローテートする
  5. journalctl --vacuum-size=200M を実行する
  6. Dockerが存在する場合:docker system prune を実行する
  7. apt clean または dnf clean all を実行する
  8. inode使用量を確認する:df -i

手順2〜5だけで、通常はシステムを安定させるのに十分な容量を回収できる。その後で落ち着いて根本原因を調査し、きちんと修正しよう。

次のインシデントを防ぐために

私が経験してきたディスクフルの危機はすべて同じパターンをたどっていた:何かが何週間も野放しで成長し続け、誰も気づかず、ある瞬間に一気にすべてが壊れる。一度きりのクリーンアップでは解決しない。永続的な変更が必要だ:

  • すべてのアプリケーションログに logrotate を設定する――/etc/logrotate.d/ で既存の設定を確認し、不足しているものを追加する
  • journaldの設定で SystemMaxUse を設定する
  • ビルドサーバー・CIサーバーでは、cronで週1回 docker system prune を実行する
  • 監視スタックにディスク使用量を追加する――どんなツールを使っていても、80%でアラートが届くようにしておく
  • バックアップの保持ポリシーを見直す――アプリケーションと同じディスクに古いバックアップが置かれている状態は、いつか大惨事になる

ディスク容量管理は地味な作業だ。しかし、今日30分かけて設定しておけば、6ヶ月後の深夜2時の消火活動を防げる。これは本当だ。

Share: