Що таке dangling image і як їх видалити?
Dangling images накопичуються природно у будь-якому Docker-сетапі, що часто білдить image. Це не corruption і не баг, це побічний ефект того, як працюють tag. Чистити їх це звичайна disk-гігієна.
Теорія
TL;DR
- Dangling image це image без tag і без іншого image, що посилається на нього як на батька. Доступний лише по digest.
- З'являються найчастіше після того, як
docker buildперевикористовує tag, попередній image з тим tag стає dangling. - У
docker imagesвидно як рядки, де REPOSITORY і TAG обидва<none>. - Безпечно видаляти: ніщо не посилається; нічого не зламається, якщо вони зникнуть.
docker image pruneвидаляє ВСІ dangling.docker image prune -aагресивніший, ще видаляє unused-but-tagged image.
Швидкий приклад
# Зібрати той самий tag двічі → перший image стає dangling
$ docker build -t myapp:1.0 .
$ # ... редагуємо Dockerfile ...
$ docker build -t myapp:1.0 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp 1.0 4f06b3e2c0c1 2 minutes ago 180MB
<none> <none> 8a3f2d1c9b8e 10 minutes ago 180MB ← DANGLING
nginx 1.27 a3b4c5d6e7f8 3 weeks ago 54MB
# Перелічити лише dangling
$ docker images -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 8a3f2d1c9b8e 10 minutes ago 180MB
# Почистити
$ docker image prune -f
Deleted Images:
deleted: sha256:8a3f2d1c9b8e...
Total reclaimed space: 180MBДругий білд замінив tag-pointer myapp:1.0; старий image все ще на диску, але без tag. prune його видаляє.
Dangling vs unused
Це різниця, на якій люди зашпинюються.
| Dangling | Unused | |
|---|---|---|
| Має tag? | Ні (<none>:<none>) | Так |
| На нього посилається container? | Ні | Ні |
Видаляє docker image prune | Так | Ні |
Видаляє docker image prune -a | Так | Так |
Dangling = untagged + ніщо не посилається. Unused = tagged, але жоден поточний container його не використовує.
Tagged image, що ти стягнув три місяці тому і ніколи не запускав container з нього, це unused, але не dangling. Звичайний prune його не зачепить; prune -a зачепить.
Як з'являються dangling image
Найпоширеніші шляхи:
- Перевикористання tag при білді.
docker build -t myapp:1.0двічі. Image першого білду втрачає tag. - Re-pull з тим же tag.
docker pull nginx:latestпісля того, як upstream запушив новий latest. Старий image стає untagged. - Multi-stage білди. Кожен проміжний стейдж дає untagged image. З дефолтними налаштуваннями BuildKit вони кешуються невидимо; з legacy builder вони показуються як dangling.
- Failed білди.
docker build, що падає посередині, лишає проміжні untagged image.
BuildKit (дефолт з Docker 23) набагато краще не лишає такого. Legacy builder продукував більше dangling image на білд.
Команди cleanup, у порядку агресії
# Лише dangling (безпечно, зазвичай те, що треба)
docker image prune
docker image prune -f # пропустити підтвердження
# Dangling І будь-який tagged image без container
docker image prune -a
# Все: зупинені container, dangling image, невикористані мережі, build-cache
docker system prune
# Те саме плюс volume (ДЕСТРУКТИВНО, стирає named volume теж)
docker system prune -a --volumes
# Filtered
docker image prune -a --filter 'until=24h' # лише image старші за 24гЗвичка щоденних ops: docker system prune -f щотижня на dev-машинах. Звільняє гігабайти.
Прод-обережність: ніколи system prune -a --volumes сліпо, флаг --volumes ще видаляє named volume, що можуть бути твоїми базами.
Типові помилки
Плутати prune з prune -a
# Видаляє лише untagged dangling image
$ docker image prune
# Видаляє ВСІ image, що жоден container не використовує (включно з tagged)
$ docker image prune -aЯкщо у тебе nginx:1.27, node:22, postgres:16 стягнуті, але жоден container їх зараз не крутить, звичайний prune їх лишає; prune -a видаляє. Сюрприз, коли вперше re-pull'аєш 200MB image, бо prune -a його змів.
Запуск system prune --volumes на проді
# ДЕСТРУКТИВНО: ще видаляє named volume (бази!)
$ docker system prune -af --volumes--volumes видаляє будь-який volume, не змонтований running-container. Якщо твоя DB коротко down на час deploy, її volume не приєднаний і його зносить. Ніколи не використовуй --volumes на проді без явної верифікації.
Думати dangling = corrupt
$ docker images
REPOSITORY TAG IMAGE ID SIZE
<none> <none> 8a3f2d1c9b8e 180MBDangling image не corrupt; це повністю валідні OCI image, які просто втратили tag. Можеш docker run <image-id> проти них без проблем. Видаляють, не зламані.
Pruning на CI runner з активними білдами
docker image prune -a посеред білду може race з build-cache і сповільнити наступні білди. Плануй cleanup між білдами, не під час.
Реальне застосування
- Dev-машини: щотижневий
docker system prune -f(і--volumesлише коли впевнений, що нічого важливого не unmounted). - CI runners: cleanup-хук в кінці кожного job:
docker container prune -f && docker image prune -f. - Прод-хости: запланований
docker image prune -af --filter 'until=168h'(тиждень) через cron, тримає свіжі image, видаляє старі. - Disk-pressure response:
docker system dfспочатку, щоб побачити, куди іде місце, потім таргетований prune.
Питання для поглиблення
Q: Яка різниця між docker image prune і docker rmi?
A: prune bulk + filtered; rmi <id> таргетований на конкретний image (або список). Бери prune, коли тобі байдуже, які саме image зникнуть; бери rmi, коли хочеш видалити конкретний.
Q: Як побачити використання диску Docker?
A: docker system df показує totals (image, container, volume, build-cache) і reclaimable простір. docker system df -v verbose з per-image і per-volume розмірами.
Q: Чи можна тримати dangling image для кешу?
A: Загалом ні, вони не використовуються як build-cache (BuildKit-cache живе деінде). Єдиний раз, коли б ти їх тримав, це якщо використовуєш legacy builder і конкретний intermediate перевикористовується. З сучасним BuildKit просто prune.
Q: Чому prune не видаляє мій image, хоч жоден container не running?
A: Бо у image є tag. Звичайний prune зачіпає лише dangling (untagged) image. Додай -a, щоб ще видалити unused-tagged.
Q: (Senior) Як налаштувати автоматичний Docker cleanup на прод-сервері?
A: Щоденний systemd-timer або cron-job, що крутить docker container prune -f --filter 'until=24h' && docker image prune -af --filter 'until=168h' && docker builder prune -f --filter 'until=72h'. Уникай --volumes. Моніторь диск через docker system df до і після для верифікації, що cleanup робить очікуване. Для великих кластерів це живе у host config-management (Ansible/Chef), не per-container.
Приклади
Cleanup loop для CI runner
# .github/workflows/cleanup.yml або Jenkins postBuild
#!/bin/sh
set -e
docker container prune -f --filter 'until=1h'
docker image prune -f
docker builder prune -f --filter 'until=24h'
# Volume лишаємо, вони тримають inter-job кеші, якщо є.Дрібний cleanup після кожного job; за тиждень тримає /var/lib/docker runner'а від вибуху.
Inspect перед prune
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 47 12 8.421GB 6.3GB (74%)
Containers 18 4 412MB 287MB (69%)
Local Volumes 9 5 3.2GB 180MB (5%)
Build Cache 128 0 2.1GB 2.1GB (100%)
$ docker image prune -af
Total reclaimed space: 6.3GB
$ docker builder prune -f
Total reclaimed space: 2.1GBЦифри кажуть, куди іде місце. Image-prune звільнив 6.3GB; build-cache ще 2.1GB. Volume не зачеплено (180MB тільки, і вони мабуть тримають реальні дані).
Bulk-видалення з фільтром
$ docker images -f dangling=true -q | xargs -r docker rmi
# Або простіше:
$ docker image prune -fДві форми еквівалентні. prune сучасний one-liner; xargs-форма з часів до того, як prune існував, і досі працює.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів