Публичные реестры образов удобны, но накладывают ограничения: квоты на скачивание, обязательный выход в сеть, чужие условия хранения и невозможность держать внутренние образы под контролем. Свой реестр решает все эти вопросы — это отдельный контейнер с каталогом на обычном диске, авторизацией по паролю и возможностью работать в локальной сети без доступа извне. Разберём полный путь: запуск, конфигурация, публикация образов, TLS и очистка.
Что ставим и как раскладываются данные
Базовый вариант — официальный образ registry:2 на движке Distribution. Он хранит слои в файловой системе, поддерживает удаление по дайджесту и сборку мусора. Более тяжёлые решения вроде Harbor или GitLab Container Registry добавляют веб-интерфейс, сканирование и права доступа, но для домашнего сервера или небольшой команды избыточны.
mkdir -p /srv/registry/data /srv/registry/config
df -h /srv
du -sh /srv/registry/data
ls -ld /srv/registry/data
Каталог образов растёт со временем: каждый слой лежит отдельно, а один и тот же базовый слой занимает место ровно один раз. Выделяйте под /srv/registry отдельный раздел и не размещайте его в системном корне — иначе заполнение реестра остановит весь сервер.
Запуск реестра с конфигурацией
Вместо флагов docker run удобнее держать конфигурацию в файле: так видно, что именно включено, и проще менять настройки без пересоздания контейнера.
docker run -d -p 5000:5000 --name registry --restart=always -v /srv/registry/data:/var/lib/registry -v /srv/registry/config:/etc/docker/registry registry:2
docker logs -f registry
curl -i http://127.0.0.1:5000/v2/
curl -i http://127.0.0.1:5000/v2/_catalog
Ответ 401 Unauthorized на /v2/ означает, что реестр жив и требует авторизацию — это штатное поведение. После настройки доступа там будет 200 OK. Конфигурация лежит в /srv/registry/config/config.yml и монтируется в контейнер по пути /etc/docker/registry/config.yml.
version: 0.1
log:
fields:
service: registry
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
auth:
htpasswd:
realm: private-registry
path: /etc/docker/registry/htpasswd
Блок storage.delete.enabled: true обязателен, если вы планируете удалять устаревшие теги: без него мусор не удаляется вообще. Значение addr — порт внутри контейнера, наружу он пробрасывается ключом -p. Поле http.headers добавляет защитные заголовки ко всем ответам, а блок log задаёт уровень подробности журнала: для первых недель работы поставьте информационный уровень, чтобы видеть каждую загрузку слоя.
services:
registry:
image: registry:2
restart: always
ports:
- "5000:5000"
volumes:
- /srv/registry/data:/var/lib/registry
- /srv/registry/config:/etc/docker/registry
Тот же набор в виде файла Compose удобнее держать в версионируемом каталоге: изменения видны в diff, а пересоздание контейнера сводится к двум командам. Порядок действий при обновлении образа реестра — выгрузить данные, заменить образ, запустить контейнер и убедиться, что каталог v2 на диске остался на месте.
Авторизация через htpasswd
Реестр проверяет файл с хешами паролей в формате Apache. Утилита входит в пакет apache2-utils в Debian и Ubuntu или в httpd-tools в Fedora и CentOS.
sudo apt install apache2-utils
htpasswd -Bbn admin Sup3rSecret > /srv/registry/config/htpasswd
cat /srv/registry/config/htpasswd
docker restart registry
Флаг -B включает bcrypt, -n печатает результат в stdout вместо файла. Пароль не хранится в открытом виде — только его хеш, поэтому сам файл можно брать в резервную копию. Для второго пользователя добавьте строку ещё одной командой, а для отзыва доступа удалите соответствующую строку из файла.
Публикация и вытягивание образов
Рабочий цикл повторяет привычные команды, меняется только имя префикса — оно должно содержать адрес реестра, иначе Docker отправит образ в публичный хаб.
docker login registry.local:5000
docker tag myapp:1.4 registry.local:5000/team/myapp:1.4
docker push registry.local:5000/team/myapp:1.4
docker pull registry.local:5000/team/myapp:1.4
curl -u admin:Sup3rSecret http://127.0.0.1:5000/v2/_catalog
curl -u admin:Sup3rSecret http://127.0.0.1:5000/v2/team/myapp/tags/list
Указание team/ в имени образа превращается в группу внутри реестра и упрощает права доступа, если позже появится второй набор учётных данных. Дайджест слоя можно посмотреть в выводе docker push и в заголовке ответа Docker-Content-Digest. Полезная привычка — не перезаписывать рабочий тег вроде latest, а назначать каждому выпуску номер: тогда откат сводится к повторному вытягиванию старого тега, и никто не удержит на сервере образ, которого больше нет в списке тегов.
TLS через reverse proxy
Перед выходом в сеть реестр закрывают nginx: он терминирует TLS, ограничивает размер тела запроса и прячет порт 5000 от внешнего мира.
server {
listen 443 ssl;
server_name registry.local;
ssl_certificate /etc/letsencrypt/live/registry.local/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/registry.local/privkey.pem;
client_max_body_size 2g;
chunked_transfer_encoding on;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
После правки проверяйте конфигурацию и перезагружайте сервис, а на рабочих машинах положите корневой сертификат в /etc/docker/certs.d/registry.local/ca.crt. Если сертификата нет, Docker потребует строку insecure-registries в /etc/docker/daemon.json, но для постоянной инфраструктуры лучше доверять своему центру сертификации.
sudo nginx -t && sudo systemctl reload nginx
sudo ufw allow from 192.168.1.0/24 to any port 443 proto tcp
sudo systemctl restart docker
Удаление тегов и сборка мусора
Устаревшие образы не исчезают сами: сначала удаляют манифест по дайджесту, затем запускают сборку мусора, которая вычищает нессылочные слои.
DIGEST=$(curl -sI -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' -u admin:Sup3rSecret http://127.0.0.1:5000/v2/team/myapp/manifests/1.4 | grep -i docker-content-digest | cut -d' ' -f2)
curl -u admin:Sup3rSecret -X DELETE http://127.0.0.1:5000/v2/team/myapp/manifests/$DIGEST
docker exec registry registry garbage-collect --dry-run /etc/docker/registry/config.yml
docker exec registry registry garbage-collect /etc/docker/registry/config.yml
Промежуточный запуск с --dry-run показывает, какие слои будут удалены. Сборку мусора устраивают по расписанию, но только при остановленных клиентах: работающие подтягивания во время очистки получат ошибку чтения слоя.
Чек-лист надёжности
- том реестра вынесен на отдельный раздел с контролем свободного места;
- контейнер перезапускается автоматически через
--restart=alwaysили юнит systemd; - пароли заданы bcrypt, порт 5000 не проброшен наружу без TLS;
- регулярно смотрите
du -sh /srv/registry/dataи планируйте очистку; - каталог
/srv/registry/dataвходит в резервную копию вместе сconfig.yml; - метрики реестра доступны на
/metricsдля подключения к системе мониторинга; - запуск и обновление описаны в файле Compose или юните systemd, а не в истории команд;
- после каждого запуска очистки проверьте, что образы на серверах запускаются и перезагрузка проходит штатно.
Итог
Приватный реестр — это контейнер, каталог на диске и файл с хешами паролей. Начните с проверенного запуска на порту 5000, затем добавьте htpasswd, закройте доступ обратным прокси с сертификатом и настройте очистку неиспользуемых слоёв. Такой реестр полностью контролируется: вы знаете, где лежат образы, кто к ним ходит и сколько диска они занимают.