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

    Микрозаметка о том, как запустить 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
      0

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

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

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

        0

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

          0

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

          0

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

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое