FaceHost - Статьи

Как развернуть свое Docker хранилище в Ubuntu 20.04

Виртуальные сервера VPS/VDS База знаний
В данном руководстве мы изучим, как развернуть своё собственное Docker хранилище на сервере, работающем под управлением Ubuntu 20.04.
Хранилище Docker
У Docker есть бесплатное общедоступное хранилище, Docker Hub, которое может припарковать ваши образы Docker. Тонкость состоит в том, что не всегда есть желание хранить свои образы там, где они будут общедоступны. Образы обычно содержат весь необходимый для запуска код, поэтому использование собственного реестра предпочтительнее для хранения проприентарного программного обеспечения.
Мы будем использовать Docker Compose, чтобы определять конфигурации запуска ваших контейнеров Docker. А также, мы будем использовать веб-сервер Nginx для передачи трафика сервера из интернета в работающий контейнер Docker. Изучив содержимое данного руководства, вы сможете поместить образ Docker в своё личное хранилище, а также, безопасно извлечь его с удалённого сервера.

Подготовка серверов

В данном мануале мы будем использовать два сервера. Один из них будет выступать в качестве хоста для вашего приватного Docker хранилища, а другой – в качестве сервера-клиента. Оба VPS работают под управлением операционной системы Ubuntu 20.04. Все работы на серверах мы будем производить под учётной записью, не являющейся root-ом, но имеющей привилегии sudo. Для настройки межсетевого экрана на серверах мы будем использовать интерфейс UFW.
Так как в качестве веб-сервера мы планируем использовать Nginx, то давайте перейдём к его настройке. Действия по настройке Nginx нужно будет произвести на сервере, выступающем в качестве хоста для нашего хранилища. Сначала необходимо установить Nginx:
$ sudo apt update
$ sudo apt install nginx
Теперь добавьте Nginx в список приложений, доступ для которых разрешён в нашем брандмауэре, и проверьте статус службы Nginx:
$ sudo ufw allow 'Nginx HTTP'
$ systemctl status nginx
Если сервис запущен вы можете проверить его доступность через веб-интерфейс. Для чего в браузере наберите IP-адрес вашего сервера-хоста:
Далее, вам необходимо будет добавить виртуальный хост для вашего домена. В нашем мануале мы будем использовать домен my-domain.host. Обратите внимание, что А-запись домена, с которым вы будете производить дальнейшие действия, должна соответствовать IP-адресу вашего сервера-хоста.

Итак, создайте каталог для вашего сайта и предоставьте ему соответствующие права:
$ sudo mkdir -p /var/www/my-domain.host/html
$ sudo chown -R $USER:$USER /var/www/my-domain.host/html
$ sudo chmod -R 755 /var/www/my-domain.host
Теперь в созданном каталоге создайте файл страницы сайта index.html:
$ sudo nano /var/www/my-domain.host/html/index.html
Наполните его содержимым:
<html>
   <head>
      <title>
         Domain on Nginx
      </title>
   </head>
   <body>
      <h1>
         Connection to my-domain.host is created successfully!!!
      </h1>
   </body>
</html>
Закройте файл, сохранив изменения (Ctrl+X, после чего Y и Enter).

В директории /etc/nginx/sites-available/ создайте файл my-domain.host:
$ sudo nano /etc/nginx/sites-available/my-domain.host
Скопируйте в него следующие строки:
server
   {
      listen 80;
      listen [::]:80;
      root /var/www/my-domain.host/html;
      index index.html index.htm index.nginx-debian.html;
      server_name my-domain.host www.my-domain.host;
      location /
         {
            try_files $uri $uri/ =404;
         }
   }
Закройте файл с сохранением изменений и создайте ссылку в /etc/nginx/sites-enabled/:
$ sudo ln -s /etc/nginx/sites-available/my-domain.host /etc/nginx/sites-enabled/
Отредактируйте файл /etc/nginx/nginx.conf:
$ sudo nano /etc/nginx/nginx.conf
В этом файле вам нужно будет раскомментировать строку, которая содержит server_names_hash_bucket_size.

Для проверки корректности синтаксиса Nginx наберите:
$ sudo nginx -t
Должно быть:
Перезапустите Nginx:
$ sudo systemctl restart nginx
После этого ваш домен должен стать доступным по своему доменному имени:
И нам останется только настроить редирект всего трафика с протокола HTTP на HTTPS. Мы осуществим это при помощи центра сертификации Let’s Encrypt.

Сначала необходимо будет установить пакет snap:
$ sudo apt install snapd
Затем с его помощью проинсталлируйте Certbot:
$ sudo snap install --classic certbot
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
Далее, в брандмауэре необходимо разрешить трафик по протоколу HTTPS, закрыв трафик по HTTP:
$ sudo ufw allow 'Nginx Full'
$ sudo ufw delete allow 'Nginx HTTP'
$ sudo ufw status
Теперь, необходимо запустить Certbot с помощью плагина nginx для указания доменов, которые будут использовать сертификаты:
$ sudo certbot --nginx
Здесь вам нужно будет указать свой E-mail, согласиться с предоставляемыми условиями и указать домен, для которого требуется активировать протокол HTTPS.

С этого момента подключение к вашему домену защищено:
Далее, переходим с настройке Docker.

Установка Docker

Пакет Docker будем устанавливать из официального репозитория Docker. Чтобы сделать это, мы добавим новый источник пакетов и ключ GPG от Docker, чтобы быть уверенными в валидности загрузки пакета. После чего установим пакет.
Проинсталлируйте несколько необходимых пакетов, которые позволят установщику использовать пакеты обновлений по протоколу HTTPS:
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common
Теперь, добавьте в систему ключ GPG для официального репозитория Docker:
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Далее, добавьте репозиторий Docker и запустите обновление базы данных пакетов обновлений:
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
$ sudo apt update
Убедитесь, что дальнейшая установка будет произведена из репозитория Docker, а не из дефолтного репозитория Ubuntu:
$ apt-cache policy docker-ce
Обратите внимание, что docker-ce не установлен, но готов к установке из репозитория Ubuntu 20.04.

И наконец, запустите установку Docker:
$ sudo apt install docker-ce
Теперь Docker установлен, служба запущена и процесс доступен для старта при загрузке системы. Проверьте:
$ sudo systemctl status docker
Также, нам нужно будет установить инструмент, который позволяет осуществлять запуск сред выполнения приложений с несколькими контейнерами. Это – Docker Compose. Чтобы загрузить самую свежую Docker Compose, посмотрите её номер на странице официального репозитория Github. Наберите в командой строке команду, которая загрузит исполняемый файл:
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
После этого задайте загруженному файлу соответствующие права. Это позволит сделать docker-compose исполняемым файлом:
$ sudo chmod +x /usr/local/bin/docker-compose
Проверьте версию установленного компонента:
$ docker-compose --version
Теперь всё готово для установки и настройки Docker хранилища.

Установка и настройка Docker хранилища

В командной строке Docker очень полезен когда вы запускаете и тестируете контейнеры. Но для больших развёртываний, включающих несколько параллельно выполняющихся контейнеров, она оказывается очень громоздкой.
С помощью Docker Compose вы можете создать один файл .yml для настройки конфигурации каждого контейнера и данных, которые нужны контейнерам для взаимодействия друг с другом. Вы можете использовать docker-compose как инструмент командной строки для выдачи команд всем компонентам, составляющих ваше приложение, и управления ими как группой.
Docker хранилище само по себе является приложением с несколькими компонентами, поэтому для управления им вы будете использовать Docker Compose. Для запуска хранилища, необходимо будет создать файл docker-compose.yml, который определит как хранилище, так и расположение на диске, где оно будет хранить данные.
На сервере-хосте создайте каталог docker-registry для хранения настроек и перейдите в него:
$ mkdir ~/docker-registry
$ cd ~/docker-registry
Там создайте каталог data, где будут храниться образы:
$ mkdir data
Создайте файл docker-compose.yml:
$ sudo nano docker-compose.yml
Добавьте в него следующие строки, которые определят основной экземпляр хранилища:
version: '3'

services:
  registry:
    image: registry:2
    ports:
    - "5000:5000"
    environment:
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - ./data:/data
Здесь:

  • registry – название первой службы;
  • registry:2 – определение образа в registry версии 2;
  • ports – сопоставление порта 5000 на хосте порту 5000 на контейнере;
  • REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY – переменная, определяющая каталог для хранения данных;
  • volumes – сопоставление каталога /data на сервере-хосте каталогу /data в контейнере.

Теперь запустите созданную конструкцию:
$ docker-compose up
Контейнер и его зависимости будут загружены и запущены:
Обратите внимание на сообщение No HTTP secret provided. К нему мы ещё вернёмся в данном руководстве. Последняя строка содержит сообщение о том, что запущено прослушивание на порту 5000.
Чтобы прервать выполнение команды, нажмите Ctrl+C.

Настройка переадресации портов Nginx

Ранее, мы уже настроили доступ к нашему домену по протоколу HTTPS. Чтобы открыть защищённое хранилище, вам необходимо будет всего лишь настроить Nginx в части перенаправления трафика от домена к контейнеру хранилища.
Откройте для редактирования созданный ранее файл, содержащий настройки вашего сервера:
$ sudo nano /etc/nginx/sites-available/my-domain.host
Найдите там блок location:
Необходимо переадресовать трафик на порт 5000, на котором хранилище будет слушать трафик. Также, можно добавить заголовки к запросам, направленным хранилищу, которое от имени сервера предоставляет дополнительную информацию о самом запросе. Замените содержимое блока location следующими строками:
location / {
    # Do not allow connections from docker 1.5 and earlier
    # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
    if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
      return 404;
    }

    proxy_pass                          http://localhost:5000;
    proxy_set_header  Host              $http_host;   # required for docker client's sake
    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_read_timeout                  900;
}
Блок if проверяет пользовательский агент запроса, верифицирует версию клиента Docker выше 1.5 и определяет, что это не приложение Go, которое пытается получить доступ.

Сохраните файл перед его закрытием. Для применения новых настроек перезапустите Nginx:
$ sudo systemctl restart nginx
Чтобы убедиться, что Nginx правильно перенаправляет трафик в контейнер хранилища на порту 5000, запустите его:
$ cd ~/docker-registry
$ docker-compose up
И теперь, наберите в браузере:
https://my-domain.host/v2
Вы увидите пустой объект JSON:
В терминале же вы сможете видеть, что в последней строке запрос GET был сделан в /v2/. Это указывает на какую конечную точку вы отправили запрос из своего браузера. Другими словами, контейнер получил запрос, который вы сделали, и вернул ответ {}. Код http.response.status=200 в последних строках означает, что контейнер справился с запросом успешно.
Чтобы прервать выполнение команды, нажмите Ctrl+C.

Настройка аутентификации

Nginx позволяет настроить аутентификацию HTTP для сайтов, которыми Nginx управляет. Именно это вы можете использовать для ограничения доступа к вашему хранилищу Docker. Чтобы этого добиться, необходимо создать файл аутентификации при помощи htpasswd и добавить в него комбинацию пользователь-пароль.
Утилиту htpasswd вы можете получить, например, установив пакет apache2-utils:
$ sudo apt install apache2-utils
Теперь необходимо сохранить файл проверки подлинности с учетными данными в ~/docker-registry/auth/:
$ mkdir ~/docker-registry/auth
$ cd ~/docker-registry/auth
На следующем шаге создайте своего первого пользователя, заменив user1 на какое-нибудь своё имя учётной записи. Здесь, опция -B нужна для применения алгоритма bcrypt:
$ htpasswd -Bc registry.password user1
Далее, введите пароль для создаваемого пользователя:
Теперь учётная запись user1 и её пароль добавлены в registry.password.

Если вам необходимо добавить других пользователей, повторите предыдущую команду, но с другим именем пользователя и без использования флага -c, который отвечает за создание нового файла:
$ htpasswd -B registry.password user2
Теперь нужно заставить Docker использовать созданный файл аутентификации. Для этого отредактируйте его:
$ sudo nano ~/docker-registry/docker-compose.yml
Добавьте в файл выделенные строки:
version: '3'

services:
  registry:
    image: registry:2
    ports:
    - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - ./auth:/auth
      - ./data:/data
Таким образом, мы добавили переменные окружения, указывающие на необходимость использования аутентификации HTTP, и указали путь к созданному файлу htpasswd. Для REGISTRY_AUTH мы указали значение htpasswd, которое аутентифицирует используемую схему. Переменная REGISTRY_AUTH_HTPASSWD_PATH содержит путь к файлу аутентификации. Переменная REGISTRY_AUTH_HTPASSWD_REALM означает имя области htpasswd.

Также, мы смонтировали каталог ./auth/, чтобы сделать файл доступным внутри контейнера хранилища. Закройте файл сохранив внесённые изменения.

Теперь можно проверить корректность работы системы аутентификации:
$ cd ~/docker-registry
$ docker-compose up
После этого, обновите страницу браузера, в котором открыт ваш домен. Система должна попросить вас ввести имя пользователя и пароль:
Введите учётные данные и, в случае успешной аутентификации, вы увидите пустой объект JSON:
Это означает, что вы успешно прошли проверку подлинности и получили доступ к хранилищу.
Как и раньше, выход осуществляется через Ctrl+C.
Теперь ваше хранилище защищено, и доступ к нему можно осуществить только после успешной проверки подлинности. Далее, мы настроим его на запуск в фоновом режиме, при этом хранилище будет устойчиво к перезагрузкам, запускаясь каждый раз автоматически.

Запуск Docker хранилища как службы

Для того, чтобы контейнер хранилища запускался каждый раз при загрузке системы или после её сбоя, необходимо проинструктировать Docker Compose всегда поддерживать его работу.
Отредактируйте файл docker-compose.yml:
$ sudo nano ~/docker-registry/docker-compose.yml
Добавьте в файл выделенную строку:
version: '3'

services:
  registry:
    restart: always
    image: registry:2
    ports:
    - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - ./auth:/auth
      - ./data:/data
Настройка restart гарантирует нам, что контейнер выдержит перезапуск системы. Сохраните изменения и закройте файл.

А теперь, запустите ваше хранилище как фоновый процесс при помощи опции -d:
$ docker-compose up -d
С этого момента ваше хранилище работает в фоновом режиме, и поэтому, вы можете спокойно закрыть сессию SSH, и даже перезагрузить сервер. Это не окажет на хранилище никакого эффекта.

Увеличение размера загружаемых файлов для Nginx

Перед тем, как помещать образ в хранилище, необходимо убедиться, что наше хранилище сможет обрабатывать загрузку файлов большого объёма.
По умолчанию предельный размер загружаемого файла в Nginx составляет 1MB. Этого явно не достаточно для образа Docker. Чтобы это исправить, необходимо внести изменения в главный конфигурационный файл Nginx. Он расположен в директории /etc/nginx/. Откройте его для редактирования, набрав в командной строке:
$ cd /etc/nginx
$ sudo nano nginx.conf
Найдите там секцию http и добавьте в неё выделенную строку:
http {
        ##
        # Basic Settings
        ##
        client_max_body_size 16384m;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
Значение параметра client_max_body_size теперь установлено в 16384 MB. Это означает, что максимальный размер загружаемого файла равно 16GB.
Закройте файл с сохранением изменений и перезапустите Nginx:
$ sudo systemctl restart nginx

Публикация в Docker хранилище

Настало время попробовать загрузить образ на наше хранилище. Так как у нас пока нет доступных образов, мы можем, в качестве теста, использовать образ ubuntu, который можно скачать с общедоступного Docker хранилища.
Действия по публикации образа в наше хранилище мы будем производить на нашем втором сервере, который мы обозначили как сервер-клиент.
Для начала, чтобы на этом сервере запускать команду docker не используя полномочия sudo, необходимо добавить вашего пользователя в группу docker:
$ sudo usermod -aG docker your-user
Теперь, для того, чтобы загрузить образ ubuntu, запустить его и получить доступ к его оболочке, в командной строке вашего сервера-клиента наберите:
$ docker run -t -i ubuntu /bin/bash
Опции -i и -t позволяют вам получить интерактивный доступ к оболочке внутри контейнера.
Теперь, когда вы подключились к оболочке, в корне системы создайте файл my.IMAGE:
root@2b8dcf17a0db:/# touch /my.IMAGE
Создав такой файл, мы изменили первоначальный контейнер. Наличие этого файла позже позволит нам понять, что мы имеем дело именно с точно таким же контейнером.
Выход из оболочки контейнера осуществляется при помощи CNTL D, либо командой:
root@2b8dcf17a0db:/# exit
Далее, создайте новый образ из контейнера, в который вы только что внесли изменения:
$ docker commit $(docker ps -lq) new-ubuntu-image
Новый образ теперь доступен локально, и, значит, вы можете передать его в ваше хранилище. Сначала подключитесь к нему:
$ docker login https://my-domain.host
Введите имя пользователя и пароль для получения доступа к контейнеру. В нашем случае, это данные учётной записи user1.

В случае успешной аутентификации система выведет подобное сообщение:
Теперь, когда вы подключились, переименуйте созданный образ:
$ docker tag new-ubuntu-image my-domain.host/new-ubuntu-image
И наконец, передайте только что отмеченный образ в ваше хранилище:
$ docker push my-domain.host/new-ubuntu-image
Вы получите вывод, аналогичный следующему:
После того, как мы отправили образ в хранилище, давайте лишние образы удалим. Сначала необходимо вывести на экран список локально доступных образов:
$ docker images
Мы удалим все образы, кроме первоначально загруженного образа ubuntu:
$ docker rmi new-ubuntu-image
$ docker rmi my-domain.host/new-ubuntu-image
Если ещё раз вывести список образов, то вы увидите, что в списке остался только образ ubuntu:
$ docker images
Таким образом, мы проверили, как хранилище обрабатывает аутентификацию пользователей при их подключении и как позволяет пользователям, прошедшим проверку, передавать образ в хранилище. Теперь попробуем забрать образ из нашего хранилища.

Извлечение из Docker хранилища

Для загрузки образа из хранилища подключитесь к нему набрав на вашем сервере-клиенте команду:
$ docker login https://my-domain.host
Не забывайте использовать домен своего сервера-хоста вместо my-domain.host.
Для того, чтобы принять файл образа new-ubuntu-image, выполните следующую команду:
$ docker pull my-domain.host/new-ubuntu-image
После того, как образ загрузится, выполните команду для просмотра списка имеющихся на сервере-клиенте образов:
$ docker images
Здесь вы видите, что образ new-ubuntu-image снова присутствует на сервере-клиенте. Значит, к нему можно подключиться:
$ docker run -t -i my-domain.host/new-ubuntu-image
Подключившись, посмотрите содержимое корня системы:
root@65c69421278c:/# ls -l
Увидев файл my.IMAGE, можно сделать вывод, что это – экземпляр именно того образа, в который мы вносили коррективы перед загрузкой в наше хранилище.
Закрыть оболочку можно командой exit, либо нажав Ctrl+D.

Заключение

В данном руководстве мы увидели, как развернуть своё приватное Docker хранилище на сервере, работающем под управлением Ubuntu 20.04. Мы защитили подключение к развёрнутому хранилищу при помощи системы проверки подлинности учётных записей. А также, мы попробовали создать контейнер с образом Docker, передать его в хранилище и снова загрузить на сервер, использовавшийся нами как клиент нашего Docker хранилища.