Skip to main content

Як вирішити проблему "no space left on device" на Docker-хості?

«No space left on device» це найтиповіший Docker-біль у production. Images накопичуються, build-cache росте, container-логи без меж, volumes переживають container'и, що їх створили. Фікс це мікс immediate-cleanup і довгострокової гігієни.

Теорія

TL;DR

  • Діагнозь через docker system df перед prune. Знай, куди йде простір.
  • docker system prune -af --volumes reclaim'ить все, що не active. Безпечно у dev, обережно у prod (дропає detached-volume).
  • Логи мовчазний killer: chatty-container може заповнити /var/lib/docker/containers/<id>/<id>-json.log до гігабайт.
  • Build-cache може вирости до десятків GB на busy CI-host. Prune regularly.
  • Довгостроково: ставь /var/lib/docker на окремий partition; enable log-rotation у daemon.json; cron prune.

Куди йде простір

Docker зберігає все під /var/lib/docker (або куди показує data-root):

/var/lib/docker/ ├── overlay2/ # image-layer'и + writable container-layer ├── containers/<id>/ # container-metadata + логи (json-file) ├── volumes/ # named-volume ├── image/ # image-manifest metadata ├── buildkit/ # build-cache (з BuildKit) └── tmp/ # transient

На busy-host, breakdown зазвичай:

  • 30-50%, image-layer
  • 20-40%, anonymous/orphan-volume
  • 10-20%, container-logи
  • 5-20%, build-cache

docker system df показує це в human-readable формі.

Категорії waste

КатегоріяЩо цеЯк чистити
Stopped containerexited container, збережені для docker logs/docker startdocker container prune -f
Dangling imageimage без tag (заміщені новішим build)docker image prune -f
Unused imageimage, не reference'ний жодним containerdocker image prune -af (нота -a)
Anonymous-volumevolume, auto-created VOLUME-директивою Dockerfile, ніколи не cleaneddocker volume prune -f (named-volume теж, якщо -a)
Build-cacheBuildKit cache-layer від минулих builddocker builder prune -af
Container-logиjson-file логи, що виросли без межlog-rotation-config
Networksunused custom-networks (малі)docker network prune -f

Приклади

Диагностичний flow

bash
# Крок 1: який розмір /var/lib/docker? sudo du -sh /var/lib/docker # 87G # Крок 2: breakdown за Docker-категорією docker system df # TYPE TOTAL ACTIVE SIZE RECLAIMABLE # Images 67 12 31.4GB 24.1GB (76%) # Containers 34 8 3.2GB 2.7GB # Volumes 28 5 45.0GB 38.0GB (84%) # Build Cache 2104 8.0GB 8.0GB # Крок 3: drill у найгіршого порушника docker system df -v # verbose: per-image, per-container, per-volume

Колонка RECLAIMABLE твій перший target.

Швидкий виграш: prune все безпечно

bash
# Stopped-container, dangling-image, unused-network, build-cache docker system prune -f # Reclaimed: 12.5GB

Без --volumes-флага значить volume залишаються. Безпечний default.

Агресивно: reclaim'ни все, що не in use

bash
docker system prune -af --volumes # Включає: # - всі image, не використані container'ом (не лише dangling) # - всі volume, не mounted у жодному container

Небезпечно у prod: stopped-container's volume OK, але volume, що існує, але випадково не currently-mounted (бо єдиний container, що його використовує, зараз перестворюється), видаляється. Бери це у dev/CI, не prod.

Targeted-команди

bash
# Images docker image prune -f # лише dangling docker image prune -af # всі image, не використані container # Container'и docker container prune -f # stopped-container # Volume'и docker volume prune -f # volume, не mounted у жодному container # Build-cache docker builder prune -f # cache старший за 24h, dangling docker builder prune -af # весь build-cache docker builder prune --filter until=168h # cache старший за 7 днів # Networks docker network prune -f

Container-логи

Логи дефолтно json-file-driver без size-limit. Chatty-app заповнює GB.

bash
# Дивись per-container log-file розміри for c in $(docker ps -q); do name=$(docker inspect -f '{{.Name}}' $c | sed 's|/||') size=$(sudo du -sh /var/lib/docker/containers/$c/$c-json.log 2>/dev/null | cut -f1) echo "$size $name" done

Truncate runaway-log без restart:

bash
sudo truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log

Permanent-фікс через ставлення log-limit у /etc/docker/daemon.json:

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

Потім sudo systemctl restart docker. Існуючі container'и тримають старий config, поки не перестворені; нові container'и inherit.

Для production, бери log-shipper (Fluentd, Loki, syslog), щоб логи покидали host повністю.

Перенеси /var/lib/docker на більший partition

Якщо OS-partition малий (наприклад, DigitalOcean-droplet з 25 GB):

bash
# Зупини daemon sudo systemctl stop docker # Mount новий disk на /mnt/docker sudo rsync -a /var/lib/docker/ /mnt/docker/ sudo mv /var/lib/docker /var/lib/docker.bak sudo ln -s /mnt/docker /var/lib/docker # (або update daemon.json з "data-root": "/mnt/docker") sudo systemctl start docker docker info | grep 'Docker Root Dir'

Верифікуй, потім rm -rf /var/lib/docker.bak.

Periodic cleanup через cron

bash
# /etc/cron.daily/docker-prune #!/bin/sh docker container prune -f docker image prune -f docker builder prune -f --filter until=72h # Не включай --volumes; volume-cleanup потребує ручного review

Зроби executable: chmod +x /etc/cron.daily/docker-prune.

«No space left» під час build

Під час docker build, помилка часто з BuildKit's intermediate-layer, не з фінального image:

bash
docker builder prune -af # Звільнює cache. Спробуй build знову.

Або з /tmp (використовується для temporary-download):

bash
df -h /tmp # Якщо /tmp малий, set TMPDIR=/var/tmp перед docker build.

Коли prune не допомагає

Іноді docker system df рапортує багато reclaimable-простору, але docker system prune reclaim'ить дуже мало. Причини:

  1. Inode exhausted (не block). df -i, щоб перевірити.
  2. Open file-handle тримають deleted-files. Restart daemon, щоб release їх.
  3. Volume-content величезний, але сам volume in use. Volume «active» (використовується running-container), тож prune skip'ає. Інспектуй volume-content через docker run --rm -v <vol>:/data alpine du -sh /data.
  4. Snapshot/COW-chain. З devicemapper, можливо потрібно recreate storage-pool. Мігруй на overlay2.

Реальне застосування

  • CI-host: prune builder-cache погодинно; prune image щоденно; логи у syslog.
  • Production app-сервери: log-rotation у daemon.json; weekly-prune-cron; alerting на disk-usage > 70%.
  • Single-host hobby: monthly docker system prune -af. Done.
  • Disk-emergency: docker system prune -af --volumes, якщо можеш підтвердити, що нема detached, але потрібних volume; інакше prune image і builder спочатку.

Типові помилки

Run prune --volumes у prod наосліп

Якщо сервіс перестворюється і його volume коротко detached, prune його видаляє. Завжди підтверджуй volume-використання:

bash
docker volume ls # Вручну інспектуй кожен незнайомий volume перед prune

Забути -a на docker image prune

Без -a, лише dangling-image (без tag) видаляються. Tagged, але unused-image лишаються.

Ігнорувати container-логи

Один chatty-сервіс може заповнити 50 GB у <id>-json.log, поки ти питаєш, чому disk повний, а docker system df не показує нічого незвичайного.

Ставити /var/lib/docker на OS-partition без моніторингу

Коли disk заповнюється, daemon може стати unstable; restart fail, бо логи не можуть flush. Окремий partition + alerting запобігає.

Питання для поглиблення

Q: Різниця між docker prune і docker rm?


A: docker rm <name> видаляє конкретний container; docker container prune видаляє всі stopped-container за один shot. Та ж ідея для image і volume.

Q: Чи prune вб'є running-сервіси?


A: Ні. Prune-команди skip'ають resource, що active (running-container, mounted-volume, used-image). Чіпають лише genuinely-unused речі.

Q: Як побачити, що у volume перед видаленням?


A: docker run --rm -v <volname>:/data alpine ls -la /data. Якщо important-data, backup перед prune.

Q: (Senior) Як будувати довгострокову retention-policy для build-cache?


A: BuildKit підтримує cache-backend (--cache-to=type=registry,ref=...). Push-cache у dedicated-registry-image; локально prune все старше за N днів; покладайся на remote-cache для shared-CI. Це обмежує локальний disk, зберігаючи cross-build deduplication.

Q: (Senior) Чому disk заповнюється швидше, ніж docker system df каже?


A: docker system df не включає daemon.json-level стан, BuildKit's metadata або зовнішні mount. Порівнюй з du -sh /var/lib/docker/*. Невідповідності зазвичай значать: orphaned overlay2-каталоги з daemon-crash, дуже великі container log-file, або host-bind-mount у каталог, про який ти забув.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів