Disk Hygiene

Зачем

Тестовый VPS erp-test.nirbi.ru имеет всего 77GB. 2026-04-25 диск переполнился до 100% — git pull падал с No space left on device, контейнеры уходили в Dead. Источники: 58GB build cache от повторных --no-cache rebuild’ов и неограниченные json-логи контейнеров.

Защитные меры

Две независимых страховки. Обе живут в репо erp-infrastructure.

1. Лимит логов на контейнер

В docker-compose.yml объявлен YAML-anchor x-default-logging:

x-default-logging: &default-logging
  driver: json-file
  options:
    max-size: "50m"
    max-file: "3"

И применён к каждому из 17 сервисов через logging: *default-logging. Каждый контейнер не может занять логами больше 50MB × 3 ротации ≈ 150MB.

Проверка:

docker inspect erp-<service> --format '{{json .HostConfig.LogConfig}}'
# {"Type":"json-file","Config":{"max-file":"3","max-size":"50m"}}

Применяется при пересоздании

Старые контейнеры (созданные до коммита cb7b209) не подхватят лимит автоматически. Только docker compose up -d --force-recreate или обычный up -d после изменения compose’а пересоздаст их с новым LogConfig.

2. Ежедневный docker prune (cron)

Скрипт: erp-infrastructure/scripts/docker-cleanup.sh → копируется на VPS в /usr/local/sbin/docker-cleanup.sh.

Cron: /etc/cron.d/docker-cleanup срабатывает каждый день в 04:00 UTC.

Что делает:

  • docker system prune -af --filter "until=24h" — удаляет неиспользуемые образы старше суток
  • docker builder prune -f --filter "until=72h" — чистит build cache старше 3 дней

Что НЕ трогает:

  • Volumes (там postgres data, redis data, kafka data, minio data, certs)
  • Активные образы (используются текущими контейнерами)
  • Свежий build cache (последние 72ч — для быстрых итеративных билдов)

Лог: /var/log/docker-cleanup.log — каждый прогон пишет дату, занятость диска до/после, освобождённые размеры.

Установка на новом VPS

# 1. Скопировать скрипт
scp scripts/docker-cleanup.sh root@<vps>:/usr/local/sbin/
ssh root@<vps> chmod +x /usr/local/sbin/docker-cleanup.sh
 
# 2. Поставить cron
ssh root@<vps> 'cat > /etc/cron.d/docker-cleanup <<EOF
0 4 * * * root /usr/local/sbin/docker-cleanup.sh
EOF
chmod 644 /etc/cron.d/docker-cleanup'
 
# 3. Прогнать вручную для проверки
ssh root@<vps> /usr/local/sbin/docker-cleanup.sh
ssh root@<vps> cat /var/log/docker-cleanup.log

Если диск всё-таки забился

Симптомы: git pullNo space left on device, docker compose ps показывает контейнеры в статусе Dead.

Команды восстановления (по порядку):

# 1. Посмотреть, кто занял место
df -h /
docker system df
 
# 2. Жёсткая очистка (НЕ трогает volumes)
docker system prune -af
docker builder prune -af
 
# 3. Если контейнеры в Dead state — force remove
docker rm -f $(docker ps -a -q --filter status=dead)
 
# 4. Пересоздать compose
cd ~/erp/erp-infrastructure
docker compose up -d

Никогда не делать на проде

docker system prune -af --volumes — флаг --volumes снесёт postgres data. На тестовом VPS объём данных небольшой, но всё равно опасно. Скрипт docker-cleanup.sh намеренно не использует --volumes.

Что улучшить дальше (не сделано)

  • Алерт при 85% — простой df-чек в cron + curl в Telegram-бот. Сейчас узнаём только когда падает деплой.
  • GC build-cache на уровне Docker daemon/etc/docker/daemon.json с "builder": { "gc": { "defaultKeepStorage": "10GB" } }. Дублирует cron, но ловит всплески за счёт того, что Docker сам не даст кэшу разрастись > 10GB.
  • Убрать --no-cache по умолчанию из /deploy-all — основной источник cache-разрастания. Использовать --no-cache только когда подозреваем COPY-кэш-инвалидацию (Node.js с pnpm-lock.yaml).

Ссылки

  • Deployment Runbook
  • VPS Setup
  • erp-infrastructure/scripts/docker-cleanup.sh — сам скрипт
  • erp-infrastructure/docker-compose.ymlx-default-logging anchor