在Jenkins中,如何在交叉编译环境中使用Docker映像

时间:2019-10-02 15:53:43

标签: bash docker jenkins jenkins-pipeline

我目前正在尝试设置Jenkins构建管道,并且正在努力包括docker容器。我的主要问题是Jenkins将构建步骤发送到正在运行的容器的方式。

背景

我正在构建一个交叉编译项目。这发生在为交叉编译设置的linux中。 SDK提供了所有必需的东西,包括适当的编译器,标头,库等。

要设置所有内容,它还包括一个环境文件,该文件需要由当前的shell来源。该脚本会添加必要的信息,例如添加环境变量(例如,调整$ PATH)或别名。

对于Jenkins构建,我正在创建一个包含SDK的docker映像。图像工作正常,并且在运行容器时能够构建项目。但这不适用于詹金斯。经过一番评估,我发现问题出在詹金斯使用容器的方式上。

问题

Jenkins似乎通过docker run -t -d imagename cat在后​​台启动了Docker映像的容器。然后,使用docker exec发送所有后续的构建步骤。通常没关系。就我而言,由于找不到所有可执行文件(例如cmake),因此出现错误。可执行文件未安装在常规系统根目录中,而是安装在SDK文件夹中。
我向docker映像的入口点脚本添加了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图像来说明问题。

Dockerfile:

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

您甚至可以覆盖Dockerfile中的CMD设置,并使用docker run从容器外部调用foo.sh:

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

但是,当您在后台启动容器时(就像在詹金斯中所做的那样),您将无法再调用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不包含脚本目录/home/test/script

$> docker exec test /home/test/script/foo.sh
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

问题

您知道我可以配置docker映像或Jenkins管道使我的方案正常工作的可能性吗?

我能想到的一种可能的解决方案是将所有构建步骤都包装在外壳脚本中,外壳脚本本身就是环境文件的来源。但是我更喜欢使用Jenkins提供的插件并编写一个干净的Jenkinsfile(即并非所有内容都隐藏在shell脚本中)

另一个解决方案可能是将SDK直接安装在系统根目录中。但是我不确定副作用。此外,我仍然需要为构建步骤设置特定的环境变量。

1 个答案:

答案 0 :(得分:0)

在Dockerfile中使用ENV设置路径:

ENV PATH="/home/test/script:${PATH}"

然后它将与docker rundocker exec一起使用

$> docker exec test foo.sh
/home/test/script:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
相关问题