overlayfs によるディスク逼迫が起きる背景
overlayfs(Overlay Filesystem)は、Docker や containerd をはじめとするコンテナランタイムが採用しているユニオンファイルシステムです。イメージの各ビルドステップや pull 操作が「レイヤー」として /var/lib/docker/overlay2/(Docker の場合)または /var/lib/containerd/(containerd の場合)に蓄積されます。
コンテナを起動・停止・削除するたびに書き込み可能な差分レイヤーが生成され、明示的にクリーンアップしない限り残り続けます。CI/CD ワークフローでビルドを頻繁に行うノードや、多数のイメージを pull するワーカーノードでは、数ヶ月の運用だけで数十 GB から数百 GB のレイヤーが堆積することがあります。
overlayfs のレイヤーはファイルシステム上の通常ディレクトリとして見えるため、df -h でルートボリュームの使用率が高騰していても、原因がすぐには判断できません。まず「どのパスが膨らんでいるか」を特定するところから診断を始めます。
ディスク使用量の診断手順
ホスト全体のディスク使用状況と、コンテナランタイムが使用するパスを分けて確認します。
df -h /
du -sh /var/lib/docker/
du -sh /var/lib/containerd/
Docker 環境であれば、専用の診断コマンドが用意されています。
docker system df
docker system df -v
docker system df はイメージ・コンテナ・ボリューム・ビルドキャッシュのそれぞれが占めるサイズと、削除可能な「回収可能量(RECLAIMABLE)」を一覧表示します。-v を付けると個別オブジェクトの詳細が確認できます。overlay2 ディレクトリの内訳をさらに深掘りしたい場合は次のようにします。
du -sh /var/lib/docker/overlay2/* | sort -rh | head -20
ただし、これらのディレクトリ名(ハッシュ値)と実際のイメージ・コンテナの対応を手作業で追うのは困難です。後述する docker system prune や crictl を使ったアプローチが現実的です。
containerd を直接使用している Kubernetes ノードでは crictl を使います。
crictl images
crictl ps -a
診断の効率を上げるために、/var/log/ や /tmp/ など他のディレクトリの使用量も先に確認し、overlayfs 以外のディスク消費源がないかを除外しておくことを推奨します。
削除対象の見極め方
overlayfs 関連のディスク領域は大きく次のカテゴリに分かれます。
- 未使用イメージ(dangling image・参照されていないイメージ)
- 停止済みコンテナの差分レイヤー
- 未使用ボリューム
- BuildKit のビルドキャッシュ
<none>:<none> と表示される dangling イメージはビルド中間レイヤーが残ったものであり、ほぼ安全に削除できます。一方、タグ付きイメージでも実行中コンテナが参照していなければ削除対象になります。
本番環境で削除前に必ず確認すべき点は次のとおりです。
- 実行中・停止中コンテナが使用しているイメージは削除できない
- Kubernetes で
imagePullPolicy: IfNotPresentを設定している場合、ノード上のキャッシュイメージを消すと再起動時に pull が発生する - ボリュームは永続データを含む可能性があるため、最も慎重に扱う必要がある
docker image prune はデフォルトで dangling イメージのみを対象にします。docker image prune -a は参照されていないすべてのイメージを削除するため、本番ノードでは意図しないイメージ消失につながるリスクがあります。実行前に --filter や --dry-run(Docker 23.0 以降)を活用して対象を絞り込みます。
本番環境向けクリーンアップ設計
本番環境では「一括削除」ではなく、段階的かつ検証可能な設計が求められます。
ステップ1:ドライランで影響範囲を確認する
Docker 23.0 以降では --dry-run フラグが使用可能です。実際には何も削除せず、削除対象と回収見込みサイズを出力します。
docker system prune --dry-run
docker image prune -a --dry-run
ステップ2:影響の小さいものから段階的に削除する
停止済みコンテナ → ビルドキャッシュ → dangling イメージ の順に進めます。ボリュームは別途手動確認してから削除します。
# 停止済みコンテナの削除
docker container prune -f
# ビルドキャッシュの削除
docker builder prune -f
# dangling イメージの削除(-a は付けない)
docker image prune -f
ステップ3:containerd 直接利用環境(Kubernetes ノード等)
crictl rmi で個別削除もできますが、kubelet のガベージコレクション設定を先に調整する方が安全です。以下は想定例として示す設定値の例です。
# kubelet 設定(想定例)
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
この設定では、ディスク使用率が 85% を超えると kubelet が自動的に未使用イメージの削除を試み、80% を下回るまで継続します。手動操作に頼るよりも kubelet のライフサイクル管理に委ねる方が、本番ノードでの安全性が高まります。
切り戻し設計
クリーンアップ後に問題が発生した場合、削除したイメージは基本的に復元できません。あらかじめ重要なイメージを private registry にプッシュしておくか、削除前にイメージ ID を記録しておき、再 pull できる手順書を整備しておくことが本番運用の基本です。docker save でローカルにアーカイブを保存する方法も有効ですが、ディスク逼迫時は空き容量との兼ね合いを確認する必要があります。
再発防止とモニタリング設計
一時的なクリーンアップだけでは根本解決になりません。継続的に抑制する仕組みを組み込みます。
定期的な自動クリーンアップ
systemd timer または cron で docker system prune を定期実行します。本番ノードでは --filter until=168h などのフィルタを使い、直近1週間以内に参照されたリソースは残すよう設定します。以下は想定例として示す systemd サービスファイルの抜粋です。
# /etc/systemd/system/docker-cleanup.service(想定例)
[Service]
ExecStart=/usr/bin/docker system prune -f --filter "until=168h"
ディスク使用率アラート
Prometheus + node_exporter 環境では node_filesystem_avail_bytes を監視し、使用率 80% などの閾値でアラートを発報する設定を加えます。Alertmanager と組み合わせることで Slack 等への通知が可能です。ディスク逼迫は「突然 100% になる」のではなく、数週間かけて進行するケースが多いため、段階的な閾値(80%・90%)でのアラートが有効です。
イメージのライフサイクル設計
CI/CD パイプラインではビルドごとにタグを付けたイメージが registry に溜まりがちです。registry 側のライフサイクルポリシー(ECR の lifecycle policy、Harbor のタグ保持ルール等)とノード側の管理を合わせて設計することで、根本的な蓄積を抑制できます。ノード上に保持するイメージを「直近 N 世代」に絞る運用ルールを CI 設計の段階で決めておくことが重要です。
2026年時点の現行環境との差分
Docker Engine 25.x 以降の変化
Docker Engine 25.x 以降では BuildKit がデフォルト有効となり、ビルドキャッシュが /var/lib/docker/buildkit/ 配下に管理されます。docker builder prune を docker system prune と分けて管理する運用が一般的になりました。また --dry-run フラグの対応範囲が広がり、削除前の影響確認が容易になっています。
containerd 2.x の普及と snapshotter の変化
Kubernetes 1.30 以降のノードでは containerd 2.x が主流です。containerd 2.x では snapshotter の設計が見直され、overlayfs のスナップショット管理が改善されています。ctr snapshot ls や nerdctl system prune でスナップショット単位の確認・削除が可能になりました。Kubernetes 環境では crictl と nerdctl の使い分けを整理しておくと運用がスムーズです。
Rootless コンテナとストレージパスの分散
Rootless モードで Docker や Podman を使用している場合、ストレージパスは /home/<user>/.local/share/docker/ 等にユーザーごとに分散します。ホスト全体の df では気づきにくいため、ユーザーホームディレクトリの監視も合わせて設計に組み込む必要があります。また、fuse-overlayfs を使用している場合はネイティブ overlayfs と挙動が異なる部分があるため、使用しているスナップショッタを docker info や ctr plugins ls で事前に確認しておきます。
「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。
ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。
>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)
※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。
PR・広告
コマンド操作から基本運用までを手を動かして学べる定番書。記事で触れた技術の土台固めに。
※ Amazonのアソシエイトとして、当サイトは適格販売により収入を得ています。
