Главная/Работа с Images/Урок

3.4. Управление images

Цели

После этого материала вы сможете:

  • находить образ, ответственный за расход дискового пространства, и объяснять его размер по слоям;
  • читать docker history и связывать слои с инструкциями Dockerfile;
  • фильтровать и форматировать вывод docker images под конкретную задачу;
  • объяснить, откуда берутся dangling images и почему они накапливаются;
  • безопасно удалять образы, отличая неиспользуемые от нужных;
  • объяснить, что именно удаляет каждый вариант docker system prune, и выполнить предварительный просмотр;
  • учитывать различия вывода в Docker Engine 29 с containerd image store.

Предварительные знания

Ключевые термины

ТерминОбъяснение
dangling imageОбраз без тега, на который никто не ссылается: <none>:<none>
unused imageОбраз с тегом, не используемый ни одним container
intermediate imageПромежуточный образ, созданный при сборке (в классическом builder)
build cacheКэш BuildKit, хранящийся отдельно от образов
reclaimableОбъём, который можно освободить удалением неиспользуемого
filterУсловие отбора объектов в командах Docker: --filter key=value

Теория

Три источника расхода места

Прежде чем что-то удалять, нужно понять, что именно занимает диск. Docker расходует место по четырём независимым категориям:

КатегорияЧто этоРастёт когда
ImagesСлои образовЗагружаете и собираете образы
ContainersWritable layersContainers пишут данные и не удаляются
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) вместе с размерами слоёв.

Читается снизу вверх: последняя строка — базовый слой, первая — последняя инструкция.

text
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
КолонкиSIZEDISK 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 дают разные числа

Различие не косметическое, и на нём легко ошибиться в четыре раза.

bash
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
text
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

  1. Docker проверяет, ссылаются ли на образ существующие containers (включая остановленные). Если да — отказ.
  2. Удаляется указанный тег. Если у образа остались другие теги — на этом всё, вывод Untagged.
  3. Если тегов не осталось, удаляется запись об образе, вывод Deleted.
  4. Для каждого слоя проверяется, ссылаются ли на него другие образы. Слои без ссылок удаляются с диска.

Шаг 4 объясняет, почему удаление образа на 1.5 GB может освободить 40 MB: остальные слои переиспользуются.

Шаг 2 объясняет разницу между Untagged и Deleted в выводе — их постоянно путают.

Ссылки от остановленных containers

Остановленный container продолжает ссылаться на свой образ. Отсюда типичная ситуация:

text
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>. Это ухудшает ситуацию, а не улучшает.


Команды и примеры

Начинать с общей картины

bash
docker system df
text
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, а не образы.

Детализация:

bash
docker system df -v

Выводит построчно каждый образ, container, volume и запись кэша. На загруженной машине вывод длинный — направляйте в файл или в less.

Анализ размера конкретного образа

bash
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
text
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 обойдётся дороже, чем кажется по размеру исходников.

Отсортируем слои по убыванию размера — это самый быстрый способ найти виновника раздувания:

bash
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}')
"
text
Слоёв с данными: 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.

Фильтры и форматирование

bash
# только образы конкретного 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

Полезные форматы:

bash
# компактная таблица с датой
docker images --format 'table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}'

# самые большие образы
docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}' | sort -hr | head -10
text
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

bash
docker tag sizedemo:1 sizedemo:copy
docker rmi sizedemo:copy
text
Untagged: sizedemo:copy

Только Untagged — образ остался, у него есть другой тег.

bash
docker rmi sizedemo:1
text
Untagged: sizedemo:1
Deleted: sha256:7a6b5c4d3e2f19081726354453627180...
Deleted: sha256:1e0f9a8b7c6d5e4f3a2b190877665544...

Теперь и Untagged, и Deleted — тегов не осталось, образ и его уникальные слои удалены. Слои базового образа не удалены: на них ссылается python:3.13-slim.

Образ занят container

bash
docker build -q -t sizedemo:2 . > /dev/null
docker run -d --name busy sizedemo:2 sleep 300 > /dev/null
docker rmi sizedemo:2
text
Error response from daemon: conflict: unable to remove repository reference
"sizedemo:2" (must force) - container a1b2c3d4e5f6 is using its referenced image 8f7e6d5c4b3a

Найти виновника:

bash
docker ps -a --filter ancestor=sizedemo:2 --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}'
text
CONTAINER ID   NAMES   STATUS
a1b2c3d4e5f6   busy    Up 12 seconds

Правильное решение — удалить container:

bash
docker rm -f busy
docker rmi sizedemo:2
text
Untagged: sizedemo:2
Deleted: sha256:8f7e6d5c4b3a...

Не используйте docker rmi -f в этой ситуации. Флаг снимет тег, но образ останется на диске привязанным к container и превратится в <none>. Место не освободится, а разобраться станет труднее.

Накопление dangling images

Воспроизведём главный источник мусора — пересборку с одним тегом:

bash
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}}'
text
IMAGE ID       SIZE      CREATED
3e2f1908a7b6   168MB     10 seconds ago
2f1908a7b6c5   168MB     18 seconds ago

Две сборки потеряли тег. Колонка SIZE показывает виртуальный размер — фактически уникальных данных там килобайты, но записи копятся, и при активной разработке их становятся сотни.

Сколько места они реально занимают:

bash
docker system df -v 2>/dev/null | grep -A5 'Images space usage' | head -6

Безопасное удаление именно dangling:

bash
docker image prune
text
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. Поэтому смотреть нужно заранее, отдельными командами.

bash
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 pruneVolumes, не примонтированные ни к одному 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 с базой данных, потому что она была не запущена и потому «не используется».

Безопасная последовательность:

bash
# 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

bash
docker builder prune -f --filter 'until=168h'

Удаляет записи кэша старше недели. Флаг --filter until= доступен и у image prune, и у system prune — это самый практичный способ чистить регулярно, не теряя актуального.

Ограничить рост кэша заранее:

bash
docker builder prune -f --reserved-space 10GB

Флаг назывался --keep-storage; в Docker 29 старое имя ещё принимается, но печатает Flag --keep-storage has been deprecated.

Постоянное ограничение задаётся в конфигурации BuildKit; подробно — в разделе 13.

Уборка после урока

bash
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.

Скрипт должен:

  1. Показать общую картину по четырём категориям с указанием освобождаемого объёма.
  2. Перечислить пять самых больших образов.
  3. Отдельно показать dangling images: количество и объём.
  4. Перечислить образы с тегами, не используемые ни одним container, — то есть то, что удалит prune -a.
  5. Показать volumes, не примонтированные ни к одному container, с явным предупреждением о невосстановимости.
  6. Вывести рекомендованную последовательность команд очистки, отсортированную по возрастанию риска.

Скрипт не должен ничего удалять.

Подсказки

Подсказка 1

docker system df --format '{{json .}}' даёт структурированный вывод, который проще разбирать, чем таблицу.

Подсказка 2

Образ используется, если существует хотя бы один container (в любом состоянии) с этим ancestor:

bash
docker ps -aq --filter "ancestor=<id>"
Подсказка 3

Volumes без container'ов отбираются фильтром dangling=true:

bash
docker volume ls --filter dangling=true -q

Решение

Сначала выполните задание самостоятельно.

Показать решение
bash
#!/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

Запуск:

bash
chmod +x docker-space.sh
./docker-space.sh

Ожидаемый вывод (сокращённо):

text
═══ 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 не восстанавливаются никак.

Проверка результата

bash
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

Контрольные вопросы

На понимание:

  1. Почему сумма SIZE всех образов больше, чем показывает docker system df?
  2. Откуда берутся образы <none>:<none> и всегда ли их можно удалять?
  3. Чем Untagged отличается от Deleted в выводе docker rmi?
  4. Почему удаление образа на 1.5 GB может освободить всего 40 MB?
  5. Что означает <missing> в колонке IMAGE вывода docker history?

На применение:

  1. Как найти слой, дающий основной вклад в размер образа?
  2. Как узнать, какой container мешает удалить образ?
  3. Как посмотреть, что именно удалит docker volume prune, до его запуска?

На диагностику:

  1. Диск заполнен, docker images показывает 3 GB, но Docker занимает 40 GB. Где искать остальное?
  2. После docker system prune -a --volumes пропали данные PostgreSQL, хотя стек был просто остановлен. Что произошло и как было бы правильно?

Краткое резюме

  1. Диагностика места начинается с docker system df, а не с docker images.
  2. Место расходуется по четырём независимым категориям; чаще всего лидируют build cache и containers.
  3. Сумма SIZE не равна занятому месту из-за дедупликации слоёв.
  4. Образы <none> появляются при перепривязке тега, пересборке и загрузке по digest.
  5. docker image prune без флагов — самая безопасная очистка: удаляет только образы без тега.
  6. -a дополнительно удаляет теговые образы без container'ов — здесь чаще всего теряют нужное.
  7. docker rmi -f при занятом образе не освобождает место, а создаёт <none>.
  8. docker volume prune необратим; остановленный стек попадает в список dangling.
  9. У команд prune нет dry-run — проверять нужно заранее отдельными командами.
  10. В Docker Engine 29 вывод docker images стал древовидным, образы без тега скрыты без --all, добавлены колонки DISK USAGE и CONTENT SIZE.

Официальные источники

ИсточникСсылкаЧто подтверждает
docker images referencehttps://docs.docker.com/reference/cli/docker/image/ls/Фильтры dangling, since, label, форматирование вывода
docker history referencehttps://docs.docker.com/reference/cli/docker/image/history/Формат вывода, значение <missing>
docker rmi referencehttps://docs.docker.com/reference/cli/docker/image/rm/Поведение при нескольких тегах, конфликт с containers, флаг -f
docker image prunehttps://docs.docker.com/reference/cli/docker/image/prune/Различие между удалением dangling и -a, фильтр until
docker system prunehttps://docs.docker.com/reference/cli/docker/system/prune/Что именно удаляется, флаги -a и --volumes
docker system dfhttps://docs.docker.com/reference/cli/docker/system/df/Категории, колонка RECLAIMABLE, флаг -v
docker builder prunehttps://docs.docker.com/reference/cli/docker/builder/prune/Очистка кэша сборки, --reserved-space (бывш. --keep-storage), фильтры
Docker Engine 29 release noteshttps://docs.docker.com/engine/release-notes/29/Древовидный вывод docker images, скрытие untagged без --all, отказ от усечения имён

Навигация

← Предыдущий материал
Вернуться к разделу
Следующий материал → Save, load, export, import
Главное оглавление

Markdown на GitHub ↗