© 2021 WebHive

Создаю свой локальный Docker Registry

Не стану отрицать очевидный медицинский факт, что у меня докер головного мозга. Болезнь пустила метастазы в виде неистребимого желания заиметь свой персональный registry для хранения и распространения собственных образов. Этим бесценым опытом я и хочу поделиться.

Что за странное желание?

Позывы сделать свой registry были давно, но как-то всё не доходили руки. На самом деле не было такой острой необходимости. Как правило всё решалось простым:

$ export DOCKER_HOST=tcp://<ip адрес хоста с докером>:2375
$ docker <какая нибудь команда>

Минусы такого подхода очевидны — каждый раз образ собирается прямо на целевом сервере и каждую новую установку или обновление образа нужно выполнять на каждом сервере отдельно. И это при том, что собранный образ у меня уже есть. Хотелось бы конечно собрать и оттестировать локально один раз, а потом просто разлить готовый образ по нужным хостам.

Попытка использовать в качестве registry Docker Hub не увенчалась успехом — бесплатно там доступно не очень много.

Моя инфраструктура

Железо

У меня есть локальный сервер, на котором крутятся всякие базы данных и прочие редисы с эластиками. Запускать всё на локальной машине это просто безумие, поэтому давно уже вынес все зависимости на этот отдельный компьютер. Ну и вполне очевидно, что разместить registry я решил именно на нём.

Сеть

Единственная проблема это доступ к нему снаружи, но это даже не проблема, а скорее небольшое препятствие.

В общем я просто пробросил 443 порт со своего роутера на мой сервер внутри моей домашней сети. В моём Zyxel Keenetic Giga это делается так:

Ну и для конкретного порта настройки выглядят вот так:

DNS

Я пользуюсь DNS серверами Яндэкс-а, ну и соответственно прописал там свой внешний IP (он у меня статичный) для имени registry.webhive.ru.

На этом предварительная подготовка закончена и у меня есть доменное имя и сервер во домашней сети, доступный по 443-му порту по этому имени.

Установка registry

Сертификаты

Теперь мне нужно запустить registry и прибить его к 443 порту. Но 443 порт это HTTPS и соответственно нужно поиметь сертификат для шифрования трафика. Думаю никого не удивлю тем, что выбор пал на LetsEncrypt. Для получения сертификата воспользуюсь скриптом acme.sh и API для Яндэкс DNS-а, что позволяет максимально автоматизировать процесс.

На самом деле у docker образа registry есть встроенная поддержка LetsEncrypt, но она у меня не заработала, впрочем не только у меня. Собственно именно поэтому и установку сертификата выполнил отдельно.

Да — ещё забыл упомянуть — без HTTPS не работает авторизация и если хочется защитить свой registry паролем, то получение сертификата является обязательным шагом.

Итак ближе к делу. Получаем сертификат. Для этого на сервере запускаем команду (на сервере должен быть установлен docker)

$ docker run -v /etc/acme/webhive:/acme.sh \
             -e PDD_Token=<токен API Яндэкса> \
             --rm \
             neilpang/acme.sh \
             --issue \
             -d registry.webhive.ru \
             --dns dns_yandex \
             --accountemail "<email>" \
             --standalone

Здесь /etc/acme/webhive предварительно созданная папка для сохранения данных LetsEncrypt.

Далее нужно экспортировать полученный сертификат и ключ в папку, доступную для registry (в моём случае это /etc/docker-registry/certs). Делается это так:

$ docker run -v /etc/acme/webhive:/acme.sh \
             -v /etc/docker-registry/certs/:/etc/docker-registry/certs/ \
             --rm \
             neilpang/acme.sh \
             --install-cert -d registry.webhive.ru \
             --key-file /etc/docker-registry/certs/key.pem \
             --fullchain-file /etc/docker-registry/certs/cert.pem

Думаю тут всё очевидно — в результате получаем два файла — сертификат и ключ.

Basic Auth

Кроме сертификата нам понадобится настроить парольную авторизацию, ибо у меня нет большого желания, чтоб в мой registry лазил кто попало. Делается это так — мы используем образ registry, но как команду для генерации аккаунта пользователя.

$ docker run --rm \
             --entrypoint htpasswd \
             registry:2 \
             -Bbn <имя пользователя> <пароль> \
             > /etc/docker-registry/auth/htpasswd

Собираем всё вместе

Ну и наконец у нас всё готово для финального аккорда. У нас есть сертификат и ключ для организации HTTPS, у нас есть файл с аккаунтами пользователей. Давайте-же наконец запустим registry.

$ docker run \
    --rm \
    -p 443:5000 \
    --name registry \
    -v /etc/docker-registry/certs:/certs/ \
    -v /etc/docker-registry/auth:/auth \
    -v /var/lib/docker-registry:/var/lib/registry \
    -e REGISTRY_AUTH=htpasswd \
    -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
    -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
    -e REGISTRY_HTTP_HOST=https://registry.webhive.ru \
    -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/cert.pem \
    -e REGISTRY_HTTP_TLS_KEY=/certs/key.pem \
    registry:2

Что тут происходит — мы подсовываем приложению наши ключи и дополнительные опции в виде переменных окружения. Собственно как и описано в документации — https://docs.docker.com/registry/configuration/ (раздел Override specific configuration options).

Так-же мы мапим некоторые папки нашего хоста в докерский образ. Это как раз сертификаты и файл с аккаунтами. Ну и собственно и всё — пазл сложился. Теперь мы можем наблюдать наш registry живой и рабочий в списке процессов докера.

$ docker login registry.webhive.ru
Username: docker
Password:
WARNING! Your password will be stored unencrypted in /home/roman/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

Автоматизируй это

Для полного счастья осталось добавить registry в автозагрузку сервера. Используем systemd — ну, а какие ещё могут быть варианты в 2018 году?
Юнит-файл для этого у меня имеет следующий вид

[Unit]
Description=Docker Registry Container
After=docker.service
Requires=docker.service

[Service]
ExecStartPre=-/usr/bin/docker pull registry
ExecStartPre=-/usr/bin/docker kill registry
ExecStartPre=-/usr/bin/docker rm registry
ExecStart=/usr/bin/docker run \
                          --rm \
                          -p 443:5000 \
                          --name registry \
                          -v /etc/docker-registry/certs:/certs/ \
                          -v /etc/docker-registry/auth:/auth \
                          -v /var/lib/docker-registry:/var/lib/registry \
                          -e REGISTRY_AUTH=htpasswd \
                          -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
                          -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
                          -e REGISTRY_HTTP_HOST=https://registry.webhive.ru \
                          -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/cert.pem \
                          -e REGISTRY_HTTP_TLS_KEY=/certs/key.pem \
                          registry:2
ExecStop=-/usr/bin/docker stop registry
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

Ну и для полного счастья нужен ещё таймер для обновления сертификатов — он есть у меня, но в контексте данной статьи публиковать его не вижу нужды.

Итого

Получили рабочий Docker Registry, работающий на сервере внутри домашней сети и доступный снаружи по HTTPS и защищённый паролем.

Комментарии