Очистка Docker: почему «неиспользуемый» не значит «ненужный»

Как освободить диск от образов, томов и кэша — и не снести при этом то, без чего сервис потом не поднимется. С разбором реального случая.

v1 0 stars 0 forks 0 watchers 1 branch 0 runs Public
mikicreated via APIv1
Зачем это

Docker не убирает за собой. Каждая сборка оставляет слои, каждый перезапуск — старые образы. На активном сервере десятки гигабайт накапливаются за месяцы, и однажды диск просто кончается. Причём в самый неудачный момент: когда сервису надо перезапуститься.

Но у очистки есть обратная сторона, про которую редко пишут. Она в конце списка, и это самая важная часть.

Разобраться

1
Посмотреть, что занимает место

Сначала диск целиком, потом Docker отдельно:

bash
1df -h / # сколько занято на диске
2docker system df # сколько из этого — Docker

docker system df разбивает по категориям: образы, контейнеры, тома, кэш сборки. Колонка RECLAIMABLE показывает, сколько можно освободить.

Подробнее — флаг -v.

Why: Без этого шага очистка превращается в стрельбу вслепую. Часто оказывается, что место ест вовсе не Docker, а, например, логи в `/var/log`.
$docker system df -v
Check
  • Ты знаешь, сколько процентов диска занято
  • Ты знаешь, какая категория Docker занимает больше всего

Очистка

2
Убрать безопасное: остановленные контейнеры и кэш сборки

С этого начинай — риска почти нет, а освобождается много:

bash
1docker container prune # остановленные контейнеры
2docker builder prune # кэш сборки
3docker network prune # неиспользуемые сети

Кэш сборки обычно самая жирная строка на сервере, где регулярно собирают образы. Его потеря стоит только времени следующей сборки.

Why: Остановленный контейнер не нужен никому: если сервис управляется оркестратором или compose, он создаст новый. Кэш сборки — чисто ускорение, на работу никак не влияет.
$docker builder prune
Check
  • Освобождённый объём виден в выводе команды
  • Запущенные контейнеры не пострадали
3
Удалить старые образы — с фильтром по времениRecommended
bash
1# только висячие — слои без тега, самое безопасное
2docker image prune
3
4# все неиспользуемые старше суток — ОСТОРОЖНО
5docker image prune -a --filter "until=24h"

Разница принципиальная. Без -a удаляются только висячие слои — остатки пересборок. С -a удаляются все образы, на которых прямо сейчас не запущен ни один контейнер.

Фильтр until=24h оставляет свежее — то, что скачали сегодня для выкатки.

Why: Именно флаг `-a` и приводит к тому, ради чего написан этот список. Разбор — в следующем блоке.
$docker image prune
Check
  • Ты понимаешь разницу между prune и prune -a
Главное: «неиспользуемый» не равно «ненужный»

Почти вся документация по очистке говорит: «безопасно, удаляются только неиспользуемые ресурсы». Это правда буквально и обманчива по сути.

Реальный случай. Сервис в кластере лежал в состоянии 0/1 — упал несколько недель назад и не поднялся. Контейнера нет, значит образ не используется. Прошла очистка с -a и честно его удалила.

Когда дошли руки починить сервис, оказалось:

code
1Unable to find image 'registry/service:v1.2.1' locally

Образ пришлось тянуть заново — а это требует сети, доступа к реестру и времени. Если реестр приватный, а ключи истекли — восстановление превращается в отдельный квест.

Что из этого следует:

РесурсКогда считается неиспользуемымЧем грозит удаление
Образна нём не запущен ни один контейнер прямо сейчаслежачий сервис не поднимется без сети
Томне прикреплён ни к одному контейнерупотеря данных навсегда
Сетьнет подключённых контейнеровпочти ничем, создастся заново

Правило: перед prune -a проверь, нет ли сервисов в состоянии 0/N. Их образы выглядят ненужными ровно до того момента, когда понадобятся.

bash
1docker service ls | grep -v "1/1\|2/2\|3/3"

Очистка

4
Тома — только вручную и глазами
bash
1docker volume ls # список
2docker volume ls -f dangling=true # ни к чему не прикреплённые

Смотри список и удаляй поимённо:

bash
1docker volume rm имя-тома

Никогда не запускай docker volume prune на сервере с базами, не посмотрев список. Том остановленной базы считается неприкреплённым.

Why: Образ можно скачать заново. Контейнер можно создать заново. Том — единственное место, где лежат данные, и восстановить его неоткуда, кроме резервной копии. Если она есть.
$docker volume ls -f dangling=true
Check
  • Ты просмотрел список томов глазами
  • Ты не запускал volume prune без проверки

Автоматизация

5
Настроить регулярную очистку и мониторингRecommended

Ручная очистка работает один раз — потом про неё забывают. Рабочая схема из двух задач cron:

cron
1# очистка каждую ночь
20 3 * * * /root/docker-cleanup.sh >> /var/log/docker-cleanup.log 2>&1
3
4# проверка диска каждые 6 часов
50 */6 * * * /root/disk-monitor.sh >> /var/log/disk-monitor.log 2>&1

Разумные пороги в скрипте мониторинга:

bash
1DISK_THRESHOLD=80 # предупреждение
2DISK_CRITICAL=85 # автоочистка

Обязательно пиши логи — иначе при пропаже образа не узнаешь, кто его удалил и когда.

Why: На боевом сервере такая пара задач освободила около 20 ГБ и сняла заполнение диска с 87% до 51%. Главное — это происходит до того, как диск кончится посреди выкатки.
$crontab -l
Check
  • Задачи видны в crontab -l
  • Очистка пишет лог, и ты знаешь где он
  • Автоочистка не использует prune для томов
Сервис в кластере лежит в состоянии 0/1. Что сделает docker image prune -a с его образом?
Что из этого невозможно восстановить после удаления?
Шпаргалка
bash
1# посмотреть
2df -h /
3docker system df -v
4docker service ls | grep -v "1/1" # кто лежит — ПЕРЕД очисткой
5
6# безопасно
7docker container prune
8docker builder prune
9docker network prune
10
11# с оглядкой
12docker image prune # только висячие
13docker image prune -a --filter "until=24h" # все неиспользуемые старше суток
14
15# только вручную
16docker volume ls -f dangling=true
17docker volume rm имя-тома

Одна команда, которую стоит запомнить как опасную: docker system prune -a --volumes. Она убирает всё сразу, включая тома. На сервере с боевыми данными её не запускают никогда.