如何使用声明性流水线正确实现动态并行操作

129次阅读
没有评论

问题描述

想要在使用声明性流水线时,实现以下需求:在一个目录中找到所有文件,并为每个找到的文件启动一个并行任务。用户想知道是否可以使用声明性流水线来实现这个需求。

解决方案

请注意以下操作注意版本差异及修改前做好备份。

方案1

在声明性流水线中,可以使用findFiles步骤来实现动态并行任务。以下是一个示例代码:

pipeline {
    agent any
    stages {
        stage("test") {
            steps {
                script {
                    def tests = [:]
                    for (f in findFiles(glob: '**/html/*.html')) {
                        tests["${f}"] = {
                            node {
                                stage("${f}") {
                                    echo '${f}'
                                }
                            }
                        }
                    }
                    parallel tests
                }
            }
        }
    }
}

在上面的示例中,我们首先使用findFiles步骤找到所有匹配**/html/*.html的文件。然后,使用一个循环来为每个文件创建一个并行任务。在每个任务中,我们使用node来指定任务运行的节点,并使用stage来定义任务的名称。在任务中,我们使用echo来输出文件的名称。
请注意,这个示例是使用声明性流水线来实现的。如果你想使用脚本流水线来实现,可以参考官方文档中的示例:https://jenkins.io/doc/pipeline/examples/#parallel-multiple-nodes

方案2

如果你想保持在声明性流水线中,也可以使用以下代码来实现动态并行任务:

// 在流水线外声明变量
def tests = [:]
def files

pipeline {
    agent any
    stages {
        stage('1') {
            steps {
                script {
                    // 在流水线内给变量赋值
                    files = findFiles(glob: '**/html/*.html')
                    // 循环遍历文件
                    files.each { f ->
                        // 将每个文件添加到并行任务中
                        tests[f] = {
                            // 在这里进行高级操作
                            echo f.toString()
                        }
                    }
                    // 运行并行任务
                    parallel tests
                }
            }
        }
    }
}

在上面的示例中,我们首先在流水线外声明了变量testsfiles。然后,在流水线内给files赋值,使用findFiles步骤找到所有匹配**/html/*.html的文件。接下来,我们使用each方法循环遍历文件,并将每个文件添加到tests变量中的并行任务中。在每个任务中,我们使用echo来输出文件的名称。
请注意,如果你想将每个并行任务分配给不同的Jenkins节点,可以在任务中使用node块,如下所示:

tests[f] = {
    node {
        echo f.toString()
    }
}

这样可以将每个并行任务分配给不同的Jenkins节点运行。

方案3

如果你在动态构建步骤中调用其他作业时遇到问题,可以使用以下代码来解决:

pipeline {
    stages {
        stage('Test') {
            steps {
                script {
                    def tests = [:]
                    for (f in findFiles(glob: '**/html/*.html')) {
                        // 创建临时变量,否则名称将是for循环的最后一个值
                        def name = f
                        tests["${name}"] = {
                            build job: "${name}"
                        }
                    }
                    parallel tests
                }
            }
        }
    }
}

在上面的示例中,我们首先使用findFiles步骤找到所有匹配**/html/*.html的文件。然后,使用一个循环来为每个文件创建一个并行任务。在每个任务中,我们使用build步骤来调用其他作业。请注意,我们在循环中创建了一个临时变量name,以确保每个任务的名称正确。
请注意,动态构建步骤可能会在某些构建步骤中引起问题,比如调用其他作业时。如果你遇到这样的问题,可以尝试使用脚本流水线来实现,因为脚本流水线可以使用任意的Groovy代码。

方案4

如果你想简化代码,可以使用脚本流水线来实现动态并行任务。以下是一个示例代码:

pipeline {
    stages {
        stage("Parallel") {
            steps {
                script {
                    def list = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa"]
                    // 将列表分成每组4个
                    def parallelBatches = list.collate(4);
                    // 这样可以同时运行最多4个任务
                    for (parallelBatch in parallelBatches) {
                        def tasks = [:]
                        for (task in parallelBatch) {
                            tasks["${task}"] = {
                                stage("${task}") {
                                    echo '${task}'
                                }
                            }
                        }
                        parallel tasks
                        // 一旦一组任务完成,外层循环将进入下一组
                    }
                }
            }
        }
    }
}

在上面的示例中,我们首先定义了一个列表list,其中包含了一些任务的名称。然后,我们使用collate方法将列表分成每组4个任务。这样可以同时运行最多4个任务。接下来,我们使用两个嵌套的循环来创建并行任务。外层循环遍历每组任务,内层循环遍历每个任务,并将其添加到tasks变量中的并行任务中。在每个任务中,我们使用echo来输出任务的名称。
请注意,这个示例是使用脚本流水线来实现的。如果你想使用声明性流水线来实现,可以参考方案1或方案2。

正文完