Learning Platform
Глоссарий Troubleshooting
Урок 07.04 · 22 мин
Средний
dockerprunedisk-usagecleanupci

Prune и disk usage: когда диск забит build-кэшем

Самая частая проблема DE-разработчика после нескольких месяцев работы с Docker: no space left on device при build или pull. Виноваты обычно три категории мусора: dangling образы (без тегов), остановленные контейнеры с их writable layers, и — самое толстое — build cache от BuildKit. На активной dev-машине это может занимать 50-100GB.

В этом уроке: как диагностировать что забило диск, как чистить, и как настроить автоматическую очистку на CI.


Disk emergency: что делать когда диск 100%

docker system df: что занимает место

Первая команда при «диск забит»:

$ docker system df
TYPE            TOTAL     ACTIVE     SIZE      RECLAIMABLE
Images          47        12         8.234GB   5.123GB (62%)
Containers      15        2          234MB     189MB (80%)
Local Volumes   23        5          12.45GB   8.234GB (66%)
Build Cache     128       0          15.67GB   15.67GB (100%)

Колонки:

  • TYPE — категория артефактов.
  • TOTAL — общее количество объектов этого типа.
  • ACTIVE — сколько используется прямо сейчас (есть запущенный контейнер / volume замаунтен / образ имеет тег и используется).
  • SIZE — суммарный размер.
  • RECLAIMABLE — сколько можно высвободить через prune (то, что не active).

В примере: 5.1GB можно убрать из образов, 189MB из контейнеров, 8GB из volumes, 15.7GB из build cache. Итого ~29GB которые можно высвободить за две команды.

Подробнее по объектам:

$ docker system df --verbose

Images space usage:
REPOSITORY  TAG    IMAGE ID    CREATED        SIZE     SHARED SIZE  UNIQUE SIZE
python      3.13   abc...      2 weeks ago    145MB    0B           145MB
etl         v1     def...      1 hour ago     812MB    145MB        667MB
<none>      <none> 123abc      5 hours ago    400MB    0B           400MB   # dangling!
...

Containers space usage:
...

Local Volumes space usage:
VOLUME NAME             LINKS  SIZE
postgres_data           1      3.2GB
abandoned_volume_xyz    0      4.8GB           # никто не использует

Записи <none>:<none> это dangling images — образы, потерявшие тег (обычно от пересборки: docker build -t app:v1 . второй раз — старый image теряет тег, остаётся без имени). Volume с LINKS=0 — никем не используется.


docker image prune: удаление образов

Два режима:

# Удалить только dangling (без тегов)
docker image prune
# WARNING! This will remove all dangling images.
# Are you sure you want to continue? [y/N] y
# Deleted Images:
# untagged: <none>@sha256:abc...
# deleted: sha256:def...
# Total reclaimed space: 1.234GB

# Удалить ВСЕ не-используемые образы (без -a — только dangling)
docker image prune -a
# WARNING! This will remove all images without at least one container associated to them.
# ...
# Total reclaimed space: 8.234GB

-a агрессивно: удаляет все образы, на которые сейчас нет ссылающихся контейнеров. Если ты pull’ишь много разных образов для тестов и не запускаешь их 24/7 — будут удалены. С --filter "until=24h" можно ограничить «старше 24 часов».

# Удалить образы старше 7 дней без активных контейнеров
docker image prune -a --filter "until=168h"

docker container prune: остановленные контейнеры

Остановленные контейнеры (exited) занимают место своими writable layers:

$ docker ps -a -f "status=exited"
CONTAINER ID   IMAGE        COMMAND      CREATED       STATUS
abc123         alpine       sleep 60     2 days ago    Exited (137) 1 day ago
def456         python:3.13  python       1 week ago    Exited (0)  1 week ago

# Удалить все
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
abc123...
def456...
Total reclaimed space: 234MB

В CI и в продакшене обычно используют docker run --rm чтобы контейнеры самоудалялись после exit. Без --rm они оставляют writable layers.


docker volume prune: anonymous volumes

Anonymous volumes создаются автоматически в нескольких случаях:

  1. В образе есть VOLUME /data — Docker создаёт anonymous volume при docker run
  2. В compose не указан volume для named-volume — иногда создаётся anonymous
  3. Manual docker run -v /data (без имени)

Эти volumes часто никем не используются и забывается, но занимают диск.

$ docker volume ls
DRIVER    VOLUME NAME
local     postgres_data
local     a1b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789ab  # anonymous
local     0a1b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789ab  # anonymous

$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
# Будут удалены только volumes БЕЗ ссылающихся контейнеров (включая остановленные)

# Удалить все anonymous + named (более агрессивно)
docker volume prune -a
DANGER

docker volume prune удаляет данные БЕЗВОЗВРАТНО. Если в volume лежит база Postgres с production-данными dev-стенда — потеряешь её. Перед prune всегда проверяй docker volume ls и docker volume inspect. Для named volumes лучше удалять руками по имени: docker volume rm postgres_data.


docker builder prune: главный жирный кусок

BuildKit (default builder в Docker 23+) держит кэш сборок отдельно от images. Этот кэш растёт от каждого docker build, особенно если в Dockerfile используется RUN --mount=type=cache:

$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Total: 15.67GB
# Удалить ВЕСЬ build cache (включая используемый)
docker builder prune -a

# С фильтром
docker builder prune --filter "until=72h"   # старше 3 дней
docker builder prune --filter "type=regular,description=*pip*"

Если ты собираешь много образов, build cache это то, что нужно мониторить. На active dev-машине без очистки может занимать 50GB+ за месяц.


docker system prune: всё разом

docker system prune чистит образы + контейнеры + сети + build cache в одной команде:

# Безопасный режим: только dangling + stopped containers
docker system prune

# Агрессивный: + все не-используемые образы
docker system prune -a

# + volumes (НЕ default, потому что опасно)
docker system prune -a --volumes

# С фильтром по дате
docker system prune -a --filter "until=72h"

Это удобная «one-liner» команда для еженедельной чистки dev-машины.

Что чистит prune: иерархия команд
От узких к широким
container prunestopped containersУдаляет все остановленные контейнеры и их writable layers. Безопасно: running контейнеры не трогаются.
+
image prunedanglingБез -a: удаляет только dangling (без тегов). С -a: удаляет все без ссылающихся контейнеров.
builder prunebuild cacheКэш BuildKit, отдельный от images. На активной dev-машине часто самый большой кусок.
+
volume pruneorphan volumesVolumes без ссылающихся контейнеров. ОПАСНО: данные удаляются безвозвратно.
system prune -a --volumesALLУдаляет всё неиспользуемое. Полная очистка.

Cron-cleanup на CI

В CI каждый build добавляет образы и cache. Без очистки runner забивается за дни.

Подходы:

1. После каждого workflow: docker system prune -a --volumes --filter "until=4h" -f. Уберёт всё старше 4 часов кроме того, что используется прямо сейчас. -f (force) убирает confirm-prompt.

# .github/workflows/build.yml
jobs:
  build:
    steps:
      - run: docker build -t app:latest .
      # ... тесты ...
      - if: always()
        run: docker system prune -a --volumes --filter "until=4h" -f

2. Standalone cleanup job, scheduled:

# .github/workflows/cleanup.yml
on:
  schedule:
    - cron: '0 4 * * *'   # ежедневно в 4 утра
jobs:
  cleanup:
    runs-on: self-hosted
    steps:
      - run: docker system prune -a --volumes -f
      - run: docker builder prune -a -f

3. На self-hosted runner: в systemd-timer:

# /etc/systemd/system/docker-cleanup.timer
[Timer]
OnCalendar=daily
Persistent=true

# /etc/systemd/system/docker-cleanup.service
[Service]
ExecStart=/usr/bin/docker system prune -a --volumes --filter "until=24h" -f

Восстановление когда диск 100%

Если уже no space left:

# 1. Освободить место чем угодно за пределами Docker
sudo apt clean   # на хосте
rm -rf ~/.cache/pip

# 2. Если хватает места для docker daemon -- идём
docker system prune -a --volumes -f

# 3. Если daemon упал -- останавливаем и чистим вручную
sudo systemctl stop docker

# Удалить build cache напрямую (на Linux)
# ОСТОРОЖНО: уничтожит ВСЕ build-кэши, не только устаревшие
# sudo rm -rf /var/lib/docker/buildkit/

# Удалить overlay2 dangling layers (требует knowledge о слоях)
# sudo rm -rf /var/lib/docker/overlay2/<id-of-dangling>/

# 4. Запустить обратно
sudo systemctl start docker

# 5. Проверить
docker system df

На macOS с Docker Desktop / OrbStack: меньше опций, потому что данные в VM. Reset to factory defaults — ядерный вариант: удалит ВСЁ (все образы, контейнеры, volumes). После нужно pull всех образов заново.


Безопасный prune для прода

На production хостах с долго-живущими контейнерами:

# Только остановленные контейнеры (running не трогаются)
docker container prune -f

# Только dangling images (без тегов)
docker image prune -f

# Build cache (если builds происходят на этом хосте)
docker builder prune --filter "until=168h" -f   # старше недели

# НЕ ИСПОЛЬЗОВАТЬ на проде:
# docker volume prune   -- может удалить prod-данные
# docker system prune -a --volumes   -- слишком агрессивно

В Kubernetes хостах есть kubelet image garbage collection — настраивается через --image-gc-high-threshold / --image-gc-low-threshold. Эта система чистит сама, ручные prune не нужны.


Попробуй сам

Проверь, сколько у тебя сейчас Docker занимает:

docker system df

# Если интересно -- подробности
docker system df --verbose

# Уберём dangling и stopped (безопасно)
docker container prune -f
docker image prune -f

# Если хочешь почистить build cache (после убедиться, что cache не нужен прямо сейчас)
docker builder prune -f

# Проверим освобождённое
docker system df

# Опционально: глубокая очистка (НЕ на машине, где есть важные volumes!)
# docker system prune -a --volumes -f

Заведи привычку раз в неделю на dev-машине запускать docker system prune -f (без -a, чтобы не потерять полезные образы). Build cache отдельно — docker builder prune --filter "until=72h" -f.


Проверка знанийKnowledge check
CI-runner забит: docker system df показывает Build Cache 45GB (94% reclaimable). Команда хочет настроить автоматическую очистку, чтобы не превышать 10GB. Какой подход правильный?
ОтветAnswer
Добавить в CI workflow шаг docker builder prune --keep-storage 10G -f после каждого build (или в standalone scheduled job). Флаг --keep-storage позволяет указать целевой размер кэша -- BuildKit удалит least-recently-used записи, чтобы общий размер не превышал лимит. Альтернатива -- docker builder prune --filter until=24h -f (всё старше 24 часов), но без гарантии размера. На systemd-managed runner'е можно настроить timer + service для автозапуска. Также полезно настроить BuildKit max cache в /etc/docker/daemon.json через 'builder.gc.defaultKeepStorage'.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Какая команда показывает разбиение Docker disk usage по категориям (Images / Containers / Volumes / Build Cache)?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4