Один Docker tag, разные версии: как найти образ, из которого реально запущен контейнер
Представим обычное обновление сервиса. В Compose-файле на ноутбуке и на сервере написано одно и то же:
services:
api:
image: registry.example.com/team/api:latest
На ноутбуке уже работает новая версия, а сервер отвечает по-старому. Первая реакция — проверить строку image и убедиться, что она совпадает. Но эта строка сообщает только имя, по которому образ выбирали. Она не отвечает на главный вопрос: из какого содержимого создан уже запущенный контейнер.
Docker tag — перемещаемая ссылка. Сегодня latest может указывать на один образ, после следующего push — на другой. При этом контейнер, созданный вчера, продолжает ссылаться на прежний image ID. Даже docker compose pull сам по себе не заменяет контейнер: команда загружает образы, но не запускает их.
Сначала спрашиваем контейнер, а не тег
У запущенного контейнера есть два полезных значения. .Config.Image показывает исходную строку вроде registry.example.com/team/api:latest, а .Image — ID локального образа, из которого контейнер действительно создан.
container=example-api
docker container inspect \
--format 'declared={{.Config.Image}} actual={{.Image}} created={{.Created}}' \
"$container"
Типичный вывод выглядит так:
declared=registry.example.com/team/api:latest actual=sha256:7c1... created=2026-10-20T08:14:32Z
Теперь можно посмотреть именно этот образ, даже если локальный tag latest уже переставлен на новую версию:
image_id=$(docker container inspect --format '{{.Image}}' "$container")
docker image inspect \
--format 'id={{.Id}} repo_digests={{json .RepoDigests}} labels={{json .Config.Labels}}' \
"$image_id"
Поле .Id содержит идентификатор локального образа. В .RepoDigests находятся известные Docker адреса вида repository@sha256:..., которые связывают образ с содержимым в реестре. А в .Config.Labels можно найти версию приложения и номер коммита, если вы добавили их при сборке.
Локальный image ID и digest из реестра обозначают разные объекты, поэтому прямое сравнение этих строк не покажет, совпадают ли версии. Сначала узнаем, из какого локального образа создан контейнер. Затем отдельно проверим, что сейчас доступно в реестре под нужным тегом.
Проверяем, куда tag указывает в реестре
Команда docker buildx imagetools inspect читает описание образа в реестре. Если под одним тегом опубликованы сборки для разных архитектур, например amd64 и arm64, она покажет общий список и отдельные описания сборок. У каждого будет свой digest.
docker buildx imagetools inspect registry.example.com/team/api:latest
Теперь у нас три факта вместо одной двусмысленной строки:
- какую ссылку сохранили в конфигурации контейнера;
- какой локальный image ID реально запущен;
- на какой digest тег указывает в реестре сейчас.
Если реестр уже показывает новый digest, а .Image контейнера относится к старой сборке, причина расхождения найдена. Следующий шаг — загрузить нужный образ, пересоздать сервис и ещё раз проверить фактический ID:
docker compose pull api
docker compose up -d api
docker compose ps
docker container inspect --format '{{.Image}}' example-api
Имя контейнера зависит от проекта Compose. Если оно не задано явно, возьмите его из docker compose ps. После обновления полезно проверить не только ID, но и healthcheck либо рабочий HTTP endpoint: правильный образ ещё должен успешно запуститься с текущей конфигурацией.
Почему version tag решает только половину задачи
Тег 1.8.3 удобнее latest: по нему человек понимает, какой релиз ожидается. Но реестр технически позволяет передвинуть и такой tag. Если развёртывание должно быть воспроизводимым, сохраните digest в манифесте релиза и укажите его в Compose:
services:
api:
image: registry.example.com/team/api:1.8.3@sha256:REPLACE_WITH_RELEASE_DIGEST
После загрузки сборки в реестр подставьте полученный digest в Compose-файл. Тег останется подсказкой для человека, а digest закрепит конкретное содержимое. Для следующего релиза придётся явно заменить digest — новая сборка не подхватится только потому, что кто-то передвинул тег.
Сохраните рядом с релизом тег, digest, номер коммита и время сборки. Тогда можно проверить, какая версия ожидалась и какая запущена. Если собираете образы для нескольких архитектур, сохраните также digest общего списка сборок и архитектуру сервера.
Откат — это не только старый образ
Старый digest позволяет загрузить прежний образ. Но база данных, файлы в томах и переменные окружения останутся в текущем состоянии. Если новая версия уже изменила структуру базы, старое приложение может перестать с ней работать. Поэтому перед откатом нужно проверить совместимость данных и конфигурации.
До релиза попробуйте запустить предыдущую версию на совместимой копии данных и проверьте, что сервис отвечает. Это покажет, сможете ли вы вернуться к ней при неудачном обновлении.
От диагностики одного контейнера к воспроизводимому проекту
Если хочется пройти весь путь руками, в курсе «Docker и Docker Compose: контейнеризация приложений» есть сборка Dockerfile, сети, тома, Compose, диагностика по логам и состоянию контейнеров, работа с registry, обновление версий и откат. Практика включает 42 локальные лабораторные, а итоговый проект соединяет приложение с PostgreSQL, передачу окружения и восстановление данных.
Посмотреть программу курса Docker
По соседним проблемам пригодятся разборы порядка COPY и кэша сборки и ошибки exec format при несовпадении архитектуры. Общий маршрут собран в навигаторе Docker для разработчика.
Техническая справка: docker container inspect, docker image inspect, pull по digest, imagetools inspect, docker compose up.
