Jenkins Pipeline 參數詳解


Pipeline 是什么

Jenkins Pipeline 實際上是基於 Groovy 實現的 CI/CD 領域特定語言(DSL),主要分為兩類,一類叫做 Declarative Pipeline,一類叫做 Scripted Pipeline

Declarative Pipeline 體驗上更接近於我們熟知的 travis CI 的 travis.yml,通過聲明自己要做的事情來規范流程,形如:

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                //
            }
        }
        stage('Test') {
            steps {
                //
            }
        }
        stage('Deploy') {
            steps {
                //
            }
        }
    }
}

 

而 Scripted Pipeline 則是舊版本中 Jenkins 支持的 Pipeline 模式,主要是寫一些 groovy 的代碼來制定流程:

node {  
    stage('Build') {
        //
    }
    stage('Test') {
        //
    }
    stage('Deploy') {
        //
    }
}

 

一般情況下聲明式的流水線已經可以滿足我們的需要,只有在復雜的情況下才會需要腳本式流水線的參與。

過去大家經常在 Jenkins 的界面上直接寫腳本來實現自動化,但是現在更鼓勵大家通過在項目中增加 Jenkinsfile 的方式把流水線固定下來,實現 Pipeline As Code,Jenkins 的 Pipeline 插件將會自動發現並執行它。

語法

Declarative Pipeline 最外層有個 pipeline 表明它是一個聲明式流水線,下面會有 4 個主要的部分: agentpoststagessteps,我會逐一介紹一下。

Agent

agent 主要用於描述整個 Pipeline 或者指定的 Stage 由什么規則來選擇節點執行。Pipeline 級別的 agent 可以視為 Stage 級別的默認值,如果 stage 中沒有指定,將會使用與 Pipeline 一致的規則。在最新的 Jenkins 版本中,可以支持指定任意節點(any),不指定(none),標簽(label),節點(node),dockerdockerfile 和 kubernetes 等,具體的配置細節可以查看文檔,下面是一個使用 docker 的樣例:

agent {
    docker {
        image 'myregistry.com/node'
        label 'my-defined-label'
        registryUrl 'https://myregistry.com/'
        registryCredentialsId 'myPredefinedCredentialsInJenkins'
        args '-v /tmp:/tmp'
    }
}

 

Tips:

  • 如果 Pipeline 選擇了 none,那么 stage 必須要指定一個有效的 agent,否則無法執行
  • Jenkins 總是會使用 master 來執行 scan multibranch 之類的操作,即使 master 配置了 0 executors
  • agent 指定的是規則而不是具體的節點,如果 stage 各自配置了自己的 agent,需要注意是不是在同一個節點執行的

Stages && Stage

Stages 是 Pipeline 中最主要的組成部分,Jenkins 將會按照 Stages 中描述的順序從上往下的執行。Stages 中可以包括任意多個 Stage,而 Stage 與 Stages 又能互相嵌套,除此以外還有 parallel 指令可以讓內部的 Stage 並行運行。實際上可以把 Stage 當作最小單元,Stages 指定的是順序運行,而 parallel 指定的是並行運行。

接下來的這個 case 很好的說明了這一點:

pipeline {
    agent none
    stages {
        stage('Sequential') {
            stages {
                stage('In Sequential 1') {
                    steps {
                        echo "In Sequential 1"
                    }
                }
                stage('In Sequential 2') {
                    steps {
                        echo "In Sequential 2"
                    }
                }
                stage('Parallel In Sequential') {
                    parallel {
                        stage('In Parallel 1') {
                            steps {
                                echo "In Parallel 1"
                            }
                        }
                        stage('In Parallel 2') {
                            steps {
                                echo "In Parallel 2"
                            }
                        }
                    }
                }
            }
        }
    }
}

 

除了指定 Stage 之間的順序關系之外,我們還可以通過 when 來指定某個 Stage 指定與否:比如要配置只有在 Master 分支上才執行 push,其他分支上都只運行 build

stages {
  stage('Build') {
    when {
      not { branch 'master' }
    }
    steps {
      sh './scripts/run.py build'
    }
  }
  stage('Run') {
    when {
      branch 'master'
    }
    steps {
      sh './scripts/run.py push'
    }
  }
}

 

還能在 Stage 的級別設置 environment,這些就不展開了,文檔里有更詳細的描述。

Steps

steps 是 Pipeline 中最核心的部分,每個 Stage 都需要指定 Steps。Steps 內部可以執行一系列的操作,任意操作執行出錯都會返回錯誤。完整的 Steps 操作列表可以參考 Pipeline Steps Reference,這里只說一些使用時需要注意的點。

  • groovy 語法中有不同的字符串類型,其中 'abc' 是 Plain 字符串,不會轉義 ${WROKSPACE} 這樣的變量,而 "abc" 會做這樣的轉換。此外還有 ''' xxx ''' 支持跨行字符串,""" 同理。
  • 調用函數的 () 可以省略,使得函數調用形如 updateGitlabCommitStatus name: 'build', state: 'success',通過 , 來分割不同的參數,支持換行。
  • 可以在聲明式流水線中通過 script 來插入一段 groovy 腳本

Post

post 部分將會在 pipeline 的最后執行,經常用於一些測試完畢后的清理和通知操作。文檔中給出了一系列的情況,比較常用的是 alwayssuccess 和 failure

比如說下面的腳本將會在成功和失敗的時候更新 gitlab 的狀態,在失敗的時候發送通知郵件:

post {
  failure {
    updateGitlabCommitStatus name: 'build', state: 'failed'
    emailext body: '$DEFAULT_CONTENT', recipientProviders: [culprits()], subject: '$DEFAULT_SUBJECT'
  }
  success {
    updateGitlabCommitStatus name: 'build', state: 'success'
  }
}

 

每個狀態其實都相當於於一個 steps,都能夠執行一系列的操作,不同狀態的執行順序是事先規定好的,就是文檔中列出的順序。

Shared Libraries

同一個 Team 產出的不同項目往往會有着相似的流程,比如 golang 的大部分項目都會執行同樣的命令。這就導致了人們經常需要在不同的項目間復制同樣的流程,而 Shared Libraries 就解決了這個問題。通過在 Pipeline 中引入共享庫,把常用的流程抽象出來變成一個的指令,簡化了大量重復的操作。

在配置好 lib 之后,Jenkins 會在每個 Pipeline 啟動前去檢查 lib 是否更新並 pull 到本地,根據配置決定是否直接加載。

所有的 Shared Libraries 都要遵循相同的項目結構:

(root)
+- src                     # Groovy source files
|   +- org
|       +- foo
|           +- Bar.groovy  # for org.foo.Bar class
+- vars
|   +- foo.groovy          # for global 'foo' variable
|   +- foo.txt             # help for 'foo' variable
+- resources               # resource files (external libraries only)
|   +- org
|       +- foo
|           +- bar.json    # static helper data for org.foo.Bar

目前我們的使用比較低級,所以只用到了 vars 來存儲全局的變量。

vars 下的每一個 foo.groovy 文件都是一個獨立的 namespace,在 Pipeline 中可以以 foo.XXX 的形式來導入。比如我們有 vars/log.groovy

def info(message) {
    echo "INFO: ${message}"
}

def warning(message) {
    echo "WARNING: ${message}"
}

 

那么 Jenkinsfile 中就可以這樣調用:

// Jenkinsfile
steps {
  log.info 'Starting'
  log.warning 'Nothing to do!'
}

 

大家可能已經注意到了,在 groovy 文件中,我們可以直接像在 steps 中一樣調用已有的方法,比如 echo 和 sh 等。

我們也能在 groovy 文件中去引用 Java 的庫並返回一個變量,比如:

#!/usr/bin/env groovy
import java.util.Random;

def String name() {
  def rand = new Random()
  def t = rand.nextInt(1000)
  return String.valueOf(t)
}

 

這樣就能夠在 JenkinsFile 中去設置一個環境變量:

// Jenkinsfile
environment {
  NAME = random.name()
}

 

除了定義方法之外,我們還能讓這個文件本身就能被調用,只需要定義一個 call 方法:

#!/usr/bin/env groovy

def call() {
  sh "hello, world"
}

 

還能夠定義一個新的 section,接受一個 Block:

def call(Closure body) {
    node('windows') {
        body()
    }
}

 

這樣可以讓指定的 Body 在 windows 節點上調用:

// Jenkinsfile
windows {
    bat "cmd /?"
}

 

常用技巧

發送郵件通知

主要使用 emailext,需要在 Jenkins 的配置界面事先配置好,可用的環境變量和參數可以參考文檔 Email-ext plugin

emailext body: '$DEFAULT_CONTENT',  recipientProviders: [culprits(),developers()], subject: '$DEFAULT_SUBJECT'

結果同步到 gitlab

同樣需要配置好 gitlab 插件,在 Pipeline 中指定 options

// Jenkisfile
options {
  gitLabConnection('gitlab')
}

然后就可以在 post 中根據不同的狀態來更新 gitlab 了:

// Jenkisfile
failure {
  updateGitlabCommitStatus name: 'build', state: 'failed'
}
success {
  updateGitlabCommitStatus name: 'build', state: 'success'
}

 

文檔參考:Build status configuration

構建過程中可用的環境變量列表

Jenkins 會提供一個完整的列表,只需要訪問 <your-jenkins-url>/env-vars.html/ 即可,別忘了需要使用 "${WORKSPACE}"

在 checkout 前執行自定義操作

在 Multibranch Pipeline 的默認流程中會在 checkout 之前和之后執行 git clean -fdx,如果在測試中以 root 權限創建了文件,那么 jenkins 會因為這個命令執行失敗而報錯。所以我們需要在 checkout 之前執行自定義的任務:

#!/usr/bin/env groovy

// var/pre.groovy
def call(Closure body) {
  body()
  checkout scm
}

在 Jenkinsfile 中配置以跳過默認的 checkout 行為:

// Jenkisfile
options {
  skipDefaultCheckout true
}

在每個 stage 中執行自定義的任務即可:

// Jenkisfile
stage('Compile') {
  agent any
  steps {
    pre {
      sh 'pre compile'
    }
    sh 'real compile'
  }
}

總結

Jenkins 作為使用最為廣泛的 CI/CD 平台,網上流傳着無數的腳本和攻略,在學習和開發的時候一定要從基本出發,了解內部原理,多看官方的文檔,不要拿到一段代碼就開始用,這樣才能不會迷失在各式各樣的腳本之中。

更重要的是要結合自己的業務需求,開發和定制屬於自己的流程,不要被 Jenkins 的框架限制住。比如我們是否可以定義一個自己的 YAML 配置文件,然后根據 YAML 來生成 Pipeline,不需要業務自己寫 Pipeline 腳本,規范使用,提前檢查不合法的腳本,核心的模塊共同升級,避免了一個流程小改動需要所有項目組同步更新。這是我現在正在做的事情,有機會再跟大家分享~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM