在 Jenkins 管道中的并行阶段设置依赖项或优先级

Par*_*thi 6 parallel-processing jenkins jenkins-pipeline

我正在执行并行步骤 -

stages {
    stage ('Parallel build LEVEL 1 - A,B,C ...') {
        steps{
            parallel (
                "Build A": {
                    node('Build_Server_Stack') {
                        buildAndArchive(A) // my code
                    }
                },
                "Build B" : {
                    node('Build_Server_Stack') {
                        buildAndArchive(B)
                    }
                },
                "Build C" : {
                    node('Build_Server_Stack') {
                        buildAndArchive(C)
                    }
                }
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*ngs 9

让 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)
        }
    }
)
Run Code Online (Sandbox Code Playgroud)

...这不是很有趣。

一个更有趣的情况是,当你有 4 个工作 A、B、C 和 D 时,C 仅依赖于 A,而 D 依赖于 A 和 B,如下所示:

A   B
| \ |
C   D
Run Code Online (Sandbox Code Playgroud)

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

[A, B]
[C, D]
Run Code Online (Sandbox Code Playgroud)

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

或者:

[A]
[C, B+D]
Run Code Online (Sandbox Code Playgroud)

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

想必大多数人都会有足够的信息来选择“足够好”的配置,但不幸的是 Jenkins 似乎没有为此提供通用的解决方案。这并不是什么新想法

为了解决这个问题,我同时运行所有并行线程,然后让每个线程依次等待它们的依赖项。像CountDownLatch这样的东西将是实现等待的完美解决方案,但这在 Jenkins 中效果不佳。Jenkins步骤waitUntil似乎很理想,但由于它基于轮询,所以在工作完成和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
Run Code Online (Sandbox Code Playgroud)

这工作得很好,尽管感觉必须有更好的解决方案......我希望通过提供这个答案有人会注意到并且a)告诉我我错了并指出正确的答案;或 b) 创建一个插件来正确执行此操作;)


Mar*_*atC 3

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

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)
                  }
                }
              }

Run Code Online (Sandbox Code Playgroud)

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

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