Pipeline在Unix/Linux系統中經常用到,Pipeline將一個命令/程序/進程的輸出發送到另一個命令/程序/進程,以進行進一步處理。比如:cat test.txt | grep test1
。Jenkins 中的Pipeline借用了Unix/Linux中的 Pipeline思路,實現像流水線一樣來調度Jenkins任務,通過Jenkinsfile來描述整個持續集成流程。
一個簡單的 Pipeline項目
先來看看如何在Jenkins上創建一個簡單的Pipeline。
新建pipeline任務
新建一個pipeline任務
可以設置構建觸發方式。
接下來開始編寫Pipeline腳本,可選擇Pipline script或者Pipline script from SCM。
- Pipline script模式是將腳本寫在下面的輸入框中
- Pipline script from SCM是將Pipeline script放到Git或者SVN上,執行的時候從上面拉下來執行。
Pipeline Script
選擇 Pipeline Script,編寫如下代碼:
pipeline {
agent any
stages {
stage('begin') {
steps {
echo 'Hello pipeline'
}
}
}
post {
always {
echo 'say goodbay'
}
}
}
- stages:添加執行步驟
- post:任務執行完成後執行的操作
Pipeline Script from SCM
Pipeline Script除了寫在jenkins界面以外,也可以放入git源碼庫進行管理
在 Jenkins Pipeline任務中選擇 Pipeline script from SCM
添加git源碼地址,在 Script Path中填寫需要運行的Jenkinsfile文件所在的地址
構建
創建完成後保存,點擊Build Now,我們可以看到不同構建階段進度
構建日誌:
Jenkinsfile語法
Jenkinsfile支持兩種語法形式:
- Scripted pipeline - 腳本式流水線語法,基於 Groovy 語言構建的通用 DSL(Domain-specific language,領域特定語言)
- Declarative pipeline - 聲明式流水線語法,在v2.5之後引入,支持結構化方式,提供了更豐富的語法特性。
DSL是專注於某個應用領域的計算機語言。和Python、Java等這種通用語言(General-purpose Language, GPL)不同的是,DSL是一種爲了特定領域而設計的開發語言,比如Web 應用使用的HTML、可擴展標記語言XML、SQL語言等。
通常情況下,流水線中聲明式和腳本式語法結合使用。
Declarative pipeline
聲明式流水線語法必須包含在一個 pipeline塊內:
pipeline {
/* Declarative Pipeline */
}
pipeline塊中主要由Sections, Directives, Steps, 或者賦值語句組成。
pipeline {
agent any
stages {
stage('begin') {
steps {
echo 'Hello pipeline'
}
}
}
post {
always {
echo 'say goodbay'
}
}
}
Sections
Sections包括agent、stages、steps和post。
agent
agent定義 pipeline執行節點,必須在pipeline
塊的頂層定義。
主要參數:
any
:可以在任意可用的 agent上執行pipelinenone
:pipeline將不分配全局agent,每個 stage分配自己的agentlabel
:指定運行節點agent的 Labelnode
:自定義運行節點配置,- 指定 label
- 指定 customWorkspace
docker
:使用給定的容器執行流水線。dockerfile
:使用源碼庫中包含的Dockerfile構建的容器來執行Pipeline。kubernetes
:在kubernetes集羣執行Pipeline
上述參數也可以用於stage中。
示例腳本:本段配置待定
pipline {
agent {
node {
label "myslave"
customWorkspace "myWorkspace"
}
}
}
stages
包含一個或多個 stage, Pipeline的大部分工作在此執行。stages也是必須指定的指令,沒有參數。此外,每個 pipeline塊中必須只有一個 stages。
stage也必須指定,需要定義stage的名字:
pipeline {
agent any
stages {
stage('init') {
steps {
echo 'Hello World'
}
}
}
}
steps
steps位於stage塊中,也是必須設置的指令,無參數。
steps塊中可以包含script塊,可用於存放Scripted Pipeline 腳本:
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
}
}
post
post是在Pipeline或者 stage執行結束後的操作,不是必須出現的指令,可設置以下觸發條件:
always
:無論 Pipeline或者stage運行完成的狀態如何都會運行changed
:只有當前 Pipeline或者stage運行的狀態與先前運行狀態不同時才能運行fixed
:只有當前一次構建失敗或者不穩定,而當前 Pipeline或者stage運行成功時運行regression
:前一次運行成功,而當前Pipeline或者stage運行狀態爲failure, unstable 或者 aborted時運行aborted
:只有當前 Pipeline或者stage處於“aborted”狀態時才能運行。通常是手動終止。failure
:當前 Pipeline或者stage處於“failed”狀態時才運行success
:當前 Pipeline或者stage具有“success”狀態時才運行unstable
:當前 Pipeline或者stage具有“unstable”狀態才運行unsuccessful
:當前 Pipeline或者stage不是“success”狀態時運行cleanup
:不管Pipeline或stage的狀態如何,在每一個post條件被執行之後運行。
示例腳本:
pipeline {
agent any
stages {
stage('init') {
steps {
echo 'Hello World'
}
}
}
post {
success {
echo 'success!'
sleep 2
}
always {
echo 'goodbye'
}
}
}
Directives
Declarative pipeline也包含了各種指令,比如environment,options,parameters,triggers等,指令不是必須出現的指令。
environment
定義 Pipeline或stage運行時的環境變量,位於pipeline塊頂部的 environment
定義的環境變量適用於流水線中的所有步驟,stage
中定義的 environment
只能應用於 stage
中的步驟。
pipeline {
agent any
environment {
CC = 'clang'
}
stages {
stage('Example') {
environment {
hlw = 'hello world'
}
steps {
echo hlw
}
}
}
}
options
options指令允許在Pipeline內部定義 pipeline專有屬性。
pipeline可用選項:
buildDiscarder
:保持構建的最大個數,超過最大構建數後會丟棄以前的構建。checkoutToSubdirectory
:在工作區的子目錄中執行自動源代碼控制checkout 。disableConcurrentBuilds
:禁止並行執行 pipeline任務,可用於防止同時訪問共享資源。disableResume
:如果控制器重啓,不允許管道恢復。newContainerPerStage
:與docker或dockerfile頂級代理一起使用。指定後,每個階段將運行在同一節點上的新容器實例中,而不是所有階段都運行在同一個容器實例中。overrideIndexTriggers
:允許重寫分支索引觸發器的默認處理方法。如果分支索引觸發器在多分支或組織標籤處被禁用,options { overrideIndexTriggers(true) }
將僅爲該job啓用。否則,options { overrideIndexTriggers(false) }
將只禁用該job的分支索引觸發器。preserveStashes
:保留已完成構建的stashes ,以便在stage 重啓時使用。例如:options {preserveStashes()}
用於保存最近完成構建的stashes ,或者options {preserveStashes(buildCount: 5)}
用於保存最近完成構建的五個堆棧。quietPeriod
:爲Pipeline設置靜默期(以秒爲單位),覆蓋全局默認值。例如:options {quietPeriod(30)}
skipDefaultCheckout
:在agent指令中,默認情況下跳過從源代碼Checkout代碼。例如:options {skipDefaultCheckout()}
skipStagesAfterUnstable
:一旦構建狀態變爲 “Unstable“ 狀態,就跳過stages。例如:options {skipstagesafterstable ()}
retry
:失敗後,重試整個 Pipeline的次數例如:options { retry(3) }
timeout
:爲Pipeline的運行設置一個超時時間,在此之後,Jenkins就中止Pipeline。例如:options {timeout(time: 1, unit: 'HOURS')}
timestamps
:在所有由Pipeline運行生成的控制檯輸出前加上間。例如:options {timestamp ()}
parallelsAlwaysFailFast
:將流水線中所有後續並行階段的failfast設置爲true,用於並行執行stage中。例如:options {parallelsAlwaysFailFast()}
stage的options指令類似於Pipeline的options指令。然而,stage選項只能包含retry
, timeout
, 或 timestamps
的步驟,或者與stage相關的聲明性選項,比如skipDefaultCheckout
。
在stage中,在進入agent或檢查任何 when條件之前調用options指令中的步驟。
stage 可用選項:
skipDefaultCheckout
timeout
:爲當前stage的運行設置一個超時時間retry
timestamps
示例腳本:
pipeline {
agent any
options {
timeout (time: 1, unit: 'HOURS')
buildDiscarder(logRotator(numToKeepStr: '2')
retry(5)
}
stages {
stage('init') {
options {
timeout(time: 30, unit: 'SECONDS')
}
steps {
echo 'Hello World'
}
}
}
}
parameters
parameters
指令定義 pipeline的專有參數列表,支持參數類型:
string
:字符串類型text
:文本, 可包括多行booleanParam
:布爾參數choice
:choice 參數password
:密碼參數
示例腳本:
pipeline {
agent any
parameters {
string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '')
text(name: 'DEPLOY_TEXT', defaultValue: 'One\nTwo\nThree\n', description: '')
booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '')
choice(name: 'CHOICES', choices: ['one', 'two', 'three'], description: '')
password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'A secret password')
}
stages {
stage('Example') {
steps {
echo "${params.DEPLOY_ENV}"
echo "${params.PASSWORD}"
}
}
}
}
triggers
triggers
指令定義了 Pipeline自動化觸發的方式,主要包括3種觸發器:
cron
:接受一個cron樣式的字符串來定義 Pipeline觸發的間隔週期,例如:triggers { cron('H */4 * * 1-5') }
pollSCM
:接受一個cron樣式的字符串來定義 Jenkins檢查SCM源碼更新的常規間隔;如果存在新的更改,Pipeline將被重新觸發。例如:triggers { pollSCM('H */4 * * 1-5') }
upstream
:接受以逗號分隔的job字符串和閾值。當字符串中的任何作業以最小閾值完成時,將重新觸發Pipeline。例如:triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }
更多cron表達式語法介紹可參考 Linux cron定時介紹。
示例腳本:
pipeline {
agent any
triggers {
cron('H */4 * * 1-5')
}
stages {
stage('init') {
steps {
echo 'Hello World'
}
}
}
}
stage
前面介紹過,stage指令位於stages塊中,也是必須設置的指令,應至少包含一個。
tools
定義自動安裝和PATH
上的工具。如果沒有指定agent,則忽略此參數。
支持如下工具:
- maven
- jdk
- gradle
示例腳本:
pipeline {
agent any
tools {
maven 'apache-maven-3.0.1'
}
stages {
stage('Example') {
steps {
sh 'mvn --version'
}
}
}
}
其中工具名 apache-maven-3.0.1
必須在jenkins中預先配置:Manage Jenkins -> Global Tool Configuration 。
input
stage
的 input
指令允許使用 input
step提示輸入。 在應用了此 options
後,進入 stage
的 agent
或評估 when
條件前, stage
將暫停。 如果 input
被同意, stage
將會繼續。
可配置選項如下:
message
:必須指定,是呈現給用戶的信息。id
:可選標識符, 默認爲stage
名稱。ok
: "ok"按鈕的可選文本。submitter
:以逗號分隔的用戶列表或允許提交input
的外部組名。默認允許任何用戶。submitterParameter
:環境變量的可選名稱。如果存在,用submitter
名稱設置。parameters
:提示提交者提供的一個可選的參數列表。 和前面介紹的parameters
指令定義方法一樣。
示例腳本:
pipeline {
agent any
stages {
stage('Example') {
input {
message "Should we continue?"
ok "Yes, we should."
submitter "alice,bob"
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
}
}
steps {
echo "Hello, ${PERSON}, nice to meet you."
}
}
}
}
when
when
指令位於stage指令中,允許流水線根據給定的條件決定是否應該執行階段,必須包含至少一個條件。
支持以下嵌套條件:
-
branch:當正在構建的分支與分支模式匹配時執行這個階段, 例如:
when { branch 'master' }
。注意,只適用於多分支流水線。 -
buildingTag
:當構建正在構建標記時執行該階段。例如:when {buildingTag()}
-
environment:當指定的環境變量是給定的值時,執行這個步驟, 例如:
when { environment name: 'DEPLOY_TO', value: 'production' }
-
changelog
:如果構建的SCM變更日誌包含一個給定的正則表達式模式,則執行該階段,例如:when { changelog '.*^\\[DEPENDENCY\\] .+$' }
-
changeset
:如果構建的SCM變更集包含一個或多個匹配給定模式的文件,則執行該階段。例如:when { changeset "**/*.js" }
-
changeRequest
:如果當前構建是爲了一個“change request”(比如GitHub的Pull request, GitLab上的Merge request, Gerrit中的change等)時執行這個階段。如果沒有傳遞參數,stage在每次變更請求上運行,例如:when { changeRequest() }
-
environment
:當指定的環境變量設置爲給定的值時執行,例如:when { environment name: 'DEPLOY_TO', value: 'production' }
-
equals
:當期望值與實際值相等時執行,例如:when { equals expected: 2, actual: currentBuild.number }
-
expression
:當指定的Groovy表達式爲true時執行, 例如:when { expression { return params.DEBUG_BUILD } }
。注意,當表達式返回字符串時,它們必須轉換爲布爾值,或者返回null來計算爲false。簡單地返回“0”或“false”仍然會被計算爲“true”。 -
tag
:如果TAG_NAME
變量與給定的模式匹配則執行該階段。例如:when { tag "release-*" }
。如果提供了一個空模式,那麼如果存在TAG_NAME變量,則該階段將執行(與buildingTag()
相同)。 -
not
:當嵌套條件是false
時執行這個階段,它必須包含一個條件,例如:when { not { branch 'master' } }
-
allOf
:當所有的嵌套條件都爲true
時執行,必須包含至少一個條件,例如:when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
-
anyOf
:當至少有一個嵌套條件爲true
時執行,必須包含至少一個條件,例如:when { anyOf { branch 'master'; branch 'staging' } }
-
triggeredBy
:噹噹前構建被給定的參數觸發時執行。例如:when { triggeredBy 'SCMTrigger' }
when { triggeredBy 'TimerTrigger' }
when { triggeredBy 'BuildUpstreamCause' }
when { triggeredBy cause: "UserIdCause", detail: "vlinde" }
在進入 stage
的 agent
前評估 when
- 默認情況下,如果定義了該
stage
的agent
,則stage
的when
條件將在進入該stage
的agent
之後計算。但是,這可以通過在when
塊中指定beforeAgent
選項來更改。如果beforeAgent
設置爲true
,則首先計算when
條件,只有當條件計算爲true
時才進入agent
。
在 input
指令之前評估 when
- 默認情況下,如果定義了
stage
的when
條件,則不會在input
之前求值。但是,這可以通過在when
塊中指定beforeInput
選項來更改。如果beforeInput
設置爲true
,when
條件將首先計算,只有當條件計算爲true時纔會進入input
。 beforeInput true
優先級高於beforeAgent true
。
在 options
指令之前評估 when
- 默認情況下,如果
stage
定義了任何options
,stage
的when
條件將在進入該stage
的options
之後計算。但是,這可以通過在when
塊中指定beforeOptions
選項來更改。如果beforeOptions
設置爲true,則首先計算when
條件,只有當條件計算爲true
時纔會進入options
。 beforeOptions true
優先級高於beforeInput true
和beforeAgent true
。
示例腳本:
pipeline {
agent any
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Deploy1') {
when {
branch 'production1'
}
steps {
echo 'Deploying1'
}
}
stage('Deploy2') {
when {
allOf {
branch 'production2'
environment name: 'DEPLOY_TO', value: 'production2'
}
}
steps {
echo 'Deploying2'
}
}
stage('Deploy3') {
when {
beforeInput true
branch 'production3'
}
input {
message "Deploy to production3?"
id "simple-input"
}
steps {
echo 'Deploying3'
}
}
}
}
並行執行
聲明式流水線支持多階段並行執行,Parallel
塊中,當其中任何一個階段失敗時,可以通過添加failFast true
來強制所有並行階段都中止。也可以在管道定義中添加一個options
: options {parallelsAlwaysFailFast() }
pipeline {
agent any
stages {
stage('Non-Parallel Stage') {
steps {
echo 'This stage will be executed first.'
}
}
stage('Parallel Stage') {
when {
branch 'master'
}
failFast true
parallel {
stage('Branch A') {
agent {
label "for-branch-a"
}
steps {
echo "On Branch A"
}
}
stage('Branch B') {
agent {
label "for-branch-b"
}
steps {
echo "On Branch B"
}
}
}
}
}
}
聲明式流水線可以將stage放入矩陣單元中,Matrix可以定義一個多維的 name-value組合矩陣,並將其並行運行。具體使用方法可以參考官方文檔。
Scripted pipeline
Scripted pipeline是基於groovy語法定製的一種DSL語言,它的靈活性更高,具有更好的可擴展性。
使用它需要了解一定的groovy語法知識,可參考如下文檔:
請看下面的Scripted pipeline腳本,使用了if/else和try/catch/finally流程控制方法,無論構建狀態是什麼都發送郵件:
node('win_agent') {
try {
// 自動化測試
stage('Test') {
TEST_STATU = bat (
script: '''echo autotest''',
returnStatus: true
) == 0
if ("${TEST_STATU}" == "false") {
catchError(stageResult: 'FAILURE') {
echo "測試未通過"
env.LOG = "測試未通過"
}
} else {
echo "測試通過"
env.LOG = "測試通過"
}
}
} catch (exc) {
currentBuild.result = 'FAILURE'
echo "Something failed, I'm in the catch block."
} finally {
stage("email") {
emailext (
subject: '\'構建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}\'',
to: "[email protected]",
body: '${FILE,path="email.html"}',
)
}
}
}
DSL方法步驟
前面介紹Declarative pipeline和Scripted pipeline語法的實例中,使用到了一些方法步驟,比如sh
、echo
、emailext
等,它們是jenkins插件提供的一些方法步驟,下面舉2個例子:
1、bat/sh方法:
STATU = bat (
script: '''echo 666''',
returnStatus: true
) == 0
echo ${STATU}
2、catchError方法:捕獲到異常後,設置當前構建或者階段狀態設置爲FAILURE,並繼續執行Pipeline後面的步驟:
stage('Test') {
STATU = bat (
script: '''echo 666''',
returnStatus: true
) == 0
if ("${TEST}" == "false") {
catchError(stageResult: 'FAILURE') {
echo "測試未通過"
}
} else {
echo "測試通過"
}
}
也可以用try-catch
來實現。
當前jenkins平臺支持的更多步驟可以訪問 http://:/pipeline-syntax/html 查看。
小結
本文介紹了兩種pipeline腳本語法,使用pipeline腳本可以使任務調度更加靈活,特別是對於比較複雜的項目。
另外,這兩種pipeline腳本語法通常會一起使用,爲了讓代碼更加簡潔,建議使用pipeline共享庫,將groovy腳本放到共享庫中,這樣多個pipeline項目就可以共用這些方法,大大減少代碼冗餘。
參考文檔:
- https://www.jenkins.io/zh/doc/book/pipeline/
- https://www.jenkins.io/doc/book/pipeline/syntax/
- https://www.jenkins.io/zh/doc/book/pipeline/syntax
- https://www.jenkins.io/blog/2019/12/02/matrix-building-with-scripted-pipeline/