Микрозаметка о том, как запустить 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“ ]», после этого все заработало как часы.

Комментарии (7)


  1. Laney1
    08.06.2018 11:03

    выглядит как жуткий костыль. Разве это правильно, пихать все постгресы и томкаты в один контейнер? И что там вообще делает systemd? А если без systemd никак не обойтись, то разве не разумнее запускать контейнеры с его помощью (systemd-nspawn, racket, etc)? Тогда и с логами проблем не будет.


    1. gecube
      08.06.2018 12:26

      Полностью поддерживаю. Более того — gitlab позволяет запускать внешние сервисы в рамках CI-пайплайна ИМЕННО ДЛЯ ЭТИХ целей.
      Примеры есть по ссылке docs.gitlab.com/ee/ci/examples/php.html#use-databases-or-other-services


    1. MSerega Автор
      08.06.2018 15:58

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


      1. Kane
        08.06.2018 19:49

        Возможно для этого вам больше подойдёт VirtualBox executor docs.gitlab.com/runner/executors/virtualbox.html


  1. VolCh
    08.06.2018 14:55

    А пробовали вариант в самом образе типа ENTRYPOINT ["/bin/systemd"] ?


    1. MSerega Автор
      08.06.2018 15:54

      Да, с entrypoint пробовал разные варианты, но либо systemd корректно не стартовал, либо gitlab runner не мог в контейнер подцепиться. Возможно что-то делал не правильно, поэтому было бы интересно увидеть успешный опыт других.


  1. MSerega Автор
    08.06.2018 15:59

    В первом комментарии описал суть проблемы.