Развертывание self-hosted сервиса при помощи Docker Compose

В этой заметке я постараюсь описать набор шагов, которые я выполняю, когда хочу запустить какой-нибудь понравившийся мне новый self-hosted сервис. Но, для начала, пара слов о том, что я не буду описывать. Назовем это начальными требованиями и будем считать, что это уже все выполнено.

Итак, у меня хостится несколько сервисов. В основном, они размещены на двух виртуалках, обе созданы и работают под управлением VirtualBox на моем домашем сервере. В качестве такового выступает старенький ноутбук HP TouchSmart TM2, c i3 процессором, 8 гигабайтами памяти и 500 гигабайтовым жестким диском (примерно такой). Работает этот ноут под управлением Windows 10 Pro. На обеих виртуалках в качестве гостевой операционки используется Debian Jessie. На каждой установлен Docker и Docker Compose.

Теперь, когда я расписал некоторую часть моей инфраструктуры, пришло время рассказать о том, как я устанавливаю и запускаю очередной нужный мне self-hosted сервис.

Шаг №0.

Предполагаем, что работа на виртуалках изначально ведется от имени пользователя root. В противном случае, необходимо дать нужному пользователю определенные права, например, включить его в группу sudo, и работать с помощью sudo

Шаг №1.

Создаем переменную окружения, которая будет содержать имя создаваемого пользователя. Для этого выполняем следующую команду:

export NEWUSER_VAR=username

username - имя создаваемого пользователя. Переменную создаем для того, чтобы при выполнении последующих команд использовать ее, а не указывать каждый раз имя пользователя. Шаг этот, конечно же, не является обязательным и его можно пропустить.

Шаг №2.

Создаем нового пользователя (и группу), от имени которого будет работать новый сервис. Делаем это, исходя из соображений безопасности, хотя, может, это и не так уж и нужно. Создание производится при помощи следующей команды:

useradd -d /home/$NEWUSER_VAR -m -s /bin/bash $NEWUSER_VAR

Вместо указания имени пользователя, в этой команде используется переменная окружения, созданная на предыдущем шаге. Конечно, вместо переменной можно использовать и явное имя пользователя. В результате выполнения получаем нового пользователя, для которого будет создан домашний каталог и ассоциирован bash в качестве shell.

Шаг №3.

Включаем только что созданного пользователя в группу docker, чтобы ему стал доступен командный интерфейс Docker. Используем для этого следующую команду:

usermod -aG docker $NEWUSER_VAR

Опять же, вместо переменной можно использовать имя пользователя, созданного на предыдущем шаге.

Шаг №4.

Переключаемся на работу из под только что созданного пользователя:

su -l $NEWUSER_VAR

С переменной - все как обычно: хотите - используете ее; не хотите - используете имя пользователя. Переключение на работу под вновь созданным пользователем автоматически сделает текущим каталогом его домашний каталог

Шаг №5.

Создаем новый файл docker-compose.yml при помощи редактора nano:

nano docker-compose.yml

Новый Compose файл будет создан в домашнем каталоге пользователя.

Шаг №6.

Наполняем создаваемый файл смыслом: довольно часто разработчик ПО сам приводит необходимые примеры Compose файлов, тогда содержимое надо просто скопировать, ну и, заменить некоторые значения параметров на нужные вам. Если же разработчик ограничился только примерами запуска из командной строки, то можно перевести параметры этой командной строки в содержимое файла. Если же и таких примеров разработчик не привел, то, во-первых, следует задуматься, а тот ли образ диска был выбран - больно уж небрежен его автор, а, во-вторых, можно изучить dockerfile, используемый для построения образа диска, на предмет определения параметров запуска контейнера.

Шаг №7.

Сохраняем содержимое файла

Шаг №8.

Проверяем, не сделали ли какой-либо грубой ошибки в синтаксисе:

docker-compose config

Шаг №9.

Загружаем образ диска к себе:

docker-compose pull

Шаг №10.

Запускаем новый сервис:

