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

Posted

技术标签:

【中文标题】如何防止相同类型的两个管道 jenkins 作业在同一节点上并行运行?【英文标题】:How do I prevent two pipeline jenkins jobs of the same type to run in parallel on the same node? 【发布时间】:2016-07-27 00:47:51 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

我认为解决这个问题的方法不止一种。

管道

使用最新版本的Lockable Resources Plugin 及其lock 步骤,如其他答案中所建议的那样。 如果构建相同的项目: 取消选中Execute concurrent builds if necessary。 如果构建不同的项目: 为每个项目设置不同的nodelabel

詹金斯

将节点的执行者数量限制为1

插件

Build Blocker Plugin - 据说支持 Pipeline 项目 Throttle Concurrent Builds Plugin - 与 Pipeline 项目不兼容

【讨论】:

@sorin 你能更具体地说明你想在这里完成什么吗? 限制节点执行器的数量是不可能的,因为其他作业需要它们。我只是想防止并行构建相同的存储库。 知道如何从Jenkinsfile 中取消选中Execute concurrent builds if necessary 吗? “Throttle Concurrent Builds Plugin”现在支持 Pipeline。请在下面查看我的答案。 +1 to 将节点的执行者数量限制为 1 - 这是 Jenkins 安装的最佳优化之一。【参考方案2】:

其中一个选项是使用 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"

所以它会等到作业完成(不要与集成测试提及混淆,它只是作业名称)。

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

【讨论】:

【参考方案3】:

在“Throttle Concurrent Builds”插件has Pipeline support 之前,一个解决方案是有效地运行一个带有您的工作所需标签的主执行器。

为此,请在 Jenkins 中创建一个新节点,例如连接到 localhost 的 SSH 节点。您还可以根据您的设置使用命令选项来运行 slave.jar/swarm.jar。给节点一个执行者和一个像“resource-foo”这样的标签,并给你的工作这个标签。现在一次只能运行一个标签为“resource-foo”的作业,因为只有一个带有该标签的执行者。如果您将节点设置为尽可能多地使用(默认)并将主执行器的数量减少一,它应该完全按照预期运行,而不需要更改执行器总数。

【讨论】:

【参考方案4】:

另一种方法是使用可锁定资源插件: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 版开始,阶段中的步骤必须在“步骤”块中。 创建lockinversePrecedence: true是什么意思? @Yash from here > 默认情况下,等待构建按照它们请求获取锁的相同顺序获取锁。通过选中此选项,等待队列中的最新构建将首先获得锁。【参考方案5】:

安装 Jenkins Lockable Resources Plugin。

在您的流水线脚本中,将该部分包装在锁定块中,并为该可锁定资源命名。

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

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

【讨论】:

只是补充一下 - 我找到了锁定功能,但认为它不能解决我的问题。但是,您可以使用变量名称来定义锁定资源 - 使用 env.NODE_NAME 解决了我需要锁定阶段以不在同一节点上同时运行的问题(它可以在不同节点上同时运行) 如果锁内有异常或超时,似乎锁永远不会被释放并且其他并发构建继续运行......直到它超时...... :(不确定,但这就是我正在观察【参考方案6】:

你得到了 disableConcurrentBuilds 属性:

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

然后该作业将等待较旧的作业先完成

【讨论】:

我认为这是最明智的回应 很遗憾,此解决方案已被弃用。应按照另一个答案中的说明使用选项。 这到底是什么意思? sn-p 生成器仍然为脚本化管道显示此选项。你有提到的弃用的链接吗?【参考方案7】:

“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 更适合该任务

【讨论】:

我得到“一个或多个指定的类别不存在”,你在哪里指定类别? 我不明白你的意思。【参考方案8】:

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

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

options  disableConcurrentBuilds() 

这里有详细的描述: https://jenkins.io/doc/book/pipeline/syntax/#options

【讨论】:

我正在尝试这个,它会在全局范围内禁用并发构建,OP 希望禁用每个节点的并发构建,但仍然允许同时进行多个构建,每个可用节点只能进行一个。跨度> 这适用于我们的特定目的,尽管让我感到困扰的是我们必须在管道中配置它并且不能以某种方式在全局范围内完成。【参考方案9】:

在声明性管道语法中使用选项块的示例:

pipeline 

  options  
    disableConcurrentBuilds() 
  

...

【讨论】:

【参考方案10】:

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

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

对我们来说,一个 hacky(但令人惊讶的优雅)解决方案是将主节点的执行程序限制为 1,并使管道脚本坚持使用它(并且只使用它),然后将本地从代理连接到 Jenkins 以便采取照顾所有其他工作。

【讨论】:

以上是关于如何防止相同类型的两个管道 jenkins 作业在同一节点上并行运行?的主要内容,如果未能解决你的问题,请参考以下文章

如何将完成的管道作业构建发布到公共 Jenkins 实例?

如何在詹金斯中获取管道作业的 URL

防止共享Jenkinsfile中的默认签出

如何从 jenkins 作业的 scm 轮询中排除 jenkins 文件

如何防止某些 Jenkins 作业同时运行?

Jenkins管道脚本的开发过程应该如何?