Вы конечно можете использовать как публичный DockerHub, так и публичный или приватный Docker Registry в GitLab, но иногда возникает необходимость поднять свой локальный или сетевой Registry и стоит отметить, что сделать это совершенно несложно и сейчас я расскажу как это сделать.
Первым делом подготавливаем рабочее окружение Docker по инструкциям:
- Установка Docker+DockerCompose+KubeCtl+Helm
- Управление внутренними настройками логирования в Docker
- Перенос хранилища образов, томов и т.п. Docker в /opt/
Итак, окружение готово. Проверяем, что можно скачать образы, можно их запускать и все инструменты управления работают и продолжим.
Запускаем образ который у нас будет обслуживать наш Registry. Выбираем, что-то типа LTS, а не RC всякие, мы тут в прод играем (https://hub.docker.com/_/registry).

# docker run -d -p 5000:5000 --name registry registry:2.8.3
Проверка.
# curl -I http://127.0.0.1:5000
HTTP/1.1 200 OK
Cache-Control: no-cache
Date: Mon, 24 Feb 2025 04:11:26 GMT
Выглядит красиво. Так как я фритюрницу от Cloud.ru использую, то надо будет сразу открыть 80 и 443 для всех, там Nginx повесим и 5000 только для доступа с одного конкретного адреса для тестов. И сразу же сделаем еще и доменное имя.

Группа безопасности у меня получилась следующего вида.

Проверяем доступность по сети порта 5000.
$ telnet 213.171.26.110 5000
Trying 213.171.26.110...
Connected to 213.171.26.110.
Escape character is '^]'.
^]
telnet> quit
Connection closed.
Пробуем локально получить образ с DockerHub, назначить ему тег и разместить в нашем хранилище образов.
# docker pull bash
# docker image tag bash:latest localhost:5000/bash:latest
# docker push localhost:5000/bash:latest
Если вы попробуете провести аналогичную операцию с удаленного хоста, то получите ошибку.
Get "https://registry.anton-c.ru:5000/v2/": http: server gave HTTP response to HTTPS client
В этом случае требуется добавить НА КЛИЕНТЕ с которого выполняем push параметр в файл daemon.json.
{
"insecure-registries" : [ "213.171.26.110/32" ]
}
Перезапускаем демон docker НА КЛИЕНТЕ.
# systemctl restart docker
Проверяем, что необходимые параметры применились.
# docker info
Client: Docker Engine - Community
...
Insecure Registries:
213.171.26.110/32
127.0.0.0/8
Live Restore Enabled: false
Обратите внимание, что авторизации нет наш registry доступен всем и именно поэтому на этапе тестирования мы его прикрыли firewall для доступа из интернета.
Создадим базовый сервис для Docker Compose.
version: '3'
services:
private-registry:
image: registry:2.8.3
ports:
- "5000:5000"
Создаем файл авторизации используя apache2-utils.
# apt install apache2-utils
# htpasswd -Bc registry.password admin
Модифицируем docker-compose.yml для использования basic-авторизации.
services:
private-registry:
image: registry:2.8.3
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /tmp/registry.password
volumes:
- ./registry.password:/tmp/registry.password
ports:
- "5000:5000"
Проверяем, что больше мы не можем ничего пушить не авторизовавшись.
$ docker push registry.anton-c.ru:5000/bash:latest
The push refers to repository [registry.anton-c.ru:5000/bash]
4c9a226629c6: Preparing
488f36d19f80: Preparing
08000c18d16d: Preparing
no basic auth credentials
Пробуем авторизоваться и повторить фокус.
chernousov@home-workstation-01:~$ docker login registry.anton-c.ru:5000
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /home/chernousov/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
chernousov@home-workstation-01:~$ docker push registry.anton-c.ru:5000/bash:latest
The push refers to repository [registry.anton-c.ru:5000/bash]
4c9a226629c6: Pushed
488f36d19f80: Pushed
08000c18d16d: Pushed
latest: digest: sha256:e11ea53bf8d08835b861d8a0f34d45b5dcadef38514fb59d14d35dad7cbca204 size: 946
С авторизацией разобрались и теперь по старой схеме просто создаем Let’s encrypt сертификат и конфиг для https хоста и уходим от приседаний с портами и прописывания всяких трастов для http.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name www.registry.anton-c.ru registry.anton-c.ru;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name www.registry.anton-c.ru registry.anton-c.ru;
access_log /var/log/nginx/registry.anton-c.ru-access.log;
error_log /var/log/nginx/registry.anton-c.ru-error.log warn;
ssl_certificate /etc/letsencrypt/live/registry.anton-c.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/registry.anton-c.ru/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
client_max_body_size 64M;
fastcgi_buffers 64 4K;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
if ($host ~ ^www\.(?<domain>.+)$) {
return 301 $scheme://$domain$request_uri;
}
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_pass http://127.0.0.1:5000;
}
}
Собсно вот и все дела.