- Home »

Создание приватного Docker реестра на Ubuntu 24
Если вы когда-нибудь задавались вопросом, куда деваются все эти образы Docker после сборки, и почему пулить их из Docker Hub через медленный интернет — не самая лучшая идея для продакшена, то эта статья для вас. Создание собственного приватного Docker реестра — это не просто прихоть параноика, а реальная необходимость для любого серьёзного проекта. Особенно когда речь идёт о корпоративной разработке, где безопасность и контроль над артефактами критически важны.
Сегодня разберём, как поднять собственный Docker Registry на Ubuntu 24, настроить его правильно и не наступить на грабли, которые поджидают каждого второго админа. Приготовьтесь к практике — будет много команд, конфигов и полезных трюков.
Зачем нужен приватный Docker реестр?
Представьте ситуацию: у вас есть микросервисная архитектура из 20+ сервисов, каждый обновляется по несколько раз в день, а команда разработчиков разбросана по всему миру. Загружать каждый раз образы по 500МБ из Docker Hub — это не только медленно, но и дорого в плане трафика. Плюс, не все образы должны быть публичными.
Вот основные причины, почему стоит заморочиться с собственным реестром:
- Скорость — образы хранятся в вашей сети, пулятся мгновенно
- Безопасность — никто посторонний не получит доступ к вашим образам
- Контроль — вы сами решаете, кто и к чему имеет доступ
- Экономия трафика — особенно актуально для дата-центров с лимитированным внешним каналом
- Compliance — многие корпоративные политики безопасности требуют полного контроля над артефактами
Как это работает под капотом?
Docker Registry — это HTTP API-сервис, который работает по протоколу Docker Registry HTTP API V2. Когда вы делаете docker push
или docker pull
, клиент общается с реестром через REST API, передавая слои образов в виде блобов.
Архитектура довольно простая:
- Storage backend — где физически хранятся образы (filesystem, S3, Google Cloud Storage, etc.)
- Authentication — кто может читать/писать образы
- TLS termination — для безопасного соединения
- Reverse proxy — для балансировки и кэширования (обычно nginx)
Пошаговая настройка приватного реестра
Для начала вам понадобится сервер на Ubuntu 24. Если у вас его ещё нет, можно заказать VPS или выделенный сервер. Рекомендую минимум 2GB RAM и 20GB места на диске для старта.
Шаг 1: Устанавливаем Docker
# Обновляем пакеты
sudo apt update && sudo apt upgrade -y
# Устанавливаем необходимые пакеты
sudo apt install -y ca-certificates curl gnupg lsb-release
# Добавляем официальный GPG ключ Docker
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Добавляем репозиторий Docker
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Устанавливаем Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Добавляем пользователя в группу docker
sudo usermod -aG docker $USER
# Включаем автозапуск Docker
sudo systemctl enable docker
sudo systemctl start docker
Шаг 2: Создаём структуру директорий
# Создаём директории для нашего реестра
sudo mkdir -p /opt/docker-registry/{data,auth,certs,config}
# Назначаем правильные права
sudo chown -R $USER:$USER /opt/docker-registry
Шаг 3: Настраиваем SSL сертификаты
Без HTTPS Docker Registry работать не будет (ну, технически будет, но только локально). Есть несколько вариантов:
# Вариант 1: Самоподписанный сертификат (для тестирования)
openssl req -newkey rsa:4096 -nodes -sha256 -keyout /opt/docker-registry/certs/registry.key -x509 -days 365 -out /opt/docker-registry/certs/registry.crt
# Вариант 2: Let's Encrypt (для продакшена)
sudo apt install -y certbot
sudo certbot certonly --standalone -d your-registry.domain.com
# Копируем сертификаты Let's Encrypt
sudo cp /etc/letsencrypt/live/your-registry.domain.com/fullchain.pem /opt/docker-registry/certs/registry.crt
sudo cp /etc/letsencrypt/live/your-registry.domain.com/privkey.pem /opt/docker-registry/certs/registry.key
sudo chown $USER:$USER /opt/docker-registry/certs/*
Шаг 4: Настраиваем аутентификацию
# Устанавливаем htpasswd для создания файла паролей
sudo apt install -y apache2-utils
# Создаём файл с пользователями (замените username на нужное имя)
htpasswd -Bbn username password > /opt/docker-registry/auth/htpasswd
# Добавляем ещё пользователей
htpasswd -Bn additional_user >> /opt/docker-registry/auth/htpasswd
Шаг 5: Создаём конфигурационный файл
# Создаём config.yml
cat > /opt/docker-registry/config/config.yml << 'EOF'
version: 0.1
log:
accesslog:
disabled: false
level: info
formatter: text
storage:
filesystem:
rootdirectory: /var/lib/registry
maxthreads: 100
delete:
enabled: true
cache:
blobdescriptor: inmemory
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
Access-Control-Max-Age: [1728000]
Access-Control-Allow-Credentials: [true]
Access-Control-Expose-Headers: ['Docker-Content-Digest']
auth:
htpasswd:
realm: basic-realm
path: /auth/htpasswd
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
EOF
Шаг 6: Запускаем Registry контейнер
# Создаём docker-compose.yml
cat > /opt/docker-registry/docker-compose.yml << 'EOF'
version: '3.8'
services:
registry:
image: registry:2.8.3
container_name: docker-registry
restart: unless-stopped
ports:
- "5000:5000"
environment:
REGISTRY_CONFIG_PATH: /etc/docker/registry/config.yml
REGISTRY_HTPASSWD_REALM: Registry Realm
REGISTRY_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_STORAGE_DELETE_ENABLED: true
volumes:
- ./data:/var/lib/registry
- ./auth:/auth:ro
- ./config/config.yml:/etc/docker/registry/config.yml:ro
- ./certs:/certs:ro
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/registry.crt
REGISTRY_HTTP_TLS_KEY: /certs/registry.key
registry-ui:
image: joxit/docker-registry-ui:2.5.5
container_name: docker-registry-ui
restart: unless-stopped
ports:
- "8080:80"
environment:
SINGLE_REGISTRY: true
REGISTRY_TITLE: My Private Docker Registry
DELETE_IMAGES: true
SHOW_CONTENT_DIGEST: true
NGINX_PROXY_PASS_URL: https://registry:5000
SHOW_CATALOG_NB_TAGS: true
CATALOG_MIN_BRANCHES: 1
CATALOG_MAX_BRANCHES: 1
TAGLIST_PAGE_SIZE: 100
REGISTRY_SECURED: true
CATALOG_ELEMENTS_LIMIT: 1000
depends_on:
- registry
EOF
# Запускаем
cd /opt/docker-registry
docker compose up -d
Шаг 7: Настраиваем reverse proxy (nginx)
# Устанавливаем nginx
sudo apt install -y nginx
# Создаём конфигурацию для реестра
sudo tee /etc/nginx/sites-available/docker-registry << 'EOF'
upstream docker-registry {
server localhost:5000;
}
upstream docker-registry-ui {
server localhost:8080;
}
server {
listen 80;
server_name your-registry.domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-registry.domain.com;
ssl_certificate /opt/docker-registry/certs/registry.crt;
ssl_certificate_key /opt/docker-registry/certs/registry.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
client_max_body_size 0;
chunked_transfer_encoding on;
location /v2/ {
proxy_pass http://docker-registry;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
location / {
proxy_pass http://docker-registry-ui;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
EOF
# Включаем сайт
sudo ln -s /etc/nginx/sites-available/docker-registry /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
Тестируем наш реестр
Теперь самое интересное — проверяем, что всё работает:
# Логинимся в наш реестр
docker login your-registry.domain.com
# Тагируем существующий образ
docker tag nginx:latest your-registry.domain.com/nginx:latest
# Пушим образ в наш реестр
docker push your-registry.domain.com/nginx:latest
# Удаляем локальный образ
docker rmi your-registry.domain.com/nginx:latest nginx:latest
# Пулим образ из нашего реестра
docker pull your-registry.domain.com/nginx:latest
Расширенная настройка и оптимизация
Настройка garbage collection
Со временем в реестре накапливается мусор из удалённых образов. Настроим автоматическую очистку:
# Создаём скрипт очистки
sudo tee /opt/docker-registry/cleanup.sh << 'EOF'
#!/bin/bash
# Останавливаем registry
cd /opt/docker-registry
docker compose stop registry
# Запускаем garbage collection
docker run --rm -v /opt/docker-registry/data:/var/lib/registry registry:2.8.3 garbage-collect /etc/docker/registry/config.yml
# Запускаем registry обратно
docker compose start registry
echo "Garbage collection completed at $(date)"
EOF
sudo chmod +x /opt/docker-registry/cleanup.sh
# Добавляем в crontab (запуск каждую неделю в 3 ночи)
(crontab -l 2>/dev/null; echo "0 3 * * 0 /opt/docker-registry/cleanup.sh >> /var/log/docker-registry-cleanup.log 2>&1") | crontab -
Настройка мониторинга
# Добавляем Prometheus метрики в docker-compose.yml
cat >> /opt/docker-registry/docker-compose.yml << 'EOF'
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
EOF
# Создаём конфигурацию Prometheus
cat > /opt/docker-registry/prometheus.yml << 'EOF'
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'docker-registry'
static_configs:
- targets: ['registry:5000']
metrics_path: '/metrics'
scheme: https
tls_config:
insecure_skip_verify: true
EOF
Сравнение с альтернативами
Решение | Плюсы | Минусы | Сложность настройки |
---|---|---|---|
Docker Registry | Простота, официальная поддержка | Базовый функционал | Низкая |
Harbor | Enterprise-функции, сканирование уязвимостей | Ресурсоёмкость | Высокая |
Nexus Repository | Поддержка разных форматов | Тяжёлый, Java-based | Средняя |
GitLab Container Registry | Интеграция с CI/CD | Нужен GitLab | Средняя |
Автоматизация и интеграция в CI/CD
Вот пример интеграции с GitLab CI:
# .gitlab-ci.yml
stages:
- build
- push
variables:
REGISTRY_URL: your-registry.domain.com
IMAGE_NAME: $REGISTRY_URL/myapp
build:
stage: build
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker build -t $IMAGE_NAME:latest .
push:
stage: push
script:
- echo $REGISTRY_PASSWORD | docker login $REGISTRY_URL -u $REGISTRY_USER --password-stdin
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker push $IMAGE_NAME:latest
only:
- main
Безопасность и бэкапы
Не забываем про безопасность:
# Создаём скрипт бэкапа
sudo tee /opt/docker-registry/backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backup/docker-registry"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Бэкапим данные реестра
tar -czf $BACKUP_DIR/registry-data-$DATE.tar.gz -C /opt/docker-registry data/
# Бэкапим конфигурацию
tar -czf $BACKUP_DIR/registry-config-$DATE.tar.gz -C /opt/docker-registry config/ auth/
# Удаляем старые бэкапы (старше 30 дней)
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
echo "Backup completed at $(date)"
EOF
sudo chmod +x /opt/docker-registry/backup.sh
# Добавляем в crontab (ежедневный бэкап в 2 ночи)
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/docker-registry/backup.sh >> /var/log/docker-registry-backup.log 2>&1") | crontab -
Интересные факты и нестандартные применения
Вот несколько крутых способов использования приватного реестра:
- Кэширующий прокси — можно настроить Registry как кэш для Docker Hub, что ускорит пулы образов
- Multi-registry setup — несколько реестров для разных команд/проектов с общим nginx
- Географическое распределение — реестры в разных регионах с синхронизацией
- Интеграция с LDAP — для корпоративной аутентификации
Пример настройки кэширующего прокси:
# В config.yml добавляем секцию proxy
proxy:
remoteurl: https://registry-1.docker.io
username: your-dockerhub-user
password: your-dockerhub-password
Устранение типичных проблем
Самые частые грабли и как их избежать:
- Проблема: "x509: certificate signed by unknown authority"
Решение: Добавить самоподписанный сертификат в доверенные или использовать Let's Encrypt - Проблема: "denied: requested access to the resource is denied"
Решение: Проверить права пользователя в htpasswd и правильность логина - Проблема: Медленные push/pull операции
Решение: Увеличить nginx client_max_body_size и настроить кэширование
Мониторинг и логирование
# Настройка ELK стека для логов
version: '3.8'
services:
elasticsearch:
image: elasticsearch:7.15.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
logstash:
image: logstash:7.15.0
depends_on:
- elasticsearch
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
kibana:
image: kibana:7.15.0
ports:
- "5601:5601"
depends_on:
- elasticsearch
Заключение и рекомендации
Поздравляю! Теперь у вас есть полноценный приватный Docker реестр, который можно использовать в продакшене. Вот основные рекомендации по использованию:
- Для разработки: Используйте самоподписанные сертификаты и базовую аутентификацию
- Для staging/production: Обязательно Let's Encrypt, мониторинг, бэкапы и автоматическая очистка
- Для enterprise: Рассмотрите Harbor или интеграцию с корпоративными системами аутентификации
Основные преимущества, которые вы получили:
- Полный контроль над образами
- Ускорение CI/CD процессов
- Экономия на внешнем трафике
- Повышение безопасности
Не забывайте регулярно обновлять Registry до последних версий и следить за логами. И помните: лучший реестр — это тот, о котором вы не думаете, потому что он просто работает!
P.S. Если вам нужен надёжный сервер для развёртывания такого реестра, рекомендую посмотреть на VPS или выделенные серверы — там можно подобрать конфигурацию под ваши потребности.
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.