Jenkins管道和信号量

时间:2017-05-30 07:07:03

标签: jenkins semaphore jenkins-pipeline

我正在构建一个Jenkins作业,它将持续运行我的所有阶段测试,但不是一次性完成(它们依赖于共享硬件)。因此,我正在使用信号量创建并行作业,以确保一次只运行有限的数量。 这是我的管道的简化版本,它重现了这个问题:

import java.util.concurrent.Semaphore

def run(job) {
  return {
    this.limiter.acquire();
    try {
      println "running ${job}"
      build job
      println "finished ${job}"
    } finally {
      this.limiter.release();
    }
  }
}

def getJobs() {
  def allJobs = Jenkins.getInstance().getJobNames()
  def stagingJobs = []
  for(String job : allJobs) {
    if (job.startsWith("staging/temp")) {
      stagingJobs.add(job)
    }
  }
  println "${stagingJobs.size()} jobs were found."
  return stagingJobs
}

this.limiter = new Semaphore(2)
def jobs = [:]
for (job in getJobs()) {
  jobs[job] = run(job)
}
parallel jobs

当我没有信号量运行时,一切正常。但是使用上面的代码,我得到的除外:

[Pipeline] echo
6 jobs were found.
[Pipeline] parallel
[Pipeline] [staging/temp1] { (Branch: staging/temp1)
[Pipeline] [staging/temp2] { (Branch: staging/temp2)
[Pipeline] [staging/temp3] { (Branch: staging/temp3)
[Pipeline] [staging/temp4] { (Branch: staging/temp4)
[Pipeline] [staging/temp5] { (Branch: staging/temp5)
[Pipeline] [staging/temp6] { (Branch: staging/temp6)

如果我查看管道步骤,我可以看到前两个作业开始,并且它们的日志消息输出。但是,似乎跑步者从未收到过分段作业完成的通知。结果,信号量永远不会释放,其他4个工作永远无法启动。这是一个线程转储中期测试,在下游构建完成之后:

Thread #7
    at DSL.build(unsure what happened to downstream build)
    at WorkflowScript.run(WorkflowScript:9)
    at DSL.parallel(Native Method)
    at WorkflowScript.run(WorkflowScript:38)
Thread #8
    at DSL.build(unsure what happened to downstream build)
    at WorkflowScript.run(WorkflowScript:9)
Thread #11
    at WorkflowScript.run(WorkflowScript:6)
Thread #12
    at WorkflowScript.run(WorkflowScript:6)

最终导致多个java.lang.InterruptedException错误。

是否可以在管道中使用信号量,或者是否有更好的方法来确保只有一部分作业一次运行?我宁愿避免将节点分成简单的测试运行器。

3 个答案:

答案 0 :(得分:2)

Concurrent Step plugin刚刚发布,在该用例中应该可以很好地工作。

这,您可以简化代码:

def semaphore = createSemaphore permit:2

def run(job) {
  return {
    acquireSemaphore (semaphore) {
      println "running ${job}"
      build job
      println "finished ${job}"
    }
  }
}

...

答案 1 :(得分:1)

由于至少有一年的插件可以帮助您获得您的意图,并且还可以选择在管道作业中使用此插件,插件是Lockable Plugin Resource

基本上你包装你的共享资源,如果资源不是免费的,作业将在lock语句之前排队。

如果您对测试的并行化感兴趣,还可以查看Parallel Test Executor Plugin

答案 2 :(得分:0)

可能有锁定步骤的解决方法

可锁定资源插件没有信号灯功能。

我花了很长时间才弄清楚如何将锁定步骤压缩为信号量行为……如果可以自己做到,那就太好了。这是一个例子...

int concurrency = 3
List colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
Map tasks = [failFast: false]
for(int i=0; i<colors.size(); i++) {
    String color = colors[i]
    int lock_id = i % concurrency
    tasks["Code ${color}"] = { ->
        stage("Code ${color}") {
            lock("color-lock-${lock_id}") {
                echo "This color is ${color}"
                sleep 30
            }
        }
    }

}
// execute the tasks in parallel with concurrency limits
stage("Rainbow") {
    parallel(tasks)
}

上面将创建自定义锁:

  • color-lock-0
  • color-lock-1
  • color-lock-2

所有并发任务将争用三个锁之一。它的效率不高(肯定不如真正的信号量高),但它做得足够好...

希望对其他人有帮助。

限制

您的管道将花费最慢的时间。因此,不幸的是,如果您有多个长时间运行的作业正在争夺同一把锁(例如color-lock-1),那么您的管道可能会比适当的信号量更长。

示例

  • color-lock-0花费20秒来循环完成所有作业。
  • color-lock-1花费30分钟来循环浏览所有作业。
  • color-lock-2花费2分钟来循环浏览所有作业。

然后,您的作业将需要30分钟才能运行...与真正的信号灯相比,它本来要快得多,因为运行时间更长的作业将占用信号灯中的下一个可用锁,而不是被阻止。

总比没有好;到目前为止,这就是我所拥有的。听起来像是使用可锁定资源插件打开功能请求的好时机。