3.4. Управление images
Цели
После этого материала вы сможете:
- находить образ, ответственный за расход дискового пространства, и объяснять его размер по слоям;
- читать
docker historyи связывать слои с инструкциямиDockerfile; - фильтровать и форматировать вывод
docker imagesпод конкретную задачу; - объяснить, откуда берутся dangling images и почему они накапливаются;
- безопасно удалять образы, отличая неиспользуемые от нужных;
- объяснить, что именно удаляет каждый вариант
docker system prune, и выполнить предварительный просмотр; - учитывать различия вывода в Docker Engine 29 с containerd image store.
Предварительные знания
- 3.1. Архитектура image — слои, config, история;
- 3.2. Слои и copy-on-write — почему размер не уменьшается удалением;
- 3.3. Tags и digests — откуда берутся образы без тега.
Ключевые термины
| Термин | Объяснение |
|---|---|
dangling image | Образ без тега, на который никто не ссылается: <none>:<none> |
unused image | Образ с тегом, не используемый ни одним container |
intermediate image | Промежуточный образ, созданный при сборке (в классическом builder) |
build cache | Кэш BuildKit, хранящийся отдельно от образов |
reclaimable | Объём, который можно освободить удалением неиспользуемого |
filter | Условие отбора объектов в командах Docker: --filter key=value |
Теория
Три источника расхода места
Прежде чем что-то удалять, нужно понять, что именно занимает диск. Docker расходует место по четырём независимым категориям:
| Категория | Что это | Растёт когда |
|---|---|---|
| Images | Слои образов | Загружаете и собираете образы |
| Containers | Writable layers | Containers пишут данные и не удаляются |
| Local Volumes | Данные volumes | Приложения хранят состояние |
| Build Cache | Кэш BuildKit | Собираете образы |
Люди обычно чистят первую категорию, а место чаще всего съедают четвёртая и вторая. Поэтому диагностика начинается с docker system df, а не с docker images.
Почему размеры не складываются
Сумма колонки SIZE в docker images почти всегда больше фактически занятого места. Причина — дедупликация слоёв (урок 3.1): десять образов на общей базе делят её слои, и на диске она лежит один раз.
Правило: для оценки занятого места используйте docker system df, а не сложение SIZE.
Откуда берутся образы <none>
Образ без тега появляется в трёх ситуациях, и их полезно различать.
1. Тег перепривязан к новому образу. Вы выполнили docker pull python:3.13-slim, вышла новая сборка — тег переехал, старый образ остался без имени. Это главный источник накопления у тех, кто регулярно обновляется.
2. Пересборка с тем же тегом. docker build -t myapp:dev . — предыдущий myapp:dev теряет тег. При активной разработке это происходит десятки раз в день.
3. Загрузка по digest. docker pull python@sha256:... даёт образ без тега намеренно.
Первые два случая — dangling images, их обычно можно удалять. Третий — нет: образ нужен, просто у него нет имени.
Различить помогает фильтр dangling=true, который отбирает именно образы, не имеющие ни тега, ни ссылок от других образов.
Что показывает docker history
Команда выводит содержимое поля history из конфигурации образа (урок 3.1) вместе с размерами слоёв.
Читается снизу вверх: последняя строка — базовый слой, первая — последняя инструкция.
IMAGE CREATED CREATED BY SIZE
<missing> 2 minutes ago CMD ["python" "app.py"] 0B ← последняя
<missing> 2 minutes ago COPY app.py /app/ # buildkit 1.2kB
<missing> 3 minutes ago RUN pip install -r requirements.txt 84.3MB
<missing> 3 minutes ago COPY requirements.txt /app/ # bui… 128B
<missing> 3 weeks ago CMD ["python3"] 0B
<missing> 3 weeks ago ADD file:abc... in / 126MB ← базовый
<missing> в колонке IMAGE — нормально: это не ошибка. Промежуточные образы существуют только на машине, где выполнялась сборка; в загруженном образе от них остались лишь записи истории.
Строки с размером 0B соответствуют инструкциям, изменившим только конфигурацию.
Различия вывода в Docker Engine 29
С containerd image store вывод docker images изменился:
| Изменение | Было | Стало |
|---|---|---|
| Формат | Плоский список | Свёрнутое дерево |
| Образы без тега | Показывались | Скрыты без --all |
| Колонки | SIZE | DISK USAGE, CONTENT SIZE |
| Усечение имён | Длинные имена обрезались | Не обрезаются |
Различие DISK USAGE и CONTENT SIZE: containerd хранит слои и в сжатом, и в распакованном виде. CONTENT SIZE — объём blob (сжатых), DISK USAGE — фактическое потребление с учётом распакованных снапшотов.
Практический вывод: если вы видите два разных числа вместо одного SIZE, вы работаете на containerd image store. Проверить: docker info --format '{{.Driver}}' даёт overlayfs.
Ловушка: docker images и docker image inspect дают разные числа
Различие не косметическое, и на нём легко ошибиться в четыре раза.
docker images fastapi-basic
echo "--- через --format ---"
docker images fastapi-basic --format '{{.Size}}'
docker image inspect fastapi-basic --format '{{.Size}}' | numfmt --to=si
echo "--- сколько весит перенос ---"
docker save fastapi-basic | wc -c | numfmt --to=si
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
fastapi-basic:latest d59b4177e2da 293MB 68.5MB
--- через --format ---
293MB
69M
--- сколько весит перенос ---
69M
{{.Size}} в двух командах означает разное:
| Команда | Что возвращает | Для чего это число |
|---|---|---|
docker images --format '{{.Size}}' | DISK USAGE | сколько занято на узле |
docker image inspect --format '{{.Size}}' | CONTENT SIZE | сколько качается из registry |
Совпадение docker save с inspect подтверждает: inspect считает сжатые blob'ы.
Почему это важно. Требование вида «образ меньше 200 MB» без указания команды становится бессмысленным: один и тот же образ FastAPI — 293 MB или 68.5 MB, смотря чем мерить. Разница — коэффициент сжатия слоёв, для Debian-основы примерно 4×.
Ориентир для сравнения: python:3.13-slim сам по себе — 178 MB на диске при 41 MB контента. То есть на диске у приложения на slim остаётся 22 MB до порога в 200 MB — уложиться нельзя, и порог задавался не про это число.
На классическом graphdriver-хранилище оба числа совпадали, потому что распакованный размер был единственным. Утверждение приведено по документации; на этом стенде проверить нельзя — переключение хранилища требует перезапуска демона с потерей образов.
Внутренний механизм
Что происходит при docker rmi
- Docker проверяет, ссылаются ли на образ существующие containers (включая остановленные). Если да — отказ.
- Удаляется указанный тег. Если у образа остались другие теги — на этом всё, вывод
Untagged. - Если тегов не осталось, удаляется запись об образе, вывод
Deleted. - Для каждого слоя проверяется, ссылаются ли на него другие образы. Слои без ссылок удаляются с диска.
Шаг 4 объясняет, почему удаление образа на 1.5 GB может освободить 40 MB: остальные слои переиспользуются.
Шаг 2 объясняет разницу между Untagged и Deleted в выводе — их постоянно путают.
Ссылки от остановленных containers
Остановленный container продолжает ссылаться на свой образ. Отсюда типичная ситуация:
Error response from daemon: conflict: unable to remove repository reference
"myapp:1.0" (must force) - container 3f2a1b4c5d6e is using its referenced image
Решение — удалить container, а не применять -f. Флаг --force снимет тег, но образ останется на диске, привязанный к container: место не освободится, а образ станет <none>. Это ухудшает ситуацию, а не улучшает.
Команды и примеры
Начинать с общей картины
docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 24 6 8.412GB 5.903GB (70%)
Containers 11 3 184.2MB 131.7MB (71%)
Local Volumes 7 3 2.104GB 1.312GB (62%)
Build Cache 183 0 4.271GB 4.271GB
Читается так:
| Колонка | Значение |
|---|---|
TOTAL | Всего объектов |
ACTIVE | Используются: образ имеет container, volume примонтирован |
SIZE | Занято с учётом дедупликации |
RECLAIMABLE | Освободится при удалении неиспользуемого |
В этом примере суммарно 15 GB, из которых освободить можно 11.5 GB. Больше всего — build cache, а не образы.
Детализация:
docker system df -v
Выводит построчно каждый образ, container, volume и запись кэша. На загруженной машине вывод длинный — направляйте в файл или в less.
Анализ размера конкретного образа
docker pull -q python:3.13-slim
mkdir -p /tmp/mgmt && cd /tmp/mgmt
cat > requirements.txt <<'EOF'
fastapi==0.141.1
uvicorn[standard]==0.52.0
EOF
cat > app.py <<'EOF'
print("hello")
EOF
cat > Dockerfile <<'EOF'
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]
EOF
docker build -q -t sizedemo:1 . > /dev/null
docker history sizedemo:1 --format 'table {{.Size}}\t{{.CreatedBy}}' --no-trunc | head -8
SIZE CREATED BY
0B CMD ["python" "app.py"]
12.3kB COPY app.py . # buildkit
51.9MB RUN /bin/sh -c pip install --no-cache-dir -r requirements.txt # buildkit
12.3kB COPY requirements.txt . # buildkit
8.19kB WORKDIR /app
0B CMD ["python3"]
16.4kB RUN /bin/sh -c set -eux; for src in idle3 pip3 pydoc3 python3 ...
Сразу видно распределение: 51.9 MB добавили зависимости, остальное — базовый образ.
Обратите внимание на COPY app.py . — 12.3 kB при файле в тридцать байт. Слой не бывает меньше блока файловой системы, и десяток мелких COPY обойдётся дороже, чем кажется по размеру исходников.
Отсортируем слои по убыванию размера — это самый быстрый способ найти виновника раздувания:
docker history sizedemo:1 --no-trunc --format '{{.Size}}\t{{.CreatedBy}}' \
| python3 -c "
import sys, re
def to_bytes(s):
s = s.strip()
m = re.match(r'^([\d.]+)\s*([KMGT]?B)$', s)
if not m:
return 0
n, unit = float(m.group(1)), m.group(2)
return int(n * {'B':1,'KB':10**3,'MB':10**6,'GB':10**9,'TB':10**12}[unit])
rows = []
for line in sys.stdin:
size, _, cmd = line.partition('\t')
b = to_bytes(size)
if b:
rows.append((b, size.strip(), cmd.strip()[:70]))
rows.sort(reverse=True)
total = sum(r[0] for r in rows)
print(f'Слоёв с данными: {len(rows)}, суммарно {total/10**6:.1f} MB\n')
for b, s, cmd in rows:
print(f'{s:>10} {b*100/total:5.1f}% {cmd}')
"
Слоёв с данными: 4, суммарно 184.4 MB
87.4MB 47.4% # debian.sh --arch 'amd64' out/ 'trixie' '@1785715200'
51.9MB 28.1% RUN /bin/sh -c pip install --no-cache-dir -r requirements.txt # buildk
40.2MB 21.8% RUN /bin/sh -c set -eux; savedAptMark="$(apt-mark showmanual)"; apt
4.94MB 2.7% RUN /bin/sh -c set -eux; apt-get update; apt-get install -y --no-ins
Вывод для этого образа: 70 % размера — база Debian и установка самого интерпретатора, 28 % — зависимости. Оптимизировать нужно выбор базы, а не команды pip.
Числа сняты 2026-08-05; при следующей пересборке python:3.13-slim они изменятся. Смотреть нужно на доли, а не на абсолютные значения. Это ровно то решение, которое разбирается в разделе 06.
Фильтры и форматирование
# только образы конкретного repository
docker images python
# образы без тега
docker images --filter 'dangling=true'
# созданные после указанного образа
docker images --filter 'since=python:3.13-slim'
# по метке
docker images --filter 'label=maintainer=course@example.com'
# только идентификаторы (для передачи в другие команды)
docker images -q
Полезные форматы:
# компактная таблица с датой
docker images --format 'table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}'
# самые большие образы
docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}' | sort -hr | head -10
1.24GB tensorflow/tensorflow:latest
612MB postgres:17
168MB sizedemo:1
178MB python:3.13-slim
97.2MB debian:trixie-slim
8.31MB alpine:3.21
Флаг sort -h понимает суффиксы KB, MB, GB — это то, что нужно.
Untagged против Deleted
docker tag sizedemo:1 sizedemo:copy
docker rmi sizedemo:copy
Untagged: sizedemo:copy
Только Untagged — образ остался, у него есть другой тег.
docker rmi sizedemo:1
Untagged: sizedemo:1
Deleted: sha256:7a6b5c4d3e2f19081726354453627180...
Deleted: sha256:1e0f9a8b7c6d5e4f3a2b190877665544...
Теперь и Untagged, и Deleted — тегов не осталось, образ и его уникальные слои удалены. Слои базового образа не удалены: на них ссылается python:3.13-slim.
Образ занят container
docker build -q -t sizedemo:2 . > /dev/null
docker run -d --name busy sizedemo:2 sleep 300 > /dev/null
docker rmi sizedemo:2
Error response from daemon: conflict: unable to remove repository reference
"sizedemo:2" (must force) - container a1b2c3d4e5f6 is using its referenced image 8f7e6d5c4b3a
Найти виновника:
docker ps -a --filter ancestor=sizedemo:2 --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}'
CONTAINER ID NAMES STATUS
a1b2c3d4e5f6 busy Up 12 seconds
Правильное решение — удалить container:
docker rm -f busy
docker rmi sizedemo:2
Untagged: sizedemo:2
Deleted: sha256:8f7e6d5c4b3a...
Не используйте
docker rmi -fв этой ситуации. Флаг снимет тег, но образ останется на диске привязанным к container и превратится в<none>. Место не освободится, а разобраться станет труднее.
Накопление dangling images
Воспроизведём главный источник мусора — пересборку с одним тегом:
for i in 1 2 3; do
echo "print('сборка $i')" > app.py
docker build -q -t rebuilt:dev . > /dev/null
done
docker images --filter 'dangling=true' --format 'table {{.ID}}\t{{.Size}}\t{{.CreatedSince}}'
IMAGE ID SIZE CREATED
3e2f1908a7b6 168MB 10 seconds ago
2f1908a7b6c5 168MB 18 seconds ago
Две сборки потеряли тег. Колонка SIZE показывает виртуальный размер — фактически уникальных данных там килобайты, но записи копятся, и при активной разработке их становятся сотни.
Сколько места они реально занимают:
docker system df -v 2>/dev/null | grep -A5 'Images space usage' | head -6
Безопасное удаление именно dangling:
docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:3e2f1908a7b6...
deleted: sha256:2f1908a7b6c5...
Total reclaimed space: 62.14kB
docker image prune без флагов удаляет только образы без тега. Это самая безопасная из команд очистки: удаляется то, на что и так ничто не ссылается.
Предварительный просмотр перед удалением
Ни одна команда prune не имеет флага dry-run. Поэтому смотреть нужно заранее, отдельными командами.
echo "=== Будет удалено при docker image prune ==="
docker images --filter 'dangling=true' --format '{{.ID}}\t{{.Size}}'
echo
echo "=== Дополнительно удалится при docker image prune -a ==="
docker images --format '{{.Repository}}:{{.Tag}}\t{{.ID}}' | while IFS=$'\t' read -r name id; do
if ! docker ps -aq --filter "ancestor=$id" | grep -q .; then
echo " $name"
fi
done
Второй блок перечисляет образы с тегами, не используемые ни одним container. Именно их удалит -a, и именно здесь чаще всего теряют нужное.
Опасные варианты очистки
Опасные команды. Ниже перечислены команды, удаляющие данные безвозвратно. Для каждой указано, что именно исчезнет и можно ли это восстановить.
| Команда | Что удаляет | Восстановимо? |
|---|---|---|
docker image prune | Образы без тега | Пересборкой или загрузкой из registry |
docker image prune -a | Все образы, не используемые container'ами, включая теговые | Только если они есть в registry |
docker container prune | Все остановленные containers | Нет: writable layers теряются |
docker volume prune | Volumes, не примонтированные ни к одному container | Нет. Данные пропадают безвозвратно |
docker builder prune | Кэш сборки | Восстанавливается пересборкой (медленно) |
docker system prune | Остановленные containers, неиспользуемые сети, dangling images, build cache | Частично |
docker system prune -a --volumes | Всё перечисленное плюс все неиспользуемые образы и все неиспользуемые volumes | Нет для volumes |
Самая опасная — последняя. Типичный сценарий потери данных: разработчик остановил стек на ночь через docker compose down, утром выполнил docker system prune -a --volumes для освобождения места — и вместе с мусором удалил volume с базой данных, потому что она была не запущена и потому «не используется».
Безопасная последовательность:
# 1. Посмотреть, что есть
docker system df
# 2. Убедиться, что важные volumes не пропадут
docker volume ls
docker volume ls --filter dangling=true # именно эти удалит volume prune
# 3. Начать с безопасного.
# Обе команды спрашивают подтверждение [y/N] — это здесь к месту.
# Для скриптов добавьте -f, но тогда и проверять придётся заранее.
docker image prune
docker builder prune
# 4. Проверить результат
docker system df
В подавляющем большинстве случаев шага 3 достаточно: build cache и dangling images дают основной объём.
Очистка build cache
docker builder prune -f --filter 'until=168h'
Удаляет записи кэша старше недели. Флаг --filter until= доступен и у image prune, и у system prune — это самый практичный способ чистить регулярно, не теряя актуального.
Ограничить рост кэша заранее:
docker builder prune -f --reserved-space 10GB
Флаг назывался
--keep-storage; в Docker 29 старое имя ещё принимается, но печатаетFlag --keep-storage has been deprecated.
Постоянное ограничение задаётся в конфигурации BuildKit; подробно — в разделе 13.
Уборка после урока
docker rm -f busy 2>/dev/null || true
docker rmi rebuilt:dev sizedemo:1 sizedemo:2 2>/dev/null || true
docker image prune -f
rm -rf /tmp/mgmt
docker system df
Практическое упражнение
Задание. Напишите скрипт docker-space.sh, который выполняет безопасный аудит дискового пространства Docker.
Скрипт должен:
- Показать общую картину по четырём категориям с указанием освобождаемого объёма.
- Перечислить пять самых больших образов.
- Отдельно показать dangling images: количество и объём.
- Перечислить образы с тегами, не используемые ни одним container, — то есть то, что удалит
prune -a. - Показать volumes, не примонтированные ни к одному container, с явным предупреждением о невосстановимости.
- Вывести рекомендованную последовательность команд очистки, отсортированную по возрастанию риска.
Скрипт не должен ничего удалять.
Подсказки
Подсказка 1
docker system df --format '{{json .}}' даёт структурированный вывод, который проще разбирать, чем таблицу.
Подсказка 2
Образ используется, если существует хотя бы один container (в любом состоянии) с этим ancestor:
docker ps -aq --filter "ancestor=<id>"
Подсказка 3
Volumes без container'ов отбираются фильтром dangling=true:
docker volume ls --filter dangling=true -q
Решение
Сначала выполните задание самостоятельно.
Показать решение
#!/usr/bin/env bash
# docker-space.sh — безопасный аудит дискового пространства Docker.
# Ничего не удаляет.
set -uo pipefail
docker info > /dev/null 2>&1 || { echo "Нет связи с Docker daemon." >&2; exit 1; }
echo "═══ 1. Общая картина ═══"
docker system df | sed 's/^/ /'
echo
echo "═══ 2. Пять самых больших образов ═══"
docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}' \
| grep -v '<none>:<none>' \
| sort -hr | head -5 \
| awk -F'\t' '{printf " %-10s %s\n", $1, $2}'
echo
echo "═══ 3. Dangling images (без тега) ═══"
dangling_ids="$(docker images --filter 'dangling=true' -q)"
if [ -z "$dangling_ids" ]; then
echo " нет"
else
count="$(echo "$dangling_ids" | grep -c .)"
echo " штук: $count"
docker images --filter 'dangling=true' \
--format ' {{.ID}} {{.Size}} создан {{.CreatedSince}}' | head -10
[ "$count" -gt 10 ] && echo " ... и ещё $((count - 10))"
echo
echo " Безопасно удаляются: docker image prune"
fi
echo
echo "═══ 4. Образы с тегами, не используемые container'ами ═══"
echo " (именно это дополнительно удалит 'docker image prune -a')"
unused=0
while IFS=$'\t' read -r name id size; do
[ "$name" = "<none>:<none>" ] && continue
if ! docker ps -aq --filter "ancestor=$id" 2>/dev/null | grep -q .; then
printf ' %-46s %s\n' "$name" "$size"
unused=$((unused + 1))
fi
done < <(docker images --format '{{.Repository}}:{{.Tag}}\t{{.ID}}\t{{.Size}}')
[ "$unused" -eq 0 ] && echo " нет"
echo
echo "═══ 5. Volumes без container'ов ═══"
vols="$(docker volume ls --filter dangling=true -q)"
if [ -z "$vols" ]; then
echo " нет"
else
echo "$vols" | while read -r v; do
[ -z "$v" ] && continue
mp="$(docker volume inspect "$v" --format '{{.Mountpoint}}' 2>/dev/null)"
sz="$(sudo du -sh "$mp" 2>/dev/null | cut -f1)"
printf ' %-44s %s\n' "$v" "${sz:-?}"
done
echo
echo " ВНИМАНИЕ: 'docker volume prune' удалит их БЕЗВОЗВРАТНО."
echo " Остановленный стек не значит «ненужный»: база данных"
echo " после 'compose down' попадает именно в этот список."
fi
echo
echo "═══ 6. Рекомендуемый порядок очистки (по возрастанию риска) ═══"
cat <<'EOF'
1. docker builder prune --filter 'until=168h'
кэш сборки старше недели. Риск: замедление следующей сборки.
2. docker image prune
только образы без тега. Риск: минимальный.
3. docker container prune
остановленные containers. Риск: теряются их writable layers.
Проверьте: docker ps -a
4. docker image prune -a
все образы без container'ов, включая теговые.
Риск: придётся заново загружать или собирать. См. пункт 4 выше.
5. docker volume prune
ОПАСНО И НЕОБРАТИМО. Сначала сделайте backup нужных volumes.
Команду 'docker system prune -a --volumes' не используйте:
она объединяет все шаги, включая самый опасный, без разбора.
EOF
Запуск:
chmod +x docker-space.sh
./docker-space.sh
Ожидаемый вывод (сокращённо):
═══ 1. Общая картина ═══
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 24 6 8.412GB 5.903GB (70%)
Containers 11 3 184.2MB 131.7MB (71%)
Local Volumes 7 3 2.104GB 1.312GB (62%)
Build Cache 183 0 4.271GB 4.271GB
═══ 2. Пять самых больших образов ═══
1.24GB tensorflow/tensorflow:latest
612MB postgres:17
168MB sizedemo:1
178MB python:3.13-slim
97.2MB debian:trixie-slim
═══ 3. Dangling images (без тега) ═══
штук: 2
3e2f1908a7b6 168MB создан 10 seconds ago
2f1908a7b6c5 168MB создан 18 seconds ago
Безопасно удаляются: docker image prune
═══ 4. Образы с тегами, не используемые container'ами ═══
(именно это дополнительно удалит 'docker image prune -a')
tensorflow/tensorflow:latest 1.24GB
debian:trixie-slim 97.2MB
═══ 5. Volumes без container'ов ═══
myproject_pgdata 612M
ВНИМАНИЕ: 'docker volume prune' удалит их БЕЗВОЗВРАТНО.
...
Что важно в этом решении.
Пункт 5 — главная его ценность. Volume myproject_pgdata выглядит «неиспользуемым» просто потому, что стек остановлен. Автоматическая очистка удалит базу данных. Скрипт показывает это явно, до того как что-то произойдёт.
Пункт 4 отвечает на вопрос, который обычно задают уже после prune -a: «а что именно удалилось?». Список удобно просмотреть заранее и решить, готовы ли вы заново загружать эти образы.
Порядок в пункте 6 не случаен: build cache восстанавливается сам, образы — из registry, а volumes не восстанавливаются никак.
Проверка результата
docker system df
docker images --filter 'dangling=true' -q | wc -l
Убедитесь, что можете ответить: какая категория занимает у вас больше всего места и какая команда освободит её с наименьшим риском.
Типичные ошибки
| Ошибка | Причина | Исправление |
|---|---|---|
Складывать SIZE из docker images | Кажется, что это фактический объём | Дедупликация слоёв; использовать docker system df |
| Чистить образы, когда место занял build cache | Начинают с самого заметного | Начинать с docker system df и смотреть, где объём |
docker rmi -f при занятом образе | Быстрый способ обойти ошибку | Удалить container; -f оставит образ на диске без тега |
docker system prune -a --volumes для освобождения места | Одна команда вместо разбора | Volumes невосстановимы. Чистить по шагам, начиная с безопасного |
| Считать остановленный volume ненужным | «Не используется» ≠ «не нужен» | После compose down база данных попадает в dangling |
Путать Untagged и Deleted | Обе строки в одном выводе | Untagged — снят тег; Deleted — удалён образ |
Удивляться <missing> в docker history | Выглядит как ошибка | Промежуточные образы существуют только на машине сборки |
Ожидать флаг dry-run у prune | Он был бы логичен | Его нет; смотреть заранее через images --filter и volume ls |
Контрольные вопросы
На понимание:
- Почему сумма
SIZEвсех образов больше, чем показываетdocker system df? - Откуда берутся образы
<none>:<none>и всегда ли их можно удалять? - Чем
Untaggedотличается отDeletedв выводеdocker rmi? - Почему удаление образа на 1.5 GB может освободить всего 40 MB?
- Что означает
<missing>в колонкеIMAGEвыводаdocker history?
На применение:
- Как найти слой, дающий основной вклад в размер образа?
- Как узнать, какой container мешает удалить образ?
- Как посмотреть, что именно удалит
docker volume prune, до его запуска?
На диагностику:
- Диск заполнен,
docker imagesпоказывает 3 GB, но Docker занимает 40 GB. Где искать остальное? - После
docker system prune -a --volumesпропали данные PostgreSQL, хотя стек был просто остановлен. Что произошло и как было бы правильно?
Краткое резюме
- Диагностика места начинается с
docker system df, а не сdocker images. - Место расходуется по четырём независимым категориям; чаще всего лидируют build cache и containers.
- Сумма
SIZEне равна занятому месту из-за дедупликации слоёв. - Образы
<none>появляются при перепривязке тега, пересборке и загрузке по digest. docker image pruneбез флагов — самая безопасная очистка: удаляет только образы без тега.-aдополнительно удаляет теговые образы без container'ов — здесь чаще всего теряют нужное.docker rmi -fпри занятом образе не освобождает место, а создаёт<none>.docker volume pruneнеобратим; остановленный стек попадает в список dangling.- У команд
pruneнет dry-run — проверять нужно заранее отдельными командами. - В Docker Engine 29 вывод
docker imagesстал древовидным, образы без тега скрыты без--all, добавлены колонкиDISK USAGEиCONTENT SIZE.
Официальные источники
| Источник | Ссылка | Что подтверждает |
|---|---|---|
| docker images reference | https://docs.docker.com/reference/cli/docker/image/ls/ | Фильтры dangling, since, label, форматирование вывода |
| docker history reference | https://docs.docker.com/reference/cli/docker/image/history/ | Формат вывода, значение <missing> |
| docker rmi reference | https://docs.docker.com/reference/cli/docker/image/rm/ | Поведение при нескольких тегах, конфликт с containers, флаг -f |
| docker image prune | https://docs.docker.com/reference/cli/docker/image/prune/ | Различие между удалением dangling и -a, фильтр until |
| docker system prune | https://docs.docker.com/reference/cli/docker/system/prune/ | Что именно удаляется, флаги -a и --volumes |
| docker system df | https://docs.docker.com/reference/cli/docker/system/df/ | Категории, колонка RECLAIMABLE, флаг -v |
| docker builder prune | https://docs.docker.com/reference/cli/docker/builder/prune/ | Очистка кэша сборки, --reserved-space (бывш. --keep-storage), фильтры |
| Docker Engine 29 release notes | https://docs.docker.com/engine/release-notes/29/ | Древовидный вывод docker images, скрытие untagged без --all, отказ от усечения имён |
Навигация
← Предыдущий материал
Вернуться к разделу
Следующий материал → Save, load, export, import
Главное оглавление