Возможно ли подружить Gitlab CI + Docker + Systemd +5


Микрозаметка о том, как запустить Docker с Systemd внутри Gitlab CI Runner'a. Возможно кому-то будет полезна, возможно кто-то решал уже подобную задачу другими способами и будет интересно если поделитесь в комментариях.

Предисловие
Был развернут Gitlab Runner внутри Docker контейнера. В какой-то момент появилась идея собирать внутри одного контейнера всю необходимую инфраструктуру (например, PostgreSQL и Tomcat) для установки приложения после этапа компиляции и прогона автотестов. Сам контейнер инфраструктуры был уже собран на основе образа Debian с Systemd и отлично работал. Но при использовании внутри Runner'a начались неожиданные проблемы. Код шага был для простоты допустим будет такой:

run-autotests:
  image: debian/systemd
  before_script:
    - cp backend.jar /opt/
    - cd /opt
  script:
    - java -jar autotests.jar

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

Казалось бы в чем проблема?

Как выяснилось — по свежим issue на самом Gitlab я не один такой столкнулся с этой проблемой.
Проблема в том, что Gitlab Runner для Docker контейнера всегда переписывает команду CMD, т.е. запускает контейнер такой командой:

docker run --cmd /bin/bash ...

И переопределить гитлабовскую CMD нельзя, можно использовать только entrypoint внутри ci скрипта, но танцы с ним ни к чему не привели.

Все роли были покрыты тестами molecule и они успешно проходили тесты внутри GitLab runner'a. Обратив на это внимание я подумал, а почему бы не запускать контейнер с systemd внутри запущенного Runner'a, г*мор, конечно, но результат мне был важнее сложностей. Просто запускать с помощью докеровских команд контейнер можно, но не эффективно, да и обработки ошибок не будет — как-то слишком не предсказуемый результат может получиться, поэтому решил написать маленькую поделку на Python, которая просто будет запускать контейнер, копировать в него архив с нужными файлами и выполнять список команд внутри контейнера.

> Код лежит здесь: GitHub

Запустить можно так:


cd <path-with-code>
pip install virtualenv
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt
python main.py   --image dramaturg/docker-debian-systemd # используемый образ
  [--network host] # тип сети если требуется
  [--volumes] "/sys/fs/cgroup:/sys/fs/cgroup:ro" "<другие>" # данный volume обязателен для systemd, можно дописать свои через пробел
  [--cmd] "/lib/systemd/systemd" # команда, которую нужно выполнить во время запуска, если нужно переопределить из контейнера
  [--data-archive] /opt/data.tar # полный путь до архива *.tar или *.tar.gz
  [--data-unarchive-path] /opt/data/logs # путь куда будет разархивированы файлы, будет создан если не существует
  [--privileged] # для запуска systemd обязателен, вопрос с более низкими привилегиями не рассматривался
  --exec-commands "touch /opt/example.log" "{bash} ls -la /opt" "mkdir -p /opt/tmp" # список команд в кавычках разделенных пробелом

Команды в [] необязательны. Специальный макрос {bash} нужен для команд, которые требуют оболочки, например, ls -la и др. Он будет заменен в процессе выполнения на /bin/bash -c «command».

На Python'e писал первый раз, так что не стоит ругать. Возможно в коде или при запуске возникнут проблемы, попытаюсь быстро исправить. Здесь я попытался объяснить общую простую идею способа запуска. Поделитесь опытом своих решений если возникали похожие проблемы.

Об используемом образе dramaturg/docker-debian-systemd
К данному образу претензий нет, но по началу возникала ошибка, которая всплывала в консоли хостовой машины, что некоторые файлы, которые создает systemd уже существуют. В Nginx сервисе не было такой проблемы, а в PostgreSQL проявлялись. Решением оказалось удаление блока «VOLUME [ „/sys/fs/cgroup“, „/run“, „/run/lock“, „/tmp“ ]», после этого все заработало как часы.




К сожалению, не доступен сервер mySQL