运行 docker-in-docker 时的 Jenkins 声明性管道问题

Jenkins declarative pipeline problem when running docker-in-docker

我刚在 运行在 Jenkins 服务器上 运行ning 内部 Docker 上使用 Jenkins 声明性管道时遇到问题,可以访问 docker.sock来自楼主。

管道的结构比较简单:

pipeline {
    agent {
        docker { image 'gradle:jdk11' }
    }
    stages {
        stage('Checkout') {
            steps {
                // ...
            }
        }
        stage('Assemble public API documentation') {
            environment {
                // ...
            }
            steps {
                // ...
            }
        }
        stage('Generate documentation') {
            steps {
                // ...
            }
        }
        stage('Upload documentation to Firebase') {
            agent {
                docker {
                    image 'node:12'
                    reuseNode false
                }
            }
            steps {
                // ...
            }
        }
    }
}

思路是运行在第一个容器中分三个阶段,然后为最后一个阶段创建一个新的容器。 进入最后阶段打印如下:

[Pipeline] stage
[Pipeline] { (Upload documentation to Firebase)
[Pipeline] getContext
[Pipeline] isUnix
[Pipeline] sh
+ docker inspect -f . node:12
/var/jenkins_home/workspace/publish_public_api_doc@tmp/durable-bc4d65d1/script.sh: 1: /var/jenkins_home/workspace/publish_public_api_doc@tmp/durable-bc4d65d1/script.sh: docker: not found
[Pipeline] isUnix
[Pipeline] sh
+ docker pull node:12
/var/jenkins_home/workspace/publish_public_api_doc@tmp/durable-297d223a/script.sh: 1: /var/jenkins_home/workspace/publish_public_api_doc@tmp/durable-297d223a/script.sh: docker: not found
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
$ docker stop --time=1 367647f97c9eed52bf85c13c2bc2203bb7194adac803d37cab0e0d0435325efa
$ docker rm -f 367647f97c9eed52bf85c13c2bc2203bb7194adac803d37cab0e0d0435325efa
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
ERROR: script returned exit code 127
Finished: FAILURE

我真的不明白这里发生了什么。 为了对此进行调试,我登录了那台机器,并从主机以及 运行ning Jenkins 容器内部 运行 docker 命令,它正在运行。 设置方式是 Docker 客户端安装在镜像中,即二进制文件本身不共享到容器中。 由于 docker 命令“未找到”,我唯一的解释是 docker 命令 启动最后阶段的代理 未执行在“顶级”Jenkins 容器中,但在 JDK 容器中,其中没有 docker 可执行文件。 然而,这似乎是出乎意料的,如果不是错误的话。 如果有人对此有所了解,我将不胜感激。

Jenkins 管道 agents/nodes
您的管道已在 top-most 级别为 运行 指定代理。管道将在该代理上(或在您的场景中的 docker 容器内)执行所有命令,直到指定另一个代理。指定新代理时,top-level 代理将通过某种协议连接到它,新代理将执行此代理范围内的所有管道 stages/steps。一旦超出范围,与新代理的连接将关闭,top-level 代理将再次执行所有命令。

错误原因是什么?
第四阶段尝试将执行上下文更改为新代理。当前代理,即 gradle:jdk11 容器,将执行连接到这个新代理的步骤。由于新代理是一个 docker 容器,gradle:jdk11 容器将尝试使用 docker 命令本身来启动新容器。
正如您所怀疑的那样,此容器中没有 docker binary/service。

为什么这是预期的行为?
假设顶级代理是通过 tcp 或 ssh 连接的不同物理机,而不是 docker 容器。这台机器需要安装所有工具,用于编译、生成文档、运行 单元测试等。它不会使用安装在 Jenkins master 上的 doxygen 二进制文件,因为它应该自己提供这个(如果 $PATH 中不存在 doxygen 则抛出错误)。同样,这台机器需要 docker 安装程序才能在第四阶段启动容器。

如何让我的管道工作?

  • 您可以创建自己的继承自 gradle:jdk11 的自定义 docker 映像并共享主机系统的 docker。这将允许您的自定义图像旋转第四阶段所需的 docker 图像。您将在全球范围内使用 agent { docker { image 'my-custom-img' } }

  • 或者,您可以在全局范围内使用主代理(或其他物理机器),并让每个阶段启动自己的容器。每个阶段都会有一个干净的工作环境,因此您需要使用 stash/unstash 或已安装的卷在阶段之间共享 src/docs。