0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Dockerホストのディスクを溢れさせないための定期メンテナンス手順

0
Posted at

はじめに

Dockerを長く運用していると、ある日突然 no space left on device でコンテナが起動しなくなる、ということがよく起きます。
主な原因は次のとおりです。

  • 使われなくなった古いイメージ
  • ビルドキャッシュ(BuildKit)
  • 停止したままのコンテナ
  • ローテーションされていないコンテナログ(json-file)
  • 使われていないボリューム

この記事では、Dockerホストのディスク容量を保つために必要な設定と、定期メンテナンスの内容、自動化の方法をまとめます。

動作確認環境の想定: Linux(systemd採用のディストリビューション)、Docker Engine 23.0以降、ストレージドライバ overlay2

1. 現状を把握する

まずは何が容量を使っているかを確認します。

ホスト全体

df -h
df -h /var/lib/docker
df -i /var/lib/docker   # inode枯渇もあわせて確認

overlay2は小さなファイルを大量に作るため、容量に余裕があってもinodeが先に枯渇するケースがあります。df -i も必ず見ておきます。

Dockerの内訳

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          42        8         18.5GB    14.2GB (76%)
Containers      12        8         1.1GB     230MB (20%)
Local Volumes   15        6         9.8GB     3.4GB (34%)
Build Cache     230       0         6.7GB     6.7GB

RECLAIMABLE が削除によって回収できる容量の目安です。詳細は -v で確認できます。

docker system df -v

コンテナログのサイズ

docker system df にはコンテナログのサイズが含まれないので、別途確認します。

sudo sh -c 'du -h /var/lib/docker/containers/*/*-json.log' | sort -h | tail -n 10

ファイル名のIDとコンテナ名の対応は次で確認できます。

docker ps -a --no-trunc --format '{{.ID}} {{.Names}}'

2. 先にやっておくべき恒久対策

定期削除の前に、「そもそも溜まりにくくする」設定を入れておきます。

2-1. コンテナログのローテーション

デフォルトのログドライバ json-file は サイズ上限なし です。これがディスク逼迫の原因の筆頭になります。

/etc/docker/daemon.json

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

この例ではコンテナ1つあたり最大30MB程度に抑えられます。
ログ収集基盤(Fluentdなど)でjsonファイルを直接読んでいない場合は、圧縮とローテーションが組み込まれている local ドライバも選択肢です。

daemon.jsonのログ設定は 新しく作成したコンテナにのみ 適用されます。
既存コンテナに反映するには、systemctl restart docker の後にコンテナの再作成(docker compose up -d --force-recreate など)が必要です。

Docker Composeでサービスごとに指定することもできます。

services:
  app:
    image: example/app:latest
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

2-2. ビルドキャッシュの自動GC

ビルドを行うホストでは、BuildKitのガベージコレクションを有効にしておきます。

{
  "builder": {
    "gc": {
      "enabled": true,
      "defaultKeepStorage": "20GB"
    }
  }
}

builder.gc 配下で指定できるキーはDockerのバージョンによって差があります。利用中のバージョンの公式ドキュメントを確認してください。

2-3. journaldの上限設定

Docker自体ではありませんが、ホストのログも地味に容量を食います。

/etc/systemd/journald.conf

[Journal]
SystemMaxUse=1G
sudo systemctl restart systemd-journald

2-4. data-rootを専用ディスクに分ける(設計段階で可能なら)

/var/lib/docker がルートファイルシステムと同居していると、Dockerの肥大化がOS全体を止めてしまいます。
新規構築時であれば、専用ディスクをマウントして data-root を指定しておくと被害を局所化できます。

{
  "data-root": "/data/docker"
}

3. 定期メンテナンスで実行するコマンド

3-1. 停止中のコンテナ

docker container prune -f --filter "until=168h"

until で「作成から一定時間経過したもの」に限定できます(168h = 7日)。

3-2. イメージ

# タグのない(dangling)イメージのみ
docker image prune -f

# どのコンテナからも参照されていないイメージを全て(7日以上前に作成されたもの)
docker image prune -a -f --filter "until=168h"

-a を付けると未使用イメージがすべて対象になります。停止中コンテナが参照しているイメージは消えません。
ただし、次回デプロイ時に再pullが発生する点には注意してください。

消したくないイメージがある場合は、ラベルで除外できます。

docker image prune -a -f --filter "until=168h" --filter "label!=keep=true"

3-3. ビルドキャッシュ

docker builder prune -f --filter "until=168h"

3-4. ネットワーク

容量への影響は小さいですが、ついでに掃除しておきます。

docker network prune -f --filter "until=168h"

3-5. ボリューム(自動化しない)

docker volume ls -f dangling=true
docker volume prune

ボリュームには データベースなどの永続データ が含まれます。定期ジョブでの自動削除は避け、中身を確認したうえで手動で実施してください。
Docker 23.0以降の docker volume prune はデフォルトで匿名ボリュームのみが対象です。名前付きボリュームも含める -a オプションは特に慎重に扱ってください。

同様の理由で、docker system prune -a --volumes を定期ジョブに入れるのはおすすめしません。

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

  • /var/lib/docker/overlay2 配下を rm で直接削除する
    → Dockerの管理情報と不整合を起こし、コンテナやイメージが壊れます。必ず docker コマンド経由で削除します。
  • 稼働中コンテナのログファイルを rm する
    → ファイルハンドルが開いたままのため容量が解放されません。緊急時は truncate で空にします(ログは失われます)。
sudo truncate -s 0 "$(docker inspect --format='{{.LogPath}}' <コンテナ名>)"

4. 自動化する(systemd timer)

cronでも構いませんが、実行履歴をjournalで追えるsystemd timerを使った例を示します。

4-1. クリーンアップスクリプト

/usr/local/sbin/docker-cleanup.sh

#!/usr/bin/env bash
set -euo pipefail

KEEP="168h"   # 保持期間

echo "=== docker cleanup start: $(date '+%F %T') ==="
df -h /var/lib/docker
docker system df

docker container prune -f --filter "until=${KEEP}"
docker image prune -a -f --filter "until=${KEEP}" --filter "label!=keep=true"
docker builder prune -f --filter "until=${KEEP}"
docker network prune -f --filter "until=${KEEP}"

echo "--- after ---"
docker system df
df -h /var/lib/docker
echo "=== docker cleanup end: $(date '+%F %T') ==="
sudo chmod 755 /usr/local/sbin/docker-cleanup.sh

4-2. service / timer

/etc/systemd/system/docker-cleanup.service

[Unit]
Description=Docker periodic cleanup
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/docker-cleanup.sh
Nice=10
IOSchedulingClass=idle

/etc/systemd/system/docker-cleanup.timer

[Unit]
Description=Run docker cleanup weekly

[Timer]
OnCalendar=Sun *-*-* 03:00:00
RandomizedDelaySec=30m
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now docker-cleanup.timer

# 確認
systemctl list-timers docker-cleanup.timer
# 手動実行
sudo systemctl start docker-cleanup.service
# 実行ログ
journalctl -u docker-cleanup.service -n 100

Persistent=true にしておくと、ホスト停止中に予定時刻を過ぎた場合でも起動後に実行されます。

5. 監視(しきい値アラート)

定期削除だけでは急増に対応できないため、使用率の監視も入れておきます。
Zabbix・Prometheusなどの監視基盤がある場合はそちらを使い、ない場合は簡易スクリプトでも最低限の検知ができます。

/usr/local/sbin/disk-check.sh

#!/usr/bin/env bash
set -euo pipefail

TARGET="/var/lib/docker"
WARN=80   # %

usage=$(df --output=pcent "$TARGET" | tail -n 1 | tr -dc '0-9')
iusage=$(df --output=ipcent "$TARGET" | tail -n 1 | tr -dc '0-9')

if [ "$usage" -ge "$WARN" ] || [ "$iusage" -ge "$WARN" ]; then
  msg="[WARN] $(hostname) ${TARGET} disk=${usage}% inode=${iusage}%"
  logger -t disk-check "$msg"
  # 通知先に合わせて変更(Slack Webhookの例)
  # curl -s -X POST -H 'Content-type: application/json' \
  #   --data "{\"text\":\"${msg}\"}" "$SLACK_WEBHOOK_URL"
fi

これを1時間ごとなどのtimerで回しておきます。

6. 運用サイクルのまとめ

頻度 作業 方法
常時 / 1時間ごと ディスク・inode使用率の監視 監視ツール or disk-check.sh
毎週 停止コンテナ・未使用イメージ・ビルドキャッシュ・ネットワークの削除 docker-cleanup.timer
毎月 docker system df -v とログサイズの確認、未使用ボリュームの棚卸し 手動
構築時 / 設定変更時 ログローテーション、BuildKit GC、journald上限、data-root分離 daemon.json など

おわりに

Dockerホストのディスク対策は、次の3段構えにしておくと安定します。

  1. 溜まりにくくする設定(ログローテーション、BuildKit GC)
  2. 定期的な削除(prune系コマンドの自動実行)
  3. 早めの検知(使用率とinodeの監視)

特にコンテナログの上限設定は忘れられがちで、効果も大きいので、まだの方はまずここから入れてみてください。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?