docker-compose up -d

После этих всех шагов вам останется только настроить ваш новый сервис (если такая процедура в нем предусмотрена) и можно им пользоваться по полной (по крайней мере, в вашей локальной сети).

Конечно, самый "странный" шаг в описанном алгоритме, это Шаг №6. Тут все очень смутно. Но, со своей стороны, я постараюсь опубликовать Compose файлы для сервисов, которые успешно работают у меня. Конечно, только в качестве примеров.

Приведенную последовательность команд можно постараться превратить в командный файл, для еще большего удобства и автоматизации. Единственное, надо понять, что делать с пресловутым шагом №6. Я например, могу предложить такой вариант:

  • подготовить нужный Compose файл заранее, в каком-нибудь каталоге, и назвать его, скажем, new_user_name.yml. Еестественно, вместо new_user_name следует использовать имя нашего нового пользователя
  • в командном файле, еще до того, как будет произведена смена пользователя на Шаге №4, предусмотреть копирование этого файла в нужный домашний каталог, с переименованием в docker-compose.yml
  • сразу после копирования файла произвести смену владельца docker-compose.yml на нового пользователя

Далее, алгоритм остается без изменений. Приведу пример командного файла, который использую сам. Хочу отметить, что запускается этот командный файл от имени моего пользователя на том компьютере, на котором разворачивается сервис, из каталога этого самого пользователя. Кроме того, я добавил его (пользователя) в группу wheel, чтобы не вбивать каждый раз пароль, когда вызывается команда su.

Итак, командный файл:

#!/bin/bash
# Шаг №0 пропускаем, работаем из-под собственного пользователя
# Шаг №1 пропускаем, вместо переменной окружения имя создаваемого пользователя передаем в командный файл через параметр
# Шаг №2, создаем пользователя, от имени которого будет запускаться контейнер с сервисом
su -c "useradd -d /home/$1 -m -s /bin/bash $1"
# Шаг №3, добавляем его в группу docker
su -c "usermod -aG docker $1"
# Шаг №4 пропускаем, будем продолжать использовать команду su
# Вместо Шагов №5, №6 и №7 - копируем заранее подготовленный и проверенный compose файл в каталог нового пользователя 
su -c "cp $1.yml /home/$1/docker-compose.yml"
# ... и назначаем этому файлу правильного владельца
su -c "chown $1:$1 /home/$1/docker-compose.yml"
# Этот каталог я создаю для удобства - все монтируемые из контейнера (или в контейнер, кому как больше нравится) каталоги будут размещаться в нем
su -c "mkdir /home/$1/vol" $1
# Шаг №8 пропускаем, мы использовали заранее подготовленный и проверенный compose файл
# Шаг №9 - загружаем образ диска к себе
su -c "docker-compose -f /home/$1/docker-compose.yml pull" $1
# Шаг №10 - запускаем новый сервис
su -c "docker-compose -f /home/$1/docker-compose.yml up -d" $1

Обратите внимание на создание каталога vol. Довольно часто, для того, чтобы сохранить между перезапусками накопленные в процессе работы контейнера данные, прибегают к такому трюку: монтируют в контейнер физический каталог хоста в качестве каталога с данными (на самом деле таких точек монтирования может быть несколько). В результате, все данные пишутся на хост, что позволяет предохранить их от потери при остановке контейнера, и, дополнительно, облегчает доступ к данным с хоста, например, при создании резервных копий. Вот я и создаю подкаталог для этих целей, так, на всякий случай.

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

Ну и, что называется, под занавес, заострю внимание на том, что данная процедура должна использоваться для каждого разворачиваемого self-hosted сервиса только один раз. В дальнейшем, для остановки и повторного запуска контейнера с сервисом, используются обычные команды Docker Compose, только выполнять их надо от имени того пользователя, который был создан при развертывании.

Вот, пожалуй, и все на сегодня. Но серию публикаций про self-hosted сервисы, да и про Docker с Docker Compose я, пожалуй, буду продолжать.