如何防止两个相同类型的管道詹金斯作业在同一节点上并行运行?


72

我不想允许相同类型(相同存储库)的两个作业在同一节点上并行运行。

如何在Jenkinsfile中使用groovy做到这一点?

Answers:


66

https://stackoverflow.com/a/43963315/6839445中提供的答案已被弃用。

当前禁用并发构建的方法是设置选项:

options { disableConcurrentBuilds() }

此处提供了详细说明:https : //jenkins.io/doc/book/pipeline/syntax/#options


9
我正在尝试这样做,并且它在全局范围内禁用并发构建,OP希望在每个节点上禁用并发构建,但仍允许同时进行多个构建,每个可用节点只能进行一次。
量子

这为我们的特定目的而工作,尽管令我感到困扰的是我们必须在管道中进行配置,并且不能以某种方式在全球范围内进行配置。
Trejkaz

42

您获得了disableConcurrentBuilds属性:

properties properties: [
  ...
  disableConcurrentBuilds(),
  ...
]

然后,工作将等待年长的人先完成


3
我认为这是最明智的回应
Dieter Hubau,2017年

不幸的是,不建议使用此解决方案。选项应如另一个答案中所述使用。
托马斯·桑德伯格

3
这到底是什么意思?代码段生成器仍会为脚本化管道显示此选项。您是否有提到的弃用链接?
hypery2k

32

另一种方法是使用可锁定资源插件: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/


使用该代码,我得到以下错误:>>未知阶段节“锁定”。从版本0.5开始,阶段中的步骤必须在“步骤”块中。<<我将“锁定”部分移至“步骤”部分,然后它起作用了。
Erik Nellessen

inversePrecedence: true创建时的含义是lock什么?
Yash

24

在声明式管道语法中使用options块的示例:

pipeline {

  options { 
    disableConcurrentBuilds() 
  }

...
}

15

我认为,解决此问题的方法不止一种。

管道

  • 根据其他答案的建议,使用可锁定资源插件的最新版本及其lock步骤。
  • 如果构建相同的项目:
    • 取消选中Execute concurrent builds if necessary
  • 如果构建其他项目:
    • 设置不同的nodelabel为每个项目。

詹金斯

  • 将节点执行程序的数量限制为1

外挂程式


@sorin您可以在这里更详细地说明您想要完成的工作吗?
luka5z '16

1
限制节点执行程序的数量是不可能的,因为其他作业需要它们。我只想防止并行构建相同的存储库。
索林2016年

1
如何取消任何想法Execute concurrent builds if necessaryJenkinsfile
Somatik '16

2
现在,“节流并发构建插件”确实具有管道支持。请在下面查看我的答案。
Mig82

+1以将节点执行程序的数量限制为1-这是优化Jenkins安装的最佳方法之一。
mkobit

12

自2004年以来,“节流并发构建插件”现在支持管道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步骤内部的步骤,而不是相反的步骤。在这种情况下,锁定步骤更适合任务


我得到“一个或多个指定类别不存在”,您在哪里指定类别?
Giovanni G

我不明白你的意思。
Mig82 '20

8

安装Jenkins可锁定资源插件

在管道脚本中,将零件包装在锁块中,并为该可锁定资源命名。

lock("test-server"){
    // your steps here
}

使用您要锁定的任何资源的名称。以我的经验,它通常是一个测试服务器或测试数据库。


2
只是添加-我发现了锁定功能,但认为它不能解决我的问题。但是,您可以使用变量名来定义锁定资源-使用env.NODE_NAME解决了我需要锁定一个阶段以使其不能在同一节点上同时运行(它可以在不同节点上同时运行)的问题
Ed Mackenzie

如果有内锁异常或超时,它似乎锁定从未发布和其他并发构建保持运行......直到它timesout ... :(不知道,但,这就是我观察
马塞罗德销售

5

如果您像我的团队一样,那么您会喜欢使用用户友好的参数化Jenkins Jobs,这些脚本会分阶段触发管道脚本,而不是保留所有声明性/常规的内容。不幸的是,这意味着每个管道构建占用2个以上的执行器插槽(一个用于管道脚本,另一个用于执行触发的作业),因此死锁的危险变得非常现实。

我到处都在寻找解决这个难题的方法,并且disableConcurrentBuilds()只能防止同一作业(分支)运行两次。它不会使不同分支的管道构建排队等待,而不是占用宝贵的执行器插槽。

对我们来说,一个棘手的(但非常出奇的优雅)解决方案是将主节点的执行程序限制为1,并使流水线脚本坚持使用(并且仅使用),然后将本地从属代理连接到Jenkins,以便照顾所有的人其他工作。


3

一种选择是使用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}"

因此它将等待作业完成(不要与集成测试提及相混淆,这只是作业名称)。

另请注意,在极少数情况下,当两个作业彼此等待时,此脚本可能会导致死锁,因此您可能希望在此处实施一些最大重试策略,而不是无限等待。


1

在“ Throttle Concurrent Builds”插件支持管道之前,一种解决方案是有效地运行具有您工作所需标签的主执行程序。

为此,请在Jenkins中创建一个新节点,例如连接到localhost的SSH节点。您也可以使用命令选项来运行slave.jar / swarm.jar,具体取决于您的设置。给该节点一个执行程序,并给它一个标签,例如“ resource-foo”,并给您的工作分配这个标签。现在一次只能执行一个标签为“ resource-foo”的作业,因为只有一个执行程序带有该标签。如果将节点设置为尽可能多地处于使用状态(默认值),并将主执行器的数量减少一个,则它的行为应与期望的完全相同,而不更改总执行器。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.