Публичные реестры образов удобны, но накладывают ограничения: квоты на скачивание, обязательный выход в сеть, чужие условия хранения и невозможность держать внутренние образы под контролем. Свой реестр решает все эти вопросы — это отдельный контейнер с каталогом на обычном диске, авторизацией по паролю и возможностью работать в локальной сети без доступа извне. Разберём полный путь: запуск, конфигурация, публикация образов, 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, закройте доступ обратным прокси с сертификатом и настройте очистку неиспользуемых слоёв. Такой реестр полностью контролируется: вы знаете, где лежат образы, кто к ним ходит и сколько диска они занимают.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте