Як вирішити проблему "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 --volumesreclaim'ить все, що не 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 container | exited container, збережені для docker logs/docker start | docker container prune -f |
| Dangling image | image без tag (заміщені новішим build) | docker image prune -f |
| Unused image | image, не reference'ний жодним container | docker image prune -af (нота -a) |
| Anonymous-volume | volume, auto-created VOLUME-директивою Dockerfile, ніколи не cleaned | docker volume prune -f (named-volume теж, якщо -a) |
| Build-cache | BuildKit cache-layer від минулих build | docker builder prune -af |
| Container-logи | json-file логи, що виросли без меж | log-rotation-config |
| Networks | unused custom-networks (малі) | docker network prune -f |
Приклади
Диагностичний flow
# Крок 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 все безпечно
# Stopped-container, dangling-image, unused-network, build-cache
docker system prune -f
# Reclaimed: 12.5GBБез --volumes-флага значить volume залишаються. Безпечний default.
Агресивно: reclaim'ни все, що не in use
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-команди
# 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 -fContainer-логи
Логи дефолтно json-file-driver без size-limit. Chatty-app заповнює GB.
# Дивись 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"
doneTruncate runaway-log без restart:
sudo truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.logPermanent-фікс через ставлення log-limit у /etc/docker/daemon.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):
# Зупини 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
# /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:
docker builder prune -af
# Звільнює cache. Спробуй build знову.Або з /tmp (використовується для temporary-download):
df -h /tmp
# Якщо /tmp малий, set TMPDIR=/var/tmp перед docker build.Коли prune не допомагає
Іноді docker system df рапортує багато reclaimable-простору, але docker system prune reclaim'ить дуже мало. Причини:
- Inode exhausted (не block).
df -i, щоб перевірити. - Open file-handle тримають deleted-files. Restart daemon, щоб release їх.
- Volume-content величезний, але сам volume in use. Volume «active» (використовується running-container), тож prune skip'ає. Інспектуй volume-content через
docker run --rm -v <vol>:/data alpine du -sh /data. - 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-використання:
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 у каталог, про який ти забув.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів