Реестры образов: Docker Hub и приватные

Где живут собранные образы и как их грамотно публиковать, версионировать и хранить.

Реестр (registry) — сервер-хранилище Docker-образов: вы загружаете туда образ командой push и скачиваете на любой машине командой pull.

Собрать образ через docker build — это полдела. Образ лежит на вашем ноутбуке, а нужен он на сервере, у коллеги и в CI. Копировать его флешкой никто не будет. Реестр решает эту задачу: вы публикуете образ один раз, а дальше любая машина с доступом к реестру скачивает ровно тот же бинарный артефакт. Самый известный публичный реестр — Docker Hub, но в реальных командах чаще встречаются приватные: GitHub Container Registry (ghcr.io), GitLab Registry, Amazon ECR, Yandex Container Registry или собственный сервер.

Зачем это на практике

Реестр — это «общая правда» о том, какой образ считается готовым. Разработчик собрал и запушил myapp:1.4.2, CI прогнал тесты, сервер скачал тот же тег — и все работают с идентичным набором байтов. Это устраняет классическое «у меня локально работало»: на проде крутится не пересобранный заново образ, а тот же самый, что прошёл проверки.

Адрес образа: как он устроен

Полное имя образа складывается из четырёх частей: реестр/пространство-имён/репозиторий:тег. Если реестр не указан, Docker подразумевает Docker Hub, а если не указан тег — подставляет latest.

docker.io/library/nginx:1.27      # официальный nginx с Docker Hub
ghcr.io/acme/api:1.4.2            # приватный образ в GitHub Container Registry
registry.gitlab.com/team/web:dev  # образ в GitLab Registry

Понимать структуру имени важно, потому что именно по нему Docker решает, куда идти за образом и куда его публиковать.

Логин, push и pull

Чтобы публиковать в приватный реестр, нужно сначала аутентифицироваться. Команда docker login сохраняет токен, и дальше push/pull работают прозрачно.

# Вход в Docker Hub (логин/пароль или access token)
docker login -u myuser

# Вход в приватный реестр — указываем его адрес
docker login ghcr.io -u myuser

# Собираем образ сразу с «правильным» именем под реестр
docker build -t ghcr.io/acme/api:1.4.2 .

# Публикуем
docker push ghcr.io/acme/api:1.4.2

# На другой машине — скачиваем
docker pull ghcr.io/acme/api:1.4.2

Важная деталь: имя образа уже содержит адрес реестра. Docker не знает «куда» пушить из отдельной настройки — он смотрит на префикс тега. Хотите в ghcr.io — собирайте образ с тегом ghcr.io/.... Если у вас уже есть образ myapp:1.4.2 без префикса, навесьте второй тег: docker tag myapp:1.4.2 ghcr.io/acme/api:1.4.2, и пушьте новый.

Теги и версионирование

Тег — это человекочитаемая метка поверх образа. Один и тот же образ может иметь несколько тегов. Хорошая стратегия — публиковать иммутабельные теги с конкретной версией и параллельно двигать «плавающие» теги-указатели.

ТегСмыслМеняется?
1.4.2конкретный релиз (semver)нет, навсегда тот же образ
1.4последний патч минорной версиида, двигается на 1.4.3, 1.4.4…
sha-a1b2c3dобраз из конкретного коммитанет
latest«последний» по соглашениюда, перетирается каждым push

Иммутабельный тег 1.4.2 даёт воспроизводимость: задеплоили в прод именно его — и через месяц этот же тег указывает на тот же образ. По нему можно откатиться, его можно просканировать, на него можно сослаться в инциденте.

Антипаттерн latest

Соблазн деплоить myapp:latest велик, но это ловушка. latest — не «свежайшая версия», а просто строка-тег, которую перетирает каждый push. Из этого следуют беды:

  • Непонятно, что именно крутится на проде — какой коммит, какая версия.
  • docker pull myapp:latest на двух серверах в разные минуты может скачать разные образы.
  • Откат невозможен: «верни latest назад» не имеет смысла.
  • Кэш сбивает с толку: локально latest старый, в реестре уже новый.

Правило: в прод деплойте конкретную версию или sha-тег. latest допустим для локальной игры и быстрых демо, но не как источник истины для деплоя.

Приватный registry своими руками

Иногда нужен собственный реестр — внутри закрытого контура или для кэша. Docker предоставляет официальный образ registry, который поднимается одной командой.

# Поднять локальный реестр на порту 5000
docker run -d -p 5000:5000 --restart=always \
  -v registry-data:/var/lib/registry \
  --name registry registry:2

# Перетегировать и запушить в него
docker tag myapp:1.4.2 localhost:5000/myapp:1.4.2
docker push localhost:5000/myapp:1.4.2

Том registry-data хранит слои на диске, иначе данные исчезнут с контейнером. В реальном проде такой реестр прикрывают TLS и аутентификацией, но для CI-кэша или закрытой сети базовая схема рабочая. Альтернатива «всё своё» — управляемые реестры (ECR, GAR, Yandex CR): за хранение и доступность отвечает провайдер.

Retention: образы копятся бесконечно

Каждый push добавляет слои, и хранилище реестра пухнет: за полгода активной разработки легко набегают сотни гигабайт старых тегов. Retention-политика — это правила автоудаления того, что больше не нужно: «храним последние 10 тегов на репозиторий», «удаляем теги старше 90 дней», «не трогаем теги-релизы v*». В управляемых реестрах это настраивается в их UI/API; в самохостинге запускают registry garbage-collect после удаления тегов, чтобы реально освободить слои.

Как это работает под капотом

Образ — не единый файл, а набор слоёв, каждый адресуется своим SHA256-хешем (это и есть content-addressable storage). При push Docker сначала спрашивает реестр, какие слои уже есть, и заливает только недостающие. Поэтому повторный push похожего образа быстрый: общие базовые слои не передаются. Тег же — это просто запись в манифесте, которая сопоставляет человекочитаемое имя (1.4.2) с хешем-дайджестом (sha256:…). Отсюда мощный приём: можно тянуть образ по дайджестуdocker pull api@sha256:abc123… — и получить гарантированно тот же байт-в-байт образ, даже если кто-то перетёр тег. В критичных деплоях так и делают.

Частые ошибки

  • Деплой по latest. Теряете воспроизводимость и возможность отката. Версионируйте явно.
  • Забыли префикс реестра при push. docker push myapp:1.4.2 уйдёт на Docker Hub, а не в ваш приватный реестр. Тегируйте с адресом.
  • Приватный реестр без тома. Перезапустили контейнер registry — все образы исчезли. Монтируйте volume.
  • Нет retention. Диск реестра заполняется, push начинает падать. Настройте автоочистку заранее.
  • Один тег на всё. Перезаписываете 1.0 новыми сборками — невозможно понять, что задеплоено. Иммутабельный тег на релиз.

Итоги

  • Реестр — общее хранилище образов; push публикует, pull скачивает, login аутентифицирует.
  • Адрес реестра входит в имя образа; нет префикса — значит Docker Hub.
  • Версионируйте иммутабельными тегами (1.4.2, sha-…); latest — антипаттерн для прода.
  • Тег по дайджесту даёт байт-в-байт воспроизводимость.
  • Приватный реестр поднимается образом registry:2 с томом; не забывайте про retention.
Проверьте себя
1. Почему деплоить в прод по тегу latest считается антипаттерном?
Alatest скачивается медленнее других тегов
BТег latest перетирается каждым push, поэтому неясно, что именно крутится, и невозможен откат
CDocker Hub запрещает использовать latest в приватных реестрах
Dlatest всегда указывает на самый старый образ
2. Куда уйдёт образ при команде docker push myapp:1.4.2 (без префикса реестра)?
AВ последний реестр, куда был выполнен docker login
BНа Docker Hub (docker.io), так как реестр в имени не указан
CВ локальный кэш, push не произойдёт
DВ реестр, указанный в переменной DOCKER_REGISTRY
3. Что даёт обращение к образу по дайджесту (api@sha256:...) вместо тега?
AОбраз скачивается без аутентификации
BАвтоматическое обновление до новой версии
CГарантию получить байт-в-байт тот же образ, даже если тег перетёрли
DСжатие слоёв на лету