在Jenkins管道中以并行阶段设置依赖关系或优先级

时间:2018-06-04 10:03:08

标签: jenkins parallel-processing jenkins-pipeline

我正在执行并行步骤 -

> tapply(df$samp.45, as.factor(df$group), mean)
       1        2        3 
78.19556 79.65747 68.91818 

现在我需要在C完成后开始执行B.我可以将B作业拉出并行块并在并行块之后添加以实现此目的。但在这种情况下,在A和C完成之前,B不会启动。对于长时间的A作业,当您有可用的空闲构建服务器时,这会影响性能。 我们是否可以解决/改进执行计划以并行运行所有计划,但使用“依赖关系”'或者'优先事项'用于并行步骤。 Promotions插件中存在类似的机制,但需要在管道中实现。

2 个答案:

答案 0 :(得分:2)

您绝对可以。尝试这样的事情:

stages {
    stage ('Parallel build LEVEL 1 - A/C+B ...') {
        parallel {
            stage("Build A") {
                agent { node { label 'A'}}
                steps {
                    buildAndArchive(A)
                }
            }
            stage("Build C and B") {
              stages {
                stage("Build C") {
                  agent { node { label 'C'}}
                  steps {
                      buildAndArchive(C)
                  }
                }
                stage("Build B") {
                  agent { node { label 'B'}}
                  steps {
                      buildAndArchive(B)
                  }
                }
              }

这将并行执行两个分支,其中一个分支在构建A,另一个在先构建C,然后在B。

另请参阅https://jenkins.io/blog/2018/07/02/whats-new-declarative-piepline-13x-sequential-stages/

答案 1 :(得分:0)

使B在C之后执行:

parallel (
    "Build A": {
        node('Build_Server_Stack') {
            buildAndArchive(A) // my code
        }
    },
    "Build C then B" : {
        node('Build_Server_Stack') {
            buildAndArchive(C)
            buildAndArchive(B)
        }
    }
)

...这不是很有趣。

一个更有趣的情况是,当您有4个作业(A,B,C和D),其中C仅取决于A,而D取决于A和B时,像这样:

A   B
| \ |
C   D

使这个变得有趣的是,您不能直接在Jenkins管道中表达这一点。无论您如何将作业安排在不同的并行块​​中,都将始终强制一项作业不必要地等待另一项。 您可以合理地将工作安排为:

[A, B]
[C, D]

但是,即使A快速完成,C也需要等待B。

或者:

[A]
[C, B+D]

但是现在D必须等待A和B串联。

大概大多数人将有足够的信息来选择“足够好”的配置,但是不幸的是,詹金斯似乎对此没有通用的解决方案。 It's not like this is a new idea

要解决此问题,我同时运行所有并行线程,然后让它们每个依次轮流等待其依赖关系。像CountDownLatch之类的东西将是实现等待的完美解决方案,但这对Jenkins来说并不是很好。 waitUntil Jenkins步骤似乎很理想,但是由于它是基于民意测验的,因此在完成工作和waitUntil注意之间不可避免地会有延迟。另一方面,lock的行为就像互斥锁。通过将两者结合起来,我们可以获得一项工作所需的行为,该行为几乎在其依赖关系完成后立即开始。

// Map job name to the names of jobs it depends on.
jobDependencies = [
    "A": [],
    "B": [],
    "C": ["A"],
    "D": ["A", "B"]
]
lockTaken = [:]
threads = [:]
jobDependencies.each { name, dependencies ->
    threads[name] = {
        // Use a lock with a name unique to this build.
        lock("${name}-${BUILD_TAG}") {
            // Notify other threads that the lock has been taken and it's safe to wait on it.
            lockTaken[name] = true
            dependencies.each { dependency ->
                // Poll until the dependency takes its lock.
                waitUntil {
                    lockTaken[dependency]
                }
                // Wait for the dependency to finish and release its lock.
                lock("${dependency}-${BUILD_TAG}") {}
            }
            // Actually run the job
            buildAndArchive(name)
        }
    }
}

parallel threads

尽管感觉好像必须有更好的解决方案,但效果很好。我希望通过提供此答案,有人会注意到,或者a)告诉我我错了,并指出正确的答案;或b)制作一个插件来正确执行;)