【问题标题】:Scripted jenkinsfile parallel stage脚本 jenkinsfile 并行阶段
【发布时间】:2018-03-31 19:01:38
【问题描述】:

我正在尝试使用 groovy DSL 编写脚本化的 Jenkinsfile,它将在一组阶段中具有并行步骤。

这是我的詹金斯文件:

node {   
stage('Build') {
    sh 'echo "Build stage"'
}

stage('API Integration Tests') {
    parallel Database1APIIntegrationTest: {
        try {
            sh 'echo "Build Database1APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }               

    }, Database2APIIntegrationTest: {
        try {
            sh 'echo "Build Database2APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }

    }, Database3APIIntegrationTest: {
        try {
            sh 'echo "Build Database3APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }
    }
}

stage('System Tests') {
    parallel Database1APIIntegrationTest: {
        try {
            sh 'echo "Build Database1APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }               

    }, Database2APIIntegrationTest: {
        try {
            sh 'echo "Build Database2APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }

    }, Database3APIIntegrationTest: {
        try {
            sh 'echo "Build Database3APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }
    }
}
}

我希望有 3 个阶段:构建;集成测试和系统测试。 在两个测试阶段中,我希望并行执行 3 组测试,每组针对不同的数据库。

我有 3 个可用的执行者。一个在主服务器上,两个代理,我希望每个并行步骤在任何可用的执行器上运行。

我注意到,在运行我的管道后,我只看到 3 个阶段,每个阶段都标记为绿色。我不想查看该阶段的日志来确定该阶段中的任何并行步骤是否成功/不稳定/失败。

我希望看到我的测试阶段中的 3 个步骤 - 标记为绿色、黄色或红色(成功、不稳定或失败)。

我考虑过将测试扩展到他们自己的阶段,但我意识到不支持并行阶段(有人知道这是否会被支持吗?),所以我不能这样做,因为管道也需要很长时间完成时间很长。

任何见解将不胜感激,谢谢

【问题讨论】:

    标签: jenkins jenkins-pipeline


    【解决方案1】:

    在 Jenkins 脚本化管道中,parallel(...) 采用一个 Map 来描述要构建的每个阶段。因此,您可以预先以编程方式构建构建阶段,这种模式允许灵活的串行/并行切换。
    我使用了与此类似的代码,其中 prepareBuildStages 返回一个 Map 列表,每个 List 元素按顺序执行,而 Map 描述了该点的并行阶段。

    // main script block
    // could use eg. params.parallel build parameter to choose parallel/serial 
    def runParallel = true
    def buildStages
    
    node('master') {
      stage('Initialise') {
        // Set up List<Map<String,Closure>> describing the builds
        buildStages = prepareBuildStages()
        println("Initialised pipeline.")
      }
    
      for (builds in buildStages) {
        if (runParallel) {
          parallel(builds)
        } else {
          // run serially (nb. Map is unordered! )
          for (build in builds.values()) {
            build.call()
          }
        }
      }
    
      stage('Finish') {
          println('Build complete.')
      }
    }
    
    // Create List of build stages to suit
    def prepareBuildStages() {
      def buildStagesList = []
    
      for (i=1; i<5; i++) {
        def buildParallelMap = [:]
        for (name in [ 'one', 'two', 'three' ] ) {
          def n = "${name} ${i}"
          buildParallelMap.put(n, prepareOneBuildStage(n))
        }
        buildStagesList.add(buildParallelMap)
      }
      return buildStagesList
    }
    
    def prepareOneBuildStage(String name) {
      return {
        stage("Build stage:${name}") {
          println("Building ${name}")
          sh(script:'sleep 5', returnStatus:true)
        }
      }
    }
    

    生成的管道显示为:

    对于可以嵌套在并行块中的内容有一定的限制,请参阅the pipeline documentation 了解详细信息。不幸的是,尽管它不如脚本(恕我直言)灵活,但大部分参考资料似乎都偏向于声明式管道。 pipeline examples 页面是最有帮助的。

    【讨论】:

    • 是否可以在并行执行中有多个步骤?我的意思是:有可能有 { || || } 作为并行步骤,其中 1.n 是非并行阶段?
    • @D.Kovács 我认为您只能拥有一个stage{},因为它是地图的价值。但是你当然可以在stage{} 中放置多个步骤,其中一个步骤是jenkins.io/doc/pipeline/steps 之一,如jenkins.io/doc/book/pipeline/syntax/#scripted-steps 中所述。可能Jenkins/BlueOcean UI 会将它们显示为一个点。
    • 如果我在“管道脚本”中使用此代码有效,但当我使用“来自 SCM 的管道脚本”时无效(我在 .groovy 文件中有相同的代码 - 来自 GIT/BitBucket ) 当我将它包装时pipeline { ... } 部分中。当我单击 Jenkinsfile 作业的仪表板时,如何解决这个问题并获得为每个动态创建的“阶段”创建的“多列”( GUI). PS: 我暂时不用蓝海
    • @EdRandall 所以我在周末尝试了它,我想我明白了(现在你好世界方式)。你的帖子也有帮助。我在这里跟踪它:stackoverflow.com/questions/58885845/…when in pipeline { ... } section
    • @EdRandall 好帖子!遗憾的是,Jenkins 管道中缺少有关并行脚本的文档。食谱这里的返回参数有些混乱。我会将prepareBuildStages() 的输出重命名为buildStages 或重命名Initialise 阶段内的变量;-)
    【解决方案2】:

    这是一个基于@Ed Randall 帖子的没有循环或函数的简单示例:

    node('docker') {
        stage('unit test') {
            parallel([
                hello: {
                    echo "hello"
                },
                world: {
                    echo "world"
                }
            ])
        }
    
        stage('build') {
            def stages = [:]
    
            stages["mac"] = {
                echo "build for mac"
            }
            stages["linux"] = {
                echo "build for linux"
            }
    
            parallel(stages)
        }
    }
    

    ...产生这个:

    请注意,地图的值不必是阶段。可以直接给出步骤。

    【讨论】:

    • 这很好用,但有什么方法可以在脚本管道中使用 failFast 或类似的东西?
    • @Fjaoos,我还没有测试过它,但一个快速的谷歌给出了一些提示:stackoverflow.com/questions/58026189/… 此外,jenkins 管道 sn-p 生成器有一些建议:parallel firstBranch: { // do something } , secondBranch: { // 做其他事情 }, failFast: true|false
    【解决方案3】:

    这是他们docs的一个例子:

    并行执行

    上一节中的示例以线性系列跨两个不同平台运行测试。实际上,如果 make check 执行需要 30 分钟才能完成,那么“测试”阶段现在需要 60 分钟才能完成!

    幸运的是,Pipeline 具有用于并行执行部分 Scripted Pipeline 的内置功能,在恰当命名的并行步骤中实现。

    重构上面的例子以使用并行步骤:

    // Jenkinsfile (Scripted Pipeline)
    
    
    stage('Build') {
        /* .. snip .. */
    }
    
    stage('Test') {
        parallel linux: {
            node('linux') {
                checkout scm
                try {
                    unstash 'app'
                    sh 'make check'
                }
                finally {
                    junit '**/target/*.xml'
                }
            }
        },
        windows: {
            node('windows') {
                /* .. snip .. */
            }
        }
    }
    

    【讨论】:

    • 舞台级别的并行与脚本级别的并行有点不同。可悲的是,在我的 Jenkins 机器上,舞台级别的并行以 stackoverflow 结束 :(.
    【解决方案4】:

    在这里简化@Ed Randall 的答案。 请记住,这是 Jenkinsfile 脚本化的(不是声明性的)

    stage("Some Stage") {
        // Stuff ...
    }
    
    
    stage("Parallel Work Stage") {
    
        // Prealocate dict/map of branchstages
        def branchedStages = [:]
    
        // Loop through all parallel branched stage names
        for (STAGE_NAME in ["Branch_1", "Branch_2", "Branch_3"]) {
    
            // Define and add to stages dict/map of parallel branch stages
            branchedStages["${STAGE_NAME}"] = {
                stage("Parallel Branch Stage: ${STAGE_NAME}") {
                    // Parallel stage work here
                    sh "sleep 10"
                }
            }
    
        }
    
        // Execute the stages in parallel
        parallel branchedStages
    }
    
    
    stage("Some Other Stage") {
        // Other stuff ...
    }
    

    请注意花括号。 这将产生以下结果(使用 BlueOcean Jenkins 插件):

    【讨论】:

    • 可能有问题?在我的测试中,代码stage("Parallel Branch Stage: ${STAGE_NAME}") 总是使用列表中的最后一个,因为 STAGE_NAME 是闭包之外的变量。
    • @WeiHuang 是的,你不能直接使用循环变量,因为闭包会捕获变量,而不是内容。见stackoverflow.com/questions/56348341/…
    【解决方案5】:

    我还尝试了类似的步骤来执行并行阶段并在阶段视图中显示所有这些阶段。您应该在并行步骤中编写一个阶段,如以下代码块所示。

    // Jenkinsfile (Scripted Pipeline)
    
    stage('Build') {
        /* .. Your code/scripts .. */
    }
    
    stage('Test') {
        parallel 'linux': {
            stage('Linux') {
                /* .. Your code/scripts .. */
            }
        }, 'windows': {
            stage('Windows') {
                /* .. Your code/scripts .. */
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      上面用 FOR 的例子是错误的,因为变量 STAGE_NAME 每次都会被覆盖,我遇到了和 Wei Huang 一样的问题。

      在这里找到解决方案:

      https://www.convalesco.org/notes/2020/05/26/parallel-stages-in-jenkins-scripted-pipelines.html

      def branchedStages = [:]
      def STAGE_NAMES =  ["Branch_1", "Branch_2", "Branch_3"]
      STAGE_NAMES.each { STAGE_NAME ->
       // Define and add to stages dict/map of parallel branch stages
          branchedStages["${STAGE_NAME}"] = {
              stage("Parallel Branch Stage: ${STAGE_NAME}") {
              // Parallel stage work here
                  sh "sleep 10"
              }
          }
        }
      parallel branchedStages
      

      【讨论】:

        【解决方案7】:

        我曾多次在并行块中使用stage{}。然后每个阶段都会显示在阶段视图中。包含parallel 的父阶段不包括所有并行阶段的时序,但每个并行阶段都会显示在阶段视图中。

        在蓝海中,平行的阶段是单独出现的,而不是显示的阶段。如果有父级,则显示为并行级的父级。

        如果您没有相同的体验,则可能需要升级插件。

        【讨论】:

        • 你能举个例子来说明你在并行块中使用舞台的地方吗?我正在努力寻找有关此的任何文档
        猜你喜欢
        • 2018-01-28
        • 1970-01-01
        • 2022-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多