В настоящее время я пытаюсь настроить конвейер сборки 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 непосредственно в корневом каталоге системы. Но я не уверен в побочных эффектах. Кроме того, мне все еще нужны определенные переменные среды, установленные для этапа сборки.