我不想让相同类型(相同存储库)的两个作业在同一个节点上并行运行。
如何在 Jenkinsfile 中使用 groovy 来做到这一点?
https://stackoverflow.com/a/43963315/6839445中提供的答案已弃用。
当前禁用并发构建的方法是设置选项:
options { disableConcurrentBuilds() }
详细说明可在此处获得: https ://jenkins.io/doc/book/pipeline/syntax/#options
你得到了 disableConcurrentBuilds 属性:
properties properties: [
...
disableConcurrentBuilds(),
...
]
然后工作将等待较旧的先完成
另一种方法是使用可锁定资源插件: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/
在声明性管道语法中使用选项块的示例:
pipeline {
options {
disableConcurrentBuilds()
}
...
}
我认为解决这个问题的方法不止一种。
lock
Execute concurrent builds if necessary
。node
或。label
1
?“ 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')
}
从管道阶段视图中,您将能够欣赏到这一点:
但是,请注意,这仅适用于node
块内的throttle
块。我确实有其他管道,我首先分配一个节点,然后做一些不需要节流的工作,然后做一些需要节流的工作。
node('some-node-label') {
//do some concurrent work
//This WILL NOT work.
throttle(['my-throttle-category']) {
//do some non-concurrent work
}
}
在这种情况下,throttle
步骤并不能解决问题,因为throttle
步骤是步骤内部的node
步骤,而不是相反的步骤。在这种情况下,锁定步骤更适合该任务
安装 Jenkins可锁定资源插件。
在您的管道脚本中,将该部分包装在锁定块中,并为该可锁定资源命名。
lock("test-server"){
// your steps here
}
使用您要锁定的任何资源的名称。以我的经验,它通常是一个测试服务器或测试数据库。
如果您像我的团队一样,那么您喜欢让管道脚本分阶段触发的用户友好的参数化 Jenkins 作业,而不是维护所有声明性/常规的汤。不幸的是,这意味着每个管道构建占用 2+ 个执行器插槽(一个用于管道脚本,另一个用于触发的作业),因此死锁的危险变得非常真实。
我到处寻找解决该困境的方法,并且disableConcurrentBuilds()
只阻止同一个作业(分支)运行两次。它不会使不同分支的管道构建排队等待,而不是占用宝贵的执行器插槽。
对我们来说,一个 hacky(但令人惊讶的优雅)解决方案是将主节点的执行程序限制为 1,并使管道脚本坚持使用它(并且只使用它),然后将本地从代理连接到 Jenkins 以照顾所有其他工作。
一种选择是使用 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”插件支持 Pipeline之前,一个解决方案是使用您的工作所需的标签有效地运行 master 的一个 executor。
为此,请在 Jenkins 中创建一个新节点,例如连接到 localhost 的 SSH 节点。您还可以根据您的设置使用命令选项来运行 slave.jar/swarm.jar。给节点一个执行者和一个像“resource-foo”这样的标签,并给你的工作这个标签。现在一次只能运行一个标签为“resource-foo”的作业,因为只有一个带有该标签的执行者。如果您将节点设置为尽可能多地使用(默认)并将主执行器的数量减少一个,那么它的行为应该完全符合预期,而不会更改总执行器。