Docker Hub бесплатно хранит только публичные образы, для приватных даёт один бесплатный репозиторий и режет скорость pull для анонимных запросов. Если образы собираются в закрытом CI и содержат код продукта, собственный Docker Registry на VPS снимает оба ограничения и держит данные там, где вы решаете сами — вплоть до отдельного сервера в том же дата-центре, что и продакшен.
Зачем свой Registry вместо Docker Hub
Приватный Registry на своём сервере убирает лимиты на количество pull в час, которые Docker Hub применяет к анонимным и бесплатным аккаунтам — при активном CI это легко превращается в ошибку «too many requests» посреди деплоя. Второй довод — данные: образ с собранным приложением часто содержит куски исходного кода и секреты сборки, а хранить его на чужой инфраструктуре не всегда допустимо по контракту с клиентом.
Третий довод — скорость. Registry на том же VPS или в той же локальной сети, что и продакшен-серверы, отдаёт слои образа за миллисекунды, а не тянет их через интернет при каждом деплое.
Registry v2 или Harbor: что выбрать
Registry v2 — минимальный образ от Docker
Официальный образ registry:2 — это один контейнер без веб-интерфейса, который отдаёт и принимает образы по HTTP API. Поднимается за 5 минут, подходит команде из нескольких разработчиков без сложных требований к правам доступа.
Harbor — Registry с UI, сканированием и RBAC
Harbor добавляет поверх того же API веб-интерфейс, ролевую модель доступа по проектам, сканирование образов на уязвимости и репликацию между несколькими Registry. Разворачивается через Helm-чарт или docker-compose из нескольких контейнеров, требует больше ресурсов VPS.
| Параметр | Registry v2 | Harbor |
|---|---|---|
| Время развёртывания | 5-10 минут | 30-60 минут |
| Веб-интерфейс | Нет | Есть |
| Сканирование уязвимостей | Нет | Встроено (Trivy) |
| Минимум RAM | 512 МБ | 4 ГБ |
Запуск Registry в Docker Compose
Для команды из 2-5 разработчиков обычно хватает Registry v2 с базовой авторизацией и хранением слоёв на диске VPS.
version: "3.8"
services:
registry:
image: registry:2
restart: always
ports:
- "5000:5000"
volumes:
- ./data:/var/lib/registry
- ./auth:/auth
- ./certs:/certs
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/registry.crt
REGISTRY_HTTP_TLS_KEY: /certs/registry.key
TLS-сертификат и авторизация по логину и паролю
Docker отказывается работать с Registry без TLS, кроме localhost и явно добавленных в список insecure-registries — второй вариант годится только для теста. Файл с логинами создаётся утилитой htpasswd из пакета apache2-utils.
apt install -y apache2-utils
mkdir -p auth
htpasswd -Bc auth/htpasswd deploy-bot
docker-compose up -d
Сертификат для домена registry.example.com проще выпустить через reverse proxy с автоматическим TLS — например, Traefik, который сам обновляет сертификат Let's Encrypt и проксирует запросы на порт 5000 контейнера registry.
Push и pull образов, очистка старых слоёв
После авторизации docker login работает с приватным Registry так же, как с Docker Hub, только вместо имени пользователя указывается полный адрес домена.
docker login registry.example.com
docker tag myapp:latest registry.example.com/myapp:1.4.0
docker push registry.example.com/myapp:1.4.0
docker pull registry.example.com/myapp:1.4.0
В пайплайне GitLab CI сборка и push образа обычно идут отдельным этапом перед деплоем на сервер. Старые слои и неиспользуемые теги стоит чистить регулярно командой registry garbage-collect, иначе диск VPS забивается образами за пару месяцев. Если Registry обслуживает кластер из нескольких хостов, его удобно ставить рядом с Docker Swarm или Kubernetes — все узлы тянут образы из одного источника.
Чек-лист перед продакшеном
- TLS-сертификат настроен и обновляется автоматически, insecure-registries не используется.
- Авторизация обязательна для push, анонимный pull отключён, если образы не предназначены для публичного доступа.
- Резервная копия каталога с данными Registry снимается по расписанию, а не только образы в самом Registry.
- Регулярная garbage collection настроена по cron, чтобы диск не заканчивался внезапно.