2.7. Терминология Docker
Цели
После этого материала вы сможете:
- точно употреблять термины image, container, registry, repository, tag, digest, layer, manifest;
- разобрать полное имя образа на составляющие и назвать значения по умолчанию;
- объяснить разницу между tag и digest и сказать, какой из них неизменен;
- распознавать ошибки словоупотребления, приводящие к практическим ошибкам;
- читать сообщения Docker, понимая, о каком объекте идёт речь.
Предварительные знания
- 2.1. Процессы и containers;
- 1.4. Первый container — были запущены первые containers.
Этот урок справочный. Его можно прочитать раньше остальных уроков раздела, если терминология мешает.
Ключевые термины
Весь урок — о ключевых терминах, поэтому таблица приводится в теоретической части.
Теория
Почему точность терминов важна
Неточное словоупотребление в Docker приводит не к недопониманию, а к ошибкам в работе. Три примера.
«Скачай образ myapp». Не указан тег — будет загружен latest, который может оказаться не тем, что имелось в виду. latest — не «последний», а просто тег по умолчанию.
«Container сломался, пересоздай его». Если проблема в образе, пересоздание container ничего не изменит: он будет создан из того же образа.
«Мы задеплоили версию 1.4.2». Если тег 1.4.2 был перезаписан, в production может работать не тот код, что тестировался. Гарантию даёт только digest.
Основные термины
| Термин | Точное определение | Частая неточность |
|---|---|---|
| image | Неизменяемый шаблон: упорядоченный набор слоёв файловой системы плюс конфигурация | Путать с container |
| container | Экземпляр образа: его слои плюс writable layer, плюс namespaces, cgroup и запущенный (или завершившийся) процесс | Путать с образом; считать «маленькой VM» |
| layer | Неизменяемый набор изменений файловой системы, идентифицируемый по хэшу содержимого | Считать, что слой = инструкция Dockerfile (не все инструкции создают слой) |
| manifest | JSON-документ, перечисляющий слои образа и ссылку на его конфигурацию | Путать с config |
| image config | JSON-документ с параметрами запуска: Cmd, Entrypoint, Env, WorkingDir, User, ExposedPorts | — |
| registry | Сервер, хранящий образы. Например, Docker Hub, GHCR, GitLab Registry | Путать с repository |
| repository | Именованная коллекция версий одного образа внутри registry | Называть «образом» |
| tag | Изменяемая метка, указывающая на конкретную версию в repository | Считать неизменяемым идентификатором |
| digest | Неизменяемый идентификатор содержимого вида sha256:... | Путать с image ID |
| image ID | Локальный идентификатор образа — digest его конфигурации | Путать с digest манифеста |
| volume | Управляемое Docker хранилище данных вне слоёв образа | Путать с bind mount |
| bind mount | Монтирование существующего каталога или файла host внутрь container | Называть «volume» |
Аналогия и её границы
Соотношение образа и container часто объясняют через классы и объекты:
| Программирование | Docker |
|---|---|
| Класс | image |
| Объект | container |
| Создание объекта | docker create / docker run |
| Несколько объектов одного класса | Несколько containers из одного образа |
Аналогия работает для главного: образ — шаблон, container — экземпляр, из одного шаблона можно создать много экземпляров.
Где она перестаёт работать: у образа есть внутренняя структура из слоёв, которые переиспользуются между разными образами. Двух «классов» с общими частями в программировании обычно не бывает, а два образа на общей базе разделяют слои физически — на диске они хранятся один раз.
Полное имя образа
Строка, которую вы передаёте в docker pull, имеет строгую структуру:
[registry[:port]/][namespace/]repository[:tag][@digest]
Разберём на примерах.
nginx
Раскрывается как docker.io/library/nginx:latest:
| Часть | Значение | Откуда взялось |
|---|---|---|
| registry | docker.io | По умолчанию |
| namespace | library | Namespace официальных образов Docker Hub |
| repository | nginx | Указано явно |
| tag | latest | По умолчанию |
python:3.13-slim
Раскрывается как docker.io/library/python:3.13-slim.
ghcr.io/astral-sh/uv:0.12.0
| Часть | Значение |
|---|---|
| registry | ghcr.io (GitHub Container Registry) |
| namespace | astral-sh |
| repository | uv |
| tag | 0.12.0 |
registry.example.com:5000/team/api@sha256:bf503bb2243c5aad...
| Часть | Значение |
|---|---|
| registry | registry.example.com:5000 |
| namespace | team |
| repository | api |
| digest | sha256:9f8e... |
Правило распознавания registry. Docker считает первую часть адресом registry, если она содержит точку или двоеточие либо равна localhost. Иначе это namespace на Docker Hub.
Отсюда практическое следствие: myregistry/myapp будет истолковано как образ myapp пользователя myregistry на Docker Hub, а не как обращение к серверу myregistry. Для локального сервера нужно localhost:5000/myapp.
Tag против digest
Различие, из которого следует большинство проблем с воспроизводимостью развёртывания.
| tag | digest | |
|---|---|---|
| Вид | 1.4.2, latest, stable | sha256:bf503bb2... |
| Изменяемость | Изменяем: можно перепривязать к другому образу | Неизменяем: вычисляется из содержимого |
| Что идентифицирует | «Версию» в понимании автора образа | Конкретное содержимое |
| Гарантия | Никакой | Полная: тот же digest = то же содержимое |
| Читаемость | Высокая | Низкая |
Digest — это криптографический хэш манифеста образа. Изменение любого байта содержимого меняет digest. Поэтому нельзя подменить образ, не изменив digest.
Tag — просто указатель. Автор образа может в любой момент перепривязать 1.4.2 к другому набору слоёв. Часто это делают для доставки исправлений безопасности — намерение хорошее, но факт остаётся: под одним тегом в разное время лежат разные образы.
Практический вывод для раздела 14: в разработке используйте теги, в production развёртывайте по digest либо гарантируйте неизменяемость тегов средствами registry.
latest — не «последний»
Распространённое заблуждение. latest — это просто тег по умолчанию, применяемый когда тег не указан. Никакой особой семантики у него нет:
- он не указывает автоматически на самую свежую версию;
- он не обновляется сам;
- репозиторий может вообще не иметь тега
latest; - автор может привязать
latestк чему угодно, включая старую версию.
Единственное, что делает Docker: подставляет latest, если тег не указан.
Чем Image ID отличается от digest
Оба выглядят как sha256:..., но идентифицируют разное:
| Image ID | Digest (manifest digest) | |
|---|---|---|
| Хэш чего | Файла конфигурации образа | Файла манифеста |
| Где виден | docker images, docker inspect | docker pull, docker images --digests |
| Область | Локальная машина | Registry, глобально |
| Совпадает на разных машинах | Да, если образ идентичен | Да |
| Используется для развёртывания | Нет | Да |
Для закрепления версии в production используется digest манифеста, а не image ID:
myapp@sha256:bf503bb2243c...
Repository и registry
Пара терминов, которую путают чаще всего.
Registry — сервер. Docker Hub, ghcr.io, ваш собственный registry.
Repository — коллекция версий одного образа внутри registry. Например, library/python — repository внутри Docker Hub, содержащий сотни тегов.
Аналогия с Git: registry — это GitHub, repository — конкретный репозиторий, теги — Git-теги внутри него.
Ошибка «загрузи образ в registry myapp» смешивает уровни: myapp — это repository, а registry — сервер, на котором он размещён.
Внутренний механизм
Что физически лежит в registry
Registry хранит три вида объектов, связанных ссылками по хэшу:
tag "1.4.2" ──────► manifest (sha256:9f8e...)
│
├──► config blob (sha256:a1b2...)
│ {"Cmd": [...], "Env": [...], "User": "app"}
│
└──► layer blobs
sha256:c3d4... (базовый слой)
sha256:e5f6... (зависимости)
sha256:0718... (код приложения)
Тег — единственный изменяемый элемент этой схемы. Всё остальное адресуется по содержимому: изменение любого блоба меняет его хэш, что меняет манифест, что меняет digest образа.
Для multi-platform образов добавляется ещё один уровень — image index, перечисляющий манифесты под разные архитектуры. Именно поэтому docker pull python:3.13-slim на amd64 и на arm64 даёт разные образы под одним тегом.
Подробно структура разбирается в разделе 03.
Команды и примеры
Разбор полного имени
docker pull nginx
docker images nginx --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}'
REPOSITORY TAG IMAGE ID
nginx latest a1b2c3d4e5f6
Полное имя видно в inspect:
docker inspect nginx --format '{{index .RepoTags 0}}'
docker inspect nginx --format '{{index .RepoDigests 0}}'
nginx:latest
nginx@sha256:bf503bb2243c5aad0aa951544dd60d165f992646441d35dea90893703fc26251
Первая строка — тег, вторая — digest.
Tag и digest на практике
docker images --digests python
REPOSITORY TAG DIGEST IMAGE ID
python 3.13-slim sha256:bf503bb2243c5aad0aa951544dd60d165f992646441d35dea90893703fc26251 f1e2d3c4b5a6
Обратите внимание: DIGEST и IMAGE ID — разные значения. Первый идентифицирует манифест в registry, второй — конфигурацию локально.
Загрузка по digest:
docker pull python@sha256:bf503bb2243c5aad0aa951544dd60d165f992646441d35dea90893703fc26251
Замените digest на реальный из вывода предыдущей команды. Такая загрузка гарантирует конкретное содержимое независимо от того, что произошло с тегами.
Один образ, несколько тегов
docker pull alpine:3.21
docker tag alpine:3.21 myalpine:test
docker tag alpine:3.21 myalpine:v1
docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}' | grep -E 'alpine'
REPOSITORY TAG IMAGE ID
alpine 3.21 b0c1d2e3f4a5
myalpine test b0c1d2e3f4a5
myalpine v1 b0c1d2e3f4a5
Три строки, один Image ID. Это один образ с тремя именами — не три копии. Место на диске занято однократно.
Проверим:
docker system df --format 'table {{.Type}}\t{{.TotalCount}}\t{{.Size}}' | head -3
Удаление одного тега не удаляет образ, пока есть другие:
docker rmi myalpine:test
docker images myalpine
Untagged: myalpine:test
REPOSITORY TAG IMAGE ID CREATED SIZE
myalpine v1 b0c1d2e3f4a5 3 weeks ago 8.31MB
Слово Untagged в выводе — не Deleted. Образ остался.
Один образ, несколько containers
docker run -d --name c1 alpine sleep 300
docker run -d --name c2 alpine sleep 300
docker run -d --name c3 alpine sleep 300
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.ID}}'
NAMES IMAGE CONTAINER ID
c3 alpine d4e5f6a7b8c9
c2 alpine c3d4e5f6a7b8
c1 alpine b2c3d4e5f6a7
Три разных container из одного образа. У каждого свой ID, свой writable layer, свои namespaces.
Докажем независимость writable layers:
docker exec c1 sh -c 'echo "данные c1" > /tmp/marker'
docker exec c2 sh -c 'echo "данные c2" > /tmp/marker'
docker exec c3 ls /tmp/
Пусто. Файл, созданный в c1, не виден в c2 и c3 — у каждого свой слой записи.
docker exec c1 cat /tmp/marker
docker exec c2 cat /tmp/marker
данные c1
данные c2
Registry в имени
docker pull ghcr.io/astral-sh/uv:latest
docker images ghcr.io/astral-sh/uv --format '{{.Repository}}:{{.Tag}}'
ghcr.io/astral-sh/uv:latest
Имя registry сохраняется в имени образа — Docker не «забывает», откуда взят образ.
Сравним с образом Docker Hub:
docker images alpine --format '{{.Repository}}:{{.Tag}}'
alpine:latest
Префикс docker.io/library/ не отображается, потому что это значение по умолчанию. Но он существует:
docker inspect alpine --format '{{index .RepoDigests 0}}'
alpine@sha256:...
Уборка
docker rm -f c1 c2 c3
docker rmi myalpine:v1 nginx ghcr.io/astral-sh/uv:latest 2>/dev/null || true
Практическое упражнение
Задание. Напишите скрипт parse-image-name.sh <image-reference>, который разбирает полное имя образа на составляющие и показывает подставленные значения по умолчанию.
Скрипт должен корректно обрабатывать:
nginxpython:3.13-slimghcr.io/astral-sh/uv:0.12.0localhost:5000/myapp:devmyapp@sha256:abc123...registry.example.com:5000/team/api:v2
Для каждого случая вывести: registry, namespace, repository, tag, digest — с пометкой, что подставлено по умолчанию.
Подсказки
Подсказка 1
Признак registry: первая часть до / содержит точку или двоеточие, либо равна localhost.
Подсказка 2
Digest отделяется символом @ и разбирается первым — до всего остального.
Подсказка 3
Двоеточие может встречаться и в порту registry (localhost:5000), и в теге. Разбирайте тег только в последней части пути.
Решение
Сначала выполните задание самостоятельно.
Показать решение
#!/usr/bin/env bash
# parse-image-name.sh — разбор полного имени образа.
set -euo pipefail
REF="${1:?Использование: $0 <image-reference>}"
registry=""; namespace=""; repository=""; tag=""; digest=""
reg_default=""; ns_default=""; tag_default=""
# 1. Digest
rest="$REF"
if [[ "$rest" == *"@"* ]]; then
digest="${rest#*@}"
rest="${rest%@*}"
fi
# 2. Registry: первая часть содержит '.' или ':' либо равна localhost
first="${rest%%/*}"
if [[ "$rest" == */* ]] && { [[ "$first" == *.* ]] || [[ "$first" == *:* ]] || [[ "$first" == "localhost" ]]; }; then
registry="$first"
rest="${rest#*/}"
else
registry="docker.io"
reg_default=" (по умолчанию)"
fi
# 3. Tag — только в последнем сегменте
last="${rest##*/}"
if [[ "$last" == *:* ]]; then
tag="${last##*:}"
rest="${rest%:*}"
elif [ -z "$digest" ]; then
tag="latest"
tag_default=" (по умолчанию)"
fi
# 4. Namespace и repository
if [[ "$rest" == */* ]]; then
namespace="${rest%/*}"
repository="${rest##*/}"
else
repository="$rest"
if [ "$registry" = "docker.io" ]; then
namespace="library"
ns_default=" (по умолчанию, официальный образ)"
else
namespace="—"
fi
fi
printf 'Исходная строка: %s\n\n' "$REF"
printf ' %-12s %s%s\n' "registry:" "$registry" "$reg_default"
printf ' %-12s %s%s\n' "namespace:" "$namespace" "$ns_default"
printf ' %-12s %s\n' "repository:" "$repository"
[ -n "$tag" ] && printf ' %-12s %s%s\n' "tag:" "$tag" "$tag_default"
[ -n "$digest" ] && printf ' %-12s %s\n' "digest:" "$digest"
echo
if [ -n "$digest" ]; then
echo " Ссылка по digest: содержимое зафиксировано."
else
echo " Ссылка по тегу: содержимое может измениться при перепривязке тега."
fi
Проверка:
chmod +x parse-image-name.sh
for ref in nginx python:3.13-slim ghcr.io/astral-sh/uv:0.12.0 \
localhost:5000/myapp:dev registry.example.com:5000/team/api:v2; do
./parse-image-name.sh "$ref"
echo "---"
done
Ожидаемый вывод для двух случаев:
Исходная строка: nginx
registry: docker.io (по умолчанию)
namespace: library (по умолчанию, официальный образ)
repository: nginx
tag: latest (по умолчанию)
Ссылка по тегу: содержимое может измениться при перепривязке тега.
---
Исходная строка: registry.example.com:5000/team/api:v2
registry: registry.example.com:5000
namespace: team
repository: api
tag: v2
Ссылка по тегу: содержимое может измениться при перепривязке тега.
Обратите внимание на разбор localhost:5000/myapp:dev: двоеточие встречается дважды, и правило «тег только в последнем сегменте» позволяет не спутать порт с тегом.
Проверка результата
Ответьте на пять вопросов по своей системе:
# 1. Сколько образов и сколько containers?
docker images -q | wc -l
docker ps -aq | wc -l
# 2. Есть ли образы с несколькими тегами (одинаковый ID)?
docker images --format '{{.ID}}' | sort | uniq -d
# 3. Полное имя произвольного образа с digest
docker inspect alpine --format '{{index .RepoDigests 0}}' 2>/dev/null
Если пункт 2 что-то вывел — у вас есть образы с несколькими именами. Объясните, почему они занимают место однократно.
Типичные ошибки
| Ошибка | Причина | Исправление |
|---|---|---|
| «Пересоздай container» при проблеме в образе | Смешаны образ и container | Пересобрать образ; container создаётся из него заново |
Расчёт на latest как на «самую свежую версию» | Название вводит в заблуждение | latest — тег по умолчанию без особой семантики. Указывать версию явно |
| Развёртывание в production по тегу | Кажется, что тег однозначен | Развёртывать по digest либо включить неизменяемость тегов в registry |
myregistry/myapp для локального registry | Часть без точки трактуется как namespace на Docker Hub | Использовать localhost:5000/myapp |
| Путаница Image ID и digest | Оба вида sha256:... | Image ID — хэш конфигурации локально; digest — хэш манифеста в registry |
| «Три тега — три образа, надо чистить место» | Ожидание, что каждое имя занимает место | Один Image ID означает один образ; место занято однократно |
docker rmi не удалил образ | Есть другие теги или containers на него ссылаются | Читать вывод: Untagged ≠ Deleted |
«Загрузить в registry myapp» | Смешаны registry и repository | Registry — сервер, repository — коллекция версий на нём |
Контрольные вопросы
На понимание:
- Чем image отличается от container? Приведите точное определение каждого.
- Что раскрывается из имени
nginxпри загрузке и почему? - Почему tag не является надёжным идентификатором версии?
- Чем digest манифеста отличается от Image ID?
- Почему три тега на один образ занимают место однократно?
На применение:
- Как узнать digest локально загруженного образа?
- Как загрузить образ, гарантированно получив конкретное содержимое?
- Как обратиться к образу в локальном registry на порту 5000?
На диагностику:
docker rmi myapp:v1вывелUntagged, ноdocker imagesпоказывает образ. Почему?- В production и в staging развёрнут «один и тот же тег
v2.1», но поведение различается. Как это возможно и как проверить?
Краткое резюме
- Image — неизменяемый шаблон; container — его экземпляр с writable layer и процессом.
- Из одного образа создаётся сколько угодно containers, каждый со своим слоем записи.
- Полное имя:
[registry/][namespace/]repository[:tag][@digest]. - По умолчанию подставляются
docker.io,library(для официальных образов) иlatest. - Первая часть считается registry, если содержит точку, двоеточие или равна
localhost. latest— тег по умолчанию, а не указатель на свежую версию.- Tag изменяем, digest неизменен: digest вычисляется из содержимого.
- Image ID — хэш конфигурации локально; digest — хэш манифеста в registry.
- Registry — сервер; repository — коллекция версий образа внутри него.
- Несколько тегов на один образ не дублируют данные на диске.
Официальные источники
| Источник | Ссылка | Что подтверждает |
|---|---|---|
| Docker overview | https://docs.docker.com/get-started/docker-overview/ | Определения image, container, registry, repository |
| docker pull reference | https://docs.docker.com/reference/cli/docker/image/pull/ | Структура имени образа, значения по умолчанию, загрузка по digest |
| docker tag reference | https://docs.docker.com/reference/cli/docker/image/tag/ | Правила именования, определение registry по имени хоста |
| docker images reference | https://docs.docker.com/reference/cli/docker/image/ls/ | Колонки вывода, флаг --digests |
| OCI Image Specification | https://github.com/opencontainers/image-spec | Манифест, config, слои, image index для multi-platform |
| OCI Distribution Specification | https://github.com/opencontainers/distribution-spec | Как теги и digest адресуют объекты в registry |
Навигация
← Предыдущий материал
Вернуться к разделу
Следующий материал → Практические задания
Главное оглавление