【发布时间】:2016-07-27 00:47:51
【问题描述】:
我不想让相同类型(相同存储库)的两个作业在同一个节点上并行运行。
如何在 Jenkinsfile 中使用 groovy 来做到这一点?
【问题讨论】:
我不想让相同类型(相同存储库)的两个作业在同一个节点上并行运行。
如何在 Jenkinsfile 中使用 groovy 来做到这一点?
【问题讨论】:
我认为解决这个问题的方法不止一种。
lock 步骤,如其他答案中所建议的那样。 Execute concurrent builds if necessary。node 或label。1?【讨论】:
Jenkinsfile 中取消选中Execute concurrent builds if necessary 吗?
其中一个选项是使用 Jenkins REST API。我研究了另一种选择,但似乎这只是一个具有管道功能的可用选项。
您应该编写脚本来轮询 Jenkins 以获取当前正在运行的作业的信息,并检查是否正在运行相同类型的作业。 为此,您应该使用 Jenkins REST API,您可以在 Jenkins 页面的右下角找到文档。 示例脚本:
#!/usr/bin/env bash
# this script waits for integration test build finish
# usage: ./wait-for-tests.sh <jenkins_user_id> <jenkins_user_token_id>
jenkins_user=$1
jenkins_token=$2
build_number=$3
job_name="integration-tests"
branch="develop"
previous_build_number=build_number
let previous_build_number-=1
previous_job_status=$(curl -s http://${jenkins_user}:${jenkins_token}@jenkins.mycompany.com/job/mycompany/job/${job_name}/branch/${branch}/${previous_build_number}/api/json | jq -r '.result')
while [ "$previous_job_status" == "null" ];
do
previous_job_status=$(curl -s http://${jenkins_user}:${jenkins_token}@jenkins.mycompany.com/job/mycompany/job/${job_name}/branch/${branch}/${previous_build_number}/api/json | jq -r '.result')
echo "Waiting for tests completion"
sleep 10
done
echo "Seems that tests are finished."
我在这里使用过 bash,但您可以使用任何语言。 然后在你的 Jenkinsfile 中调用这个脚本:
sh "./wait-for-tests.sh ${env.REMOTE_USER} ${env.REMOTE_TOKEN} ${env.BUILD_NUMBER}"
所以它会等到作业完成(不要与集成测试提及混淆,它只是作业名称)。
还要注意,在极少数情况下,当两个作业相互等待时,此脚本可能会导致死锁,因此您可能希望在此处实施一些最大重试策略,而不是无限等待。
【讨论】:
在“Throttle Concurrent Builds”插件has Pipeline support 之前,一个解决方案是有效地运行一个带有您的工作所需标签的主执行器。
为此,请在 Jenkins 中创建一个新节点,例如连接到 localhost 的 SSH 节点。您还可以根据您的设置使用命令选项来运行 slave.jar/swarm.jar。给节点一个执行者和一个像“resource-foo”这样的标签,并给你的工作这个标签。现在一次只能运行一个标签为“resource-foo”的作业,因为只有一个带有该标签的执行者。如果您将节点设置为尽可能多地使用(默认)并将主执行器的数量减少一,它应该完全按照预期运行,而不需要更改执行器总数。
【讨论】:
另一种方法是使用可锁定资源插件:https://wiki.jenkins-ci.org/display/JENKINS/Lockable+Resources+Plugin
您可以根据需要定义锁(互斥体),并且可以在名称中添加变量。例如。防止多个作业在构建节点上同时使用编译器:
stage('Build') {
lock(resource: "compiler_${env.NODE_NAME}", inversePrecedence: true) {
milestone 1
sh "fastlane build_release"
}
}
因此,如果您想防止每个节点同时运行同一分支的多个作业,您可以执行类似的操作
stage('Build') {
lock(resource: "lock_${env.NODE_NAME}_${env.BRANCH_NAME}", inversePrecedence: true) {
milestone 1
sh "fastlane build_release"
}
}
发件人:https://www.quernus.co.uk/2016/10/19/lockable-resources-jenkins-pipeline-builds/
【讨论】:
lock时inversePrecedence: true是什么意思?
安装 Jenkins Lockable Resources Plugin。
在您的流水线脚本中,将该部分包装在锁定块中,并为该可锁定资源命名。
lock("test-server"){
// your steps here
}
使用您要锁定的任何资源的名称。以我的经验,它通常是一个测试服务器或测试数据库。
【讨论】:
你得到了 disableConcurrentBuilds 属性:
properties properties: [
...
disableConcurrentBuilds(),
...
]
然后该作业将等待较旧的作业先完成
【讨论】:
“Throttle Concurrent Builds Plugin”现在支持自 throttle-concurrents-2.0 以来的管道。所以现在你可以这样做:
启动下面的管道两次,一个接一个,你会看到。您可以通过双击“立即构建” 手动执行此操作,或从另一个作业的parallel 步骤调用它。
stage('pre'){
echo "I can run in parallel"
sleep(time: 10, unit:'SECONDS')
}
throttle(['my-throttle-category']) {
// Because only the node block is really throttled.
echo "I can also run in parallel"
node('some-node-label') {
echo "I can only run alone"
stage('work') {
echo "I also can only run alone"
sleep(time: 10, unit:'SECONDS')
}
}
}
stage('post') {
echo "I can run in parallel again"
// Let's wait enough for the next execution to catch
// up, just to illustrate.
sleep(time: 20, unit:'SECONDS')
}
从管道阶段视图中,您将能够体会到这一点:
但是,请注意,这仅适用于 throttle 块内的 node 块。我确实有其他管道,我首先分配一个节点,然后做一些不需要节流的工作,然后做一些需要节流的工作。
node('some-node-label') {
//do some concurrent work
//This WILL NOT work.
throttle(['my-throttle-category']) {
//do some non-concurrent work
}
}
在这种情况下,throttle 步骤不能解决问题,因为throttle 步骤是node 步骤内部的步骤,而不是相反的步骤。在这种情况下,the lock step 更适合该任务
【讨论】:
https://stackoverflow.com/a/43963315/6839445 中提供的答案已弃用。
目前禁用并发构建的方法是设置选项:
options { disableConcurrentBuilds() }
这里有详细的描述: https://jenkins.io/doc/book/pipeline/syntax/#options
【讨论】:
在声明性管道语法中使用选项块的示例:
pipeline {
options {
disableConcurrentBuilds()
}
...
}
【讨论】:
如果您像我的团队一样,那么您喜欢让管道脚本分阶段触发的用户友好的参数化 Jenkins 作业,而不是维护所有声明性/常规的汤。不幸的是,这意味着每个管道构建占用 2+ 个执行器插槽(一个用于管道脚本,另一个用于触发的作业),因此死锁的危险变得非常真实。
我到处寻找解决这个困境的方法,disableConcurrentBuilds() 只会阻止同一个作业(分支)运行两次。它不会使不同分支的管道构建排队等待,而不是占用宝贵的执行器插槽。
对我们来说,一个 hacky(但令人惊讶的优雅)解决方案是将主节点的执行程序限制为 1,并使管道脚本坚持使用它(并且只使用它),然后将本地从代理连接到 Jenkins 以便采取照顾所有其他工作。
【讨论】: