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 可用選項:
skipDefaultCheckouttimeout:為當前stage的運行設置一個超時時間retrytimestamps
示例腳本:
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: "XXXXXXXX@163.com",
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腳本語法,使用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/
夫唯不爭,故天下莫能與之爭。——《道德經》
