Jenkins 进阶篇 - 任务关联

2021-09-22

有时候我们的一个任务里面会进行很多的步骤,例如构建一个后端的 Java 服务,可能会有代码静态扫描,静态扫描通过后会打包成 jar 或者 war 文件,打包成功后可能还会对制品进行存档备份,然后可能会制作容器镜像……,这些所有的内容完全可以在一个任务中完成,也可以拆分成多个任务分步骤进行。但是将复杂任务拆分成多个任务后,会有一些附带的其他问题,可能没法很好地利用 Jenkins 的多节点优势,当你的第二个任务要使用第一个任务的工作空间的文件时,你必须要确保这两个任务要在同一个 Jenkins 节点上执行,因为如果两个任务不在同一个节点上运行,但是第二个任务又需要使用第一个任务的工作空间的文件时就会出问题,第二个任务找不到第一个任务的工作空间。

所以若非必要就不要对任务进行拆分,如果要拆分那就要确保关联的任务都在同一个节点运行,或者任务之间没有对工作空间的依赖。对于如何确保关联任务在同一个节点运行,可以使用 Node and Label parameter 插件来实现。接下来就演示关联任务的构建与参数传递。

我们创建两个自由风格的任务,任务 a 和 b 。在做关联任务配置之前我们先安装两个 Jenkins 插件:Parameterized TriggerNode and Label parameterParameterized Trigger 是用于关联任务之间的触发和参数传递, Node and Label parameter 是用于解决关联任务绑定到同一个节点执行。插件安装完成后重启下 Jenkins 服务,用拥有管理权限的 Jenkins 账号访问 http://<jenkins-host>/restart 即可,等待重启完成后开始对两个关联任务进行配置。

创建两个自由风格的任务:image.png
a 任务的配置如下,其中【构建】部分的 shell 命令后面两行先注释屏蔽不执行。同时在【构建】里面增加一个步骤【Trigger/call builds on other projects】用来触发其他任务的构建,填入子任务的名称和要传递给子任务的参数,如果子任务有多个则用英文逗号分隔。

# a任务构建部分增加执行shell命令,内容如下
echo "检查参数:"
echo "release_tag=${release_tag}, target_platform=${target_platform}, run_test=${run_test}"
# echo "Hello Jenkins" >> a.txt
# cat a.txt

image.png
b 任务也简单,就接收打印 a 任务传过来的参数值,以及看看 a 任务生成的文件 a.txt 里面的内容,执行的 shell 命令如下,暂时先将最后一行注释掉屏蔽执行。

echo "接收到的参数:"
echo "release_tag=${release_tag}, target_platform=${target_platform}, run_test=${run_test}, parent_workspace=${parent_workspace}"
# cat ${parent_workspace}/a.txt

image.png
这样任务 a、b 都配置好了,构建任务 a 成功后会触发任务 b 的执行,当然任务 b 也可以自己单独构建执行,参数内容也是由任务 b 自己掌控填写。

单独构建任务 b,构建时的参数输入和输出的参数信息如下:
image.png
image.png

现在构建 a 任务,各个参数值如下:
image.png

看看构建日志,我们会发现 a 任务是在主节点 master 上执行的,并且构建完成后触发构建了 b 任务:
image.png
b 任务的执行日志如下,从日志可以发现 b 任务是由上游任务 a 触发构建,并且是在另一个 Jenkins 节点上执行的,接收到的参数也是从 a 任务传递过来的。
image.png
现在我们将 a 、b 任务里面执行 shell 命令后面的文件操作取消注释,让其执行文件操作相关命令:

# a 任务的 shell 命令内容
echo "检查参数:"
echo "release_tag=${release_tag}, target_platform=${target_platform}, run_test=${run_test}"
echo "Hello Jenkins" >> a.txt
cat a.txt


# b 任务的 shell 命令内容
echo "接收到的参数:"
echo "release_tag=${release_tag}, target_platform=${target_platform}, run_test=${run_test}, parent_workspace=${parent_workspace}"
# cat ${parent_workspace}/a.txt

同样的,a 任务执行完后触发了 b 任务,但是 b 任务执行失败了,因为 Jenkins 会根据各个节点的压力负载情况,将任务 a、b 分别在不同的 Jenkins 节点上运行,当 b 任务查看 a 任务工作空间的文件时找不到文件,那么 b 任务就执行失败了。
image.png
要解决这个问题就要保证各个关联任务不依赖于各个任务的工作空间的文件,如果关联任务又确实需要依赖工作空间的文件,那就要将任务绑定在同一个节点运行,这就需要用 Node and Label parameter 插件来实现,该插件可以通过一个参数来定义任务要在哪个节点上运行。例如,如果我们的 a 任务在 master 节点上运行了,那么 b 任务就也要在 master 节点上运行,才能正常访问 a 任务工作空间的文件。

我们将 a 任务配置里面【构建】步骤中【Trigger/call builds on other projects】的参数传递配置做下调整,将 a 任务的构建节点名称传给 b 任务,让 b 任务也在同一个节点上运行。

release_tag=${release_tag}
target_platform=${target_platform}
run_test=${run_test}
parent_workspace=${WORKSPACE}
patent_node_name=${NODE_NAME}

然后在 b 任务增加一个 Label 或者 Node 参数,控制任务在哪个节点运行,Label 参数是根据节点标签来匹配节点,而 Node 参数则是根据节点的名称来匹配节点,我这里添加了一个 Label 参数,设置如下:
image.png
image.png

做好节点绑定参数的设置后我们多次构建 a 任务,看看 a 、b 任务最后的执行日志。
image.png
image.png
image.png
image.png
从构建的执行日志上,我们可以看到任务 a、b 是已经绑定在同一个 Jenkins 节点上运行了。另外这一小节使用了一些 Jenkins 内置的环境变量,WORKSPACE 和 NODE_LABELS,还有一些其他的内置环境变量可以浏览器访问 http://<jenkins-host>/env-vars.html 进行查看,这些环境变量可以在任务配置时直接获取使用。
image.png