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, щоб диск не закінчувався несподівано.