Jenkins 如何在 Pipeline 中平行 build Job 與失敗不中斷 job

同事打算設定固定周期性執行 pipeline job 將所有專案 build 一輪,讓專案可以常保在健康的狀態。

非常正確的想法,但我的疑問是為什麼需要使用到 pipeline job:因為 job 有互相依賴的狀況,得確定基底 job 完成才能執行其他 job,就讓我們來看可以怎麼設定

使用 Groovy plugin 進行設定

安裝 Groovy plugin 的方式請參考 Jenkins 2 如何取得上次編譯成功的時間

同時 build 多個 stage

  1. 原本循序做法

    • 語法

      stage('testPowerShell') {
          build job: 'testPowerShell', parameters: [
          [$class: 'StringParameterValue', name: 'serviceNames', value: 'reveal,TestCert'],
          [$class: 'StringParameterValue', name: 'filelocation', value: 'D:\\serverlist.json'],
          [$class: 'StringParameterValue', name: 'groupNo', value: '0']
          ]
      }
      stage('TestCopy') {
          build job: 'TestCopy', parameters: [
              [$class: 'StringParameterValue', name: 'excludeFolders', value: 'D:\\Downloads\\NewReveal\\reveal.js-master\\test D:\\Downloads\\NewReveal\\reveal.js-master\\js']
          ]
      }
      
    • 依序執行

      1step1

      2step2

    • 有錯直接中斷後續動作

      3blocking

  2. 使用 parallel 平行處理

    • 語法

      parallel(
          job1:{stage('testPowerShell') {
              build job: 'testPowerShell', parameters: [
              [$class: 'StringParameterValue', name: 'serviceNames', value: 'reveal,TestCert'],
              [$class: 'StringParameterValue', name: 'filelocation', value: 'D:\\serverlist.json'],
              [$class: 'StringParameterValue', name: 'groupNo', value: '0']
              ]
          }},
          job2:{stage('TestCopy') {
          build job: 'TestCopy', parameters: [
              [$class: 'StringParameterValue', name: 'excludeFolders', value: 'D:\\Downloads\\NewReveal\\reveal.js-master\\test D:\\Downloads\\NewReveal\\reveal.js-master\\js']
          ]
          }}
      )
      
    • 平行執行

      將多個 stage 視為同一個 node 會同時啟動,會等待較長的 stage 完成後才統一回傳結果

      4starttoget

      5stoptoget

    • 出錯仍會執行其他 stage

      6noblocking

在同一個 stage build 多個 job

這比較符合同事的需求,需要有基礎的 job 先完成後再進行其他 job 的建置

  • 語法

    只在 stage 2 去載入多個 job 並使用 parallel build

    def transformIntoStep(jobFullName) {
        return {
            build job: jobFullName,
            parameters: [string(name: 'Environ', value: 'QA')],
            quietPeriod: 3
            }
    }
                    
    stage('Step1') {
        build job: 'A01-TestDev', parameters: [
        [$class: 'StringParameterValue', name: 'serviceNames', value: 'reveal,TestCert'],
        [$class: 'StringParameterValue', name: 'filelocation', value: 'D:\\serverlist.json'],
        [$class: 'StringParameterValue', name: 'groupNo', value: '0']
        ]
    }
    stage('Step2') {
        def branches = [: ]
        def jobs = "${buildjobs}".tokenize(',')
        def i = 0;
        for (jobName in jobs) {
        //println(jobName);
        branches["job${i}"] = transformIntoStep(jobName);
        i++;
        }
        //println(branches);
        parallel branches;
                    
    }
    stage('Step3') {
        build job: 'A01-TestDev', parameters: [
        [$class: 'StringParameterValue', name: 'serviceNames', value: 'reveal,TestCert'],
        [$class: 'StringParameterValue', name: 'filelocation', value: 'D:\\serverlist.json'],
        [$class: 'StringParameterValue', name: 'groupNo', value: '0']
        ]
    }
    
  • 正常執行

    7pipelineparallel

    8parallelok

  • 某個 job 有異常,也不會影響其他 job build

    9parallelfail

    10paralleljobok

心得

我測試下來,塞入多個 build job 的行為如果不拉出去用 function (transformIntoStep)來處理,會只 build 最後一個 job 多次,效果跟 Jobs In Parallel 相同,文件有提到 build array 是拿來 build 同一個 job 多次用的,這個要多注意

參考資料

  1. Pipeline Examples
  2. Parallelism and Distributed Builds with Jenkins
  3. Jenkins 2 如何取得上次編譯成功的時間
  4. Jobs In Parallel