Главная/Основы Containerization/Урок

2.7. Терминология Docker

Цели

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

  • точно употреблять термины image, container, registry, repository, tag, digest, layer, manifest;
  • разобрать полное имя образа на составляющие и назвать значения по умолчанию;
  • объяснить разницу между tag и digest и сказать, какой из них неизменен;
  • распознавать ошибки словоупотребления, приводящие к практическим ошибкам;
  • читать сообщения Docker, понимая, о каком объекте идёт речь.

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

Этот урок справочный. Его можно прочитать раньше остальных уроков раздела, если терминология мешает.

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

Весь урок — о ключевых терминах, поэтому таблица приводится в теоретической части.


Теория

Почему точность терминов важна

Неточное словоупотребление в Docker приводит не к недопониманию, а к ошибкам в работе. Три примера.

«Скачай образ myapp». Не указан тег — будет загружен latest, который может оказаться не тем, что имелось в виду. latest — не «последний», а просто тег по умолчанию.

«Container сломался, пересоздай его». Если проблема в образе, пересоздание container ничего не изменит: он будет создан из того же образа.

«Мы задеплоили версию 1.4.2». Если тег 1.4.2 был перезаписан, в production может работать не тот код, что тестировался. Гарантию даёт только digest.

Основные термины

ТерминТочное определениеЧастая неточность
imageНеизменяемый шаблон: упорядоченный набор слоёв файловой системы плюс конфигурацияПутать с container
containerЭкземпляр образа: его слои плюс writable layer, плюс namespaces, cgroup и запущенный (или завершившийся) процессПутать с образом; считать «маленькой VM»
layerНеизменяемый набор изменений файловой системы, идентифицируемый по хэшу содержимогоСчитать, что слой = инструкция Dockerfile (не все инструкции создают слой)
manifestJSON-документ, перечисляющий слои образа и ссылку на его конфигурациюПутать с config
image configJSON-документ с параметрами запуска: 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, имеет строгую структуру:

text
[registry[:port]/][namespace/]repository[:tag][@digest]

Разберём на примерах.

text
nginx

Раскрывается как docker.io/library/nginx:latest:

ЧастьЗначениеОткуда взялось
registrydocker.ioПо умолчанию
namespacelibraryNamespace официальных образов Docker Hub
repositorynginxУказано явно
taglatestПо умолчанию
text
python:3.13-slim

Раскрывается как docker.io/library/python:3.13-slim.

text
ghcr.io/astral-sh/uv:0.12.0
ЧастьЗначение
registryghcr.io (GitHub Container Registry)
namespaceastral-sh
repositoryuv
tag0.12.0
text
registry.example.com:5000/team/api@sha256:bf503bb2243c5aad...
ЧастьЗначение
registryregistry.example.com:5000
namespaceteam
repositoryapi
digestsha256:9f8e...

Правило распознавания registry. Docker считает первую часть адресом registry, если она содержит точку или двоеточие либо равна localhost. Иначе это namespace на Docker Hub.

Отсюда практическое следствие: myregistry/myapp будет истолковано как образ myapp пользователя myregistry на Docker Hub, а не как обращение к серверу myregistry. Для локального сервера нужно localhost:5000/myapp.

Tag против digest

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

tagdigest
Вид1.4.2, latest, stablesha256:bf503bb2...
ИзменяемостьИзменяем: можно перепривязать к другому образуНеизменяем: вычисляется из содержимого
Что идентифицирует«Версию» в понимании автора образаКонкретное содержимое
ГарантияНикакойПолная: тот же digest = то же содержимое
ЧитаемостьВысокаяНизкая

Digest — это криптографический хэш манифеста образа. Изменение любого байта содержимого меняет digest. Поэтому нельзя подменить образ, не изменив digest.

Tag — просто указатель. Автор образа может в любой момент перепривязать 1.4.2 к другому набору слоёв. Часто это делают для доставки исправлений безопасности — намерение хорошее, но факт остаётся: под одним тегом в разное время лежат разные образы.

Практический вывод для раздела 14: в разработке используйте теги, в production развёртывайте по digest либо гарантируйте неизменяемость тегов средствами registry.

latest — не «последний»

Распространённое заблуждение. latest — это просто тег по умолчанию, применяемый когда тег не указан. Никакой особой семантики у него нет:

  • он не указывает автоматически на самую свежую версию;
  • он не обновляется сам;
  • репозиторий может вообще не иметь тега latest;
  • автор может привязать latest к чему угодно, включая старую версию.

Единственное, что делает Docker: подставляет latest, если тег не указан.

Чем Image ID отличается от digest

Оба выглядят как sha256:..., но идентифицируют разное:

Image IDDigest (manifest digest)
Хэш чегоФайла конфигурации образаФайла манифеста
Где виденdocker images, docker inspectdocker pull, docker images --digests
ОбластьЛокальная машинаRegistry, глобально
Совпадает на разных машинахДа, если образ идентиченДа
Используется для развёртыванияНетДа

Для закрепления версии в production используется digest манифеста, а не image ID:

text
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 хранит три вида объектов, связанных ссылками по хэшу:

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


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

Разбор полного имени

bash
docker pull nginx
docker images nginx --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}'
text
REPOSITORY   TAG       IMAGE ID
nginx        latest    a1b2c3d4e5f6

Полное имя видно в inspect:

bash
docker inspect nginx --format '{{index .RepoTags 0}}'
docker inspect nginx --format '{{index .RepoDigests 0}}'
text
nginx:latest
nginx@sha256:bf503bb2243c5aad0aa951544dd60d165f992646441d35dea90893703fc26251

Первая строка — тег, вторая — digest.

Tag и digest на практике

bash
docker images --digests python
text
REPOSITORY   TAG         DIGEST                                                                    IMAGE ID
python       3.13-slim   sha256:bf503bb2243c5aad0aa951544dd60d165f992646441d35dea90893703fc26251   f1e2d3c4b5a6

Обратите внимание: DIGEST и IMAGE ID — разные значения. Первый идентифицирует манифест в registry, второй — конфигурацию локально.

Загрузка по digest:

bash
docker pull python@sha256:bf503bb2243c5aad0aa951544dd60d165f992646441d35dea90893703fc26251

Замените digest на реальный из вывода предыдущей команды. Такая загрузка гарантирует конкретное содержимое независимо от того, что произошло с тегами.

Один образ, несколько тегов

bash
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'
text
REPOSITORY   TAG       IMAGE ID
alpine       3.21      b0c1d2e3f4a5
myalpine     test      b0c1d2e3f4a5
myalpine     v1        b0c1d2e3f4a5

Три строки, один Image ID. Это один образ с тремя именами — не три копии. Место на диске занято однократно.

Проверим:

bash
docker system df --format 'table {{.Type}}\t{{.TotalCount}}\t{{.Size}}' | head -3

Удаление одного тега не удаляет образ, пока есть другие:

bash
docker rmi myalpine:test
docker images myalpine
text
Untagged: myalpine:test
text
REPOSITORY   TAG   IMAGE ID       CREATED       SIZE
myalpine     v1    b0c1d2e3f4a5   3 weeks ago   8.31MB

Слово Untagged в выводе — не Deleted. Образ остался.

Один образ, несколько containers

bash
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}}'
text
NAMES   IMAGE    CONTAINER ID
c3      alpine   d4e5f6a7b8c9
c2      alpine   c3d4e5f6a7b8
c1      alpine   b2c3d4e5f6a7

Три разных container из одного образа. У каждого свой ID, свой writable layer, свои namespaces.

Докажем независимость writable layers:

bash
docker exec c1 sh -c 'echo "данные c1" > /tmp/marker'
docker exec c2 sh -c 'echo "данные c2" > /tmp/marker'
docker exec c3 ls /tmp/
text

Пусто. Файл, созданный в c1, не виден в c2 и c3 — у каждого свой слой записи.

bash
docker exec c1 cat /tmp/marker
docker exec c2 cat /tmp/marker
text
данные c1
данные c2

Registry в имени

bash
docker pull ghcr.io/astral-sh/uv:latest
docker images ghcr.io/astral-sh/uv --format '{{.Repository}}:{{.Tag}}'
text
ghcr.io/astral-sh/uv:latest

Имя registry сохраняется в имени образа — Docker не «забывает», откуда взят образ.

Сравним с образом Docker Hub:

bash
docker images alpine --format '{{.Repository}}:{{.Tag}}'
text
alpine:latest

Префикс docker.io/library/ не отображается, потому что это значение по умолчанию. Но он существует:

bash
docker inspect alpine --format '{{index .RepoDigests 0}}'
text
alpine@sha256:...

Уборка

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

Скрипт должен корректно обрабатывать:

  • nginx
  • python:3.13-slim
  • ghcr.io/astral-sh/uv:0.12.0
  • localhost:5000/myapp:dev
  • myapp@sha256:abc123...
  • registry.example.com:5000/team/api:v2

Для каждого случая вывести: registry, namespace, repository, tag, digest — с пометкой, что подставлено по умолчанию.

Подсказки

Подсказка 1

Признак registry: первая часть до / содержит точку или двоеточие, либо равна localhost.

Подсказка 2

Digest отделяется символом @ и разбирается первым — до всего остального.

Подсказка 3

Двоеточие может встречаться и в порту registry (localhost:5000), и в теге. Разбирайте тег только в последней части пути.

Решение

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

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

Проверка:

bash
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

Ожидаемый вывод для двух случаев:

text
Исходная строка: 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: двоеточие встречается дважды, и правило «тег только в последнем сегменте» позволяет не спутать порт с тегом.

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

Ответьте на пять вопросов по своей системе:

bash
# 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 на него ссылаютсяЧитать вывод: UntaggedDeleted
«Загрузить в registry myapp»Смешаны registry и repositoryRegistry — сервер, repository — коллекция версий на нём

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

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

  1. Чем image отличается от container? Приведите точное определение каждого.
  2. Что раскрывается из имени nginx при загрузке и почему?
  3. Почему tag не является надёжным идентификатором версии?
  4. Чем digest манифеста отличается от Image ID?
  5. Почему три тега на один образ занимают место однократно?

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

  1. Как узнать digest локально загруженного образа?
  2. Как загрузить образ, гарантированно получив конкретное содержимое?
  3. Как обратиться к образу в локальном registry на порту 5000?

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

  1. docker rmi myapp:v1 вывел Untagged, но docker images показывает образ. Почему?
  2. В production и в staging развёрнут «один и тот же тег v2.1», но поведение различается. Как это возможно и как проверить?

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

  1. Image — неизменяемый шаблон; container — его экземпляр с writable layer и процессом.
  2. Из одного образа создаётся сколько угодно containers, каждый со своим слоем записи.
  3. Полное имя: [registry/][namespace/]repository[:tag][@digest].
  4. По умолчанию подставляются docker.io, library (для официальных образов) и latest.
  5. Первая часть считается registry, если содержит точку, двоеточие или равна localhost.
  6. latest — тег по умолчанию, а не указатель на свежую версию.
  7. Tag изменяем, digest неизменен: digest вычисляется из содержимого.
  8. Image ID — хэш конфигурации локально; digest — хэш манифеста в registry.
  9. Registry — сервер; repository — коллекция версий образа внутри него.
  10. Несколько тегов на один образ не дублируют данные на диске.

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

ИсточникСсылкаЧто подтверждает
Docker overviewhttps://docs.docker.com/get-started/docker-overview/Определения image, container, registry, repository
docker pull referencehttps://docs.docker.com/reference/cli/docker/image/pull/Структура имени образа, значения по умолчанию, загрузка по digest
docker tag referencehttps://docs.docker.com/reference/cli/docker/image/tag/Правила именования, определение registry по имени хоста
docker images referencehttps://docs.docker.com/reference/cli/docker/image/ls/Колонки вывода, флаг --digests
OCI Image Specificationhttps://github.com/opencontainers/image-specМанифест, config, слои, image index для multi-platform
OCI Distribution Specificationhttps://github.com/opencontainers/distribution-specКак теги и digest адресуют объекты в registry

Навигация

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

Markdown на GitHub ↗