В Jenkins как использовать образ Docker в среде кросс-компиляции - PullRequest
0 голосов
/ 02 октября 2019

В настоящее время я пытаюсь настроить конвейер сборки Jenkins и пытаюсь включить докер-контейнер. Моя главная проблема заключается в том, как Jenkins отправляет шаги сборки в работающий контейнер.

Справочная информация

Я строю проект кросс-компиляции. Это происходит в Linux, настроенной для кросс-компиляции. Все необходимые материалы предоставляются SDK, включая соответствующие компиляторы, заголовки, библиотеки и т. Д.

Для настройки всего, он также включает файл среды, который должен быть получен из текущей оболочки. Этот скрипт добавляет необходимую информацию, такую ​​как добавление переменных среды (например, настройка $ PATH) или псевдонимов.

Для сборок Jenkins я создаю образ докера, содержащий SDK. Изображение работает нормально, и я могу построить проект, когда я запускаю контейнер. Но это не работает на Дженкинс. После некоторой оценки я обнаружил, что проблема заключается в том, как Дженкинс использует контейнер.

Проблема

Кажется, Дженкинс запускает контейнер для изображения докера в фоновом режиме через docker run -t -d imagename cat. Затем все последующие шаги сборки отправляются с использованием docker exec. Обычно это нормально. В моем случае я получаю ошибки, так как все исполняемые файлы (например, cmake) не найдены. Исполняемые файлы устанавливаются не в обычный системный корень, а в папку SDK.
Я добавил команду source в сценарий точки входа образа докера. Команда source является источником среды SDK. Поскольку точка входа выполняется только во время run, а не с exec, все этапы сборки приводят к ошибкам, поскольку переменная $PATH установлена ​​неправильно.

Это урезанный файл Jenkinsfile:

pipeline {
    agent {
        docker {
            label 'docker'
            image 'test:01'
        }
    }
    stages {
        stage('build') {
            steps {
                cmakeBuild  installation: "InSearchPath",
                            generator: "Unix Makefile",
                            buildDir: 'build',
                            sourceDir: 'source',
                            steps: [
                                [args: 'all install']
                            ]
            }
         }
    }
}

, что приводит к следующему выводу журнала:

...
[Pipeline] withDockerContainer
$ docker run -t -d -u 1078:1001 -w /jenkins/workspace/project -v /jenkins/workspace/project:/jenkins/workspace/project:rw,z -v /jenkins/workspace/project@tmp:/jenkins/workspace/project@tmp:rw,z  test:01 cat
$ docker top ff1b4ee4e929b83e5741ef7db1e57688f7f996ca3a10a00ae4c5426cf108cb2a -eo pid,comm
...
...
...
[Pipeline] cmakeBuild
[build] $ docker exec --workdir /jenkins/workspace/project/build ff1b4ee4e929b83e5741ef7db1e57688f7f996ca3a10a00ae4c5426cf108cb2a cmake -G "Unix Makefile" /jenkins/workspace/project/source
OCI runtime exec failed: exec failed: container_linux.go:345: starting container process caused "exec: \"cmake\": executable file not found in $PATH": unknown
...

Воспроизводимое изображение

Чтобы узнать больше о поведении, я создал небольшое изображение докера, которое иллюстрирует проблему.

Файл Docker:

FROM debian:jessie

# Add entrypoint script which sources envvars file
RUN echo "#!/bin/bash\n\n\
          . /envvars \n\n\
          exec \"\$@\"\
          " >/entrypoint.sh \
    && chmod ugo+rx /entrypoint.sh

# Add a script foo.sh which returns the content of $PATH
RUN mkdir -p /home/test/script \
    && echo "#!/bin/bash\n\
            echo \$PATH \n\
            " > /home/test/script/foo.sh \
    && chmod ugo+rx /home/test/script/foo.sh

# Add the directory containing the foo.sh script to $PATH
RUN echo "export PATH=/home/test/script:$PATH \n" > /envvars

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/bin/bash"]

Вы можете создать этот образ:

$> docker build -t "test" .
Sending build context to Docker daemon   55.3kB
Step 1/6 : FROM debian:jessie
 ---> c9d6adb06e4d
Step 2/6 : RUN echo "#!/bin/bash\n\n          . /envvars \n\n          exec \"\$@\"          " >/entrypoint.sh     && chmod ugo+rx /entrypoint.sh
 ---> Using cache
 ---> 648affce60a6
Step 3/6 : RUN mkdir -p /home/test/script     && echo "#!/bin/bash\n            echo \$PATH \n            " > /home/test/script/foo.sh     && chmod ugo+rx /home/test/script/foo.sh
 ---> Running in d13391c77668
Removing intermediate container d13391c77668
 ---> 7d88c20f8673
Step 4/6 : RUN echo "export PATH=/home/test/script:$PATH \n" > /envvars
 ---> Running in 55ac66323579
Removing intermediate container 55ac66323579
 ---> 35e081186bfa
Step 5/6 : ENTRYPOINT ["/entrypoint.sh"]
 ---> Running in 53154fed036f
Removing intermediate container 53154fed036f
 ---> 4ca347cbe757
Step 6/6 : CMD ["/bin/bash"]
 ---> Running in a598aefa837b
Removing intermediate container a598aefa837b
 ---> 78fc760bb9db
Successfully built 78fc760bb9db
Successfully tagged test:latest

После создания образа вы можете запустить его в интерактивном режиме. Таким образом, вы находитесь внутри контейнера в оболочке bash и можете напрямую вызывать foo.sh, потому что /home/test/script является частью $PATH:

$> docker run --rm -ti test
root@42529686fe22:/# foo.sh
/home/test/script:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
root@42529686fe22:/# exit

Вы даже можете переопределить настройку CMD из Dockerfileи вызвать foo.sh снаружи контейнера, используя docker run:

$> docker run --rm -t test foo.sh
/home/test/script:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Но , когда вы запускаете контейнер в фоновом режиме (как это делается в Jenkins), вы можете 'Вызовите foo.sh больше:

$> docker run --rm --name test -td test cat
0ecf3efecc54d33b73aaab6b4a1e191056c55597b3fe558ff7a7a6f93db2b695

$> docker exec test foo.sh
OCI runtime exec failed: exec failed: container_linux.go:345: starting container process caused "exec: \"foo.sh\": executable file not found in $PATH": unknown

Хотя вызов foo.sh по абсолютному пути работает. Результат показывает, что $PATH не содержит директории скриптовконвейер, чтобы заставить мой сценарий работать?

Одно возможное решение, о котором я мог подумать, - это завершение всех этапов сборки в сценарии оболочки, которые сами являются источником файла среды. Но я бы предпочел использовать плагины, предоставляемые Jenkins, и написать чистый Jenkinsfile (т. Е. Не все скрыто в скриптах оболочки)

Другим решением может быть установка SDK непосредственно в корневом каталоге системы. Но я не уверен в побочных эффектах. Кроме того, мне все еще нужны определенные переменные среды, установленные для этапа сборки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...