「持續集成實踐系列 」Jenkins 2.x 構建CI自動化流水線常見技巧
在上一篇文章中,我們介紹了Jenkins 2.x實現流水線的兩種語法,以及在實際工作中該如何選擇腳本式語法或聲明式語法。原文可查閱:「持續集成實踐系列」Jenkins 2.x 搭建CI需要掌握的硬核要點(一)
在使用傳統的Jenkins Web界面和項目時,比如自由風格類型的任務,我們對處理流程的控制能力是有限的。所採用的典型形式是任務鏈:任務完成后觸發其他的任務。或者我們可能會包括構建后處理,不管任務成功完成與否,總是去做一些類似發送通知的事情。
除了這些基本的功能外,還可以添加條件性構建步驟插件,通過基於單個或者多個條件的構建步驟來定義更加複雜的流程。但即便如此,相比於我們編寫程序時可以直接控制執行流程的方法,條件性構建步驟插件對流程的控制能力依然有限。
在本篇中,我們將聊一下,關於Jenkins流水線DSL語言所提供的用於控制流水線執行流程基本結構和一些常見技巧。
1. Pipeline流水線指令常見結構
正如在系列第一篇文章中介紹到的,Jenkins DSL採用的是Groovy腳本語言。這也意味着如果當你掌握了Groovy語言,可以按照需求在流水線中使用Groovy語言的結構和習慣用法,針對這一類使用者,通常會更傾向於用腳本式語法來實現流水線。但不管採用的是哪種語法,從流水線組成的角度來講,都是由一些不同指令+步驟構建結構化代碼塊。
對於腳本式流水線,基本結構如下:
node('worker'){
stage('階段'){
// DSL
}
}
構建腳本式流水線常用的結構或者說代碼塊節點主要由node
和stage
兩個組成。
而,聲明式流水線基本結構構成環節相對要多一些,整理了一張圖如下:
需要劃一個重點:可以簡單理解node
是用於腳本式流水線,而agent
則是用於聲明式流水線。
Jenkins Pipeline支持的指令(常見):
指令名 | 說明 | 作用域 |
---|---|---|
agent | 指定流水線或特定階段在哪裡運行。 | stage 或pipeline |
environment | 設置環境變量 | stage或pipeline |
tools | 自動下載並安裝指定的工具,並將其加入到PATH變量中 | stage或pipeline |
input | 暫停pipeline,提示輸入內容 | stage |
options | 用來指定一些預定義選項 | stage 或 pipeline |
parallel | 并行執行多個step | stage |
parameters | 允許執行pipeline前傳入一些參數 | pipeline |
triggers | 定義執行pipeline的觸發器 | pipeline |
when | 定義階段執行的條件 | stage |
build | 觸發其他的job | steps |
options Jenkins Pipeline常見配置參數:
參數名 | 說明 | 例子 |
---|---|---|
buildDiscarder | 保留最近歷史構建記錄的數量 | buildDiscarder(logRotator(numToKeepStr: ’10’) |
timestamps | 添加時間戳到控制台輸出 | timestamps() |
disableConcurrentBuilds | 阻止Jenkins併發執行同一個流水線 | disableConcurrentBuilds() |
retry | pipeline發生失敗后重試次數 | retry(4) |
timeout | pipeline運行超時時間 | timeout(time:1, unit: ‘HOURS’) |
示例:
pipeline{
agent any
options{
buildDiscarder(logRotator(numToKeepStr: '10')
timestamps()
retry(3)
timeout(time:1, unit: 'HOURS')
}
stages{
stage('demo'){
steps{
sh 'echo hello'
}
}
}
}
更多pipeline指令,可參見官方介紹:
https://www.jenkins.io/doc/book/pipeline/syntax/#
下述僅挑幾個常用的,用於流水線流程控制選項的指令項,介紹一些常用技巧。
2. 超時(Timeout)
這個timeout步驟允許限制等待某個行為發生時腳本所花費的時間。其語法相當簡單。示例如下:
timeout(time:60,unit:'SECONDS'){
//該代碼塊中的過程被設置為超時
}
默認的時間單位是min。如果發生超時,該步驟就會拋出一個異常。如果異常沒有被處理,將導致整個流水線過程被中止。
通常推薦的做法是,在使用timeout
對任何造成流水線暫停的步驟(如一個input步驟)進行封裝,這樣做的結果是,即使出現差錯導致在限定的時間內沒有得到期望的輸入,流水線也會繼續執行。
示例如下:
node{
def response
stage('input'){
timeout(time:10,unit:'SECONDS'){
response = input message :'Please Input User'
parameters:[string(defaultValue:'mikezhou',description:'Enter UserId:',name:'userid')]
}
echo "Username = " + response
}
}
在這種情況下,Jenkins將會給用戶10s做出反應,如果時間到了,Jenkins會拋出一個異常來中止流水線。
如果實際在設計流水線時,當超時發生時,並不想中止流水線向下執行,可以引入try...catch
代碼塊來封裝timeout。
如下代碼塊所示:
node{
def response
stage('input'){
try {
timeout(time:10,unit:'SECONDS'){
response = input message :'Please Input User'
parameters:[string(defaultValue:'mikezhou',description:'Enter UserId:',name:'userid')]
}
}
catch(err){
response = 'user1'
}
}
}
需要注意的是,在處理異常的時候,可以在捕獲異常處設置為期望的默認值。
3. 重試(retry)
這個retry閉包將代碼封底裝為一個步驟,當代碼中有異常發生時,該步驟可以重試n次。其語法如下:
retry(n){
//代碼過程
}
如果達到重試的限制並且發生了一個異常,那麼整個過程將會被中止(除非異常被處理,如使用try...catch
代碼塊)
retry(2){
try {
def result=build job: "test_job"
echo result
}
catch(err){
if(!err.getMessage().contains("UNSTABLE"))
throw err
}
}
4. 等待直到(waitUntil)
引入waitUntil
步驟,會導致整個過程一直等待某件事發生,通常這裏的“某件事”指的是可以返回true的閉包。
如果代碼過程永不返回true的話,這個步驟將會無期限地等待下去而不會結束。所以一般常見的做法,會結合timeout步驟來封裝waitUntil步驟。
例如,使用waitUntil代碼塊來等待一個標記文件出現:
timeout(time:15,unit:'SECONDS'){
waitUntil{
def ret = sh returnStatus:true,script:'test -e /home/jenkins2/marker.txt'
return (ret==0)
}
}
再舉一個例子,假如我們要等待一個Docker容器運行起來,以便我們可以在流水線中通過REST API調用獲取一些數據。在這種情況下,如果這個URL還不可用,就會得到一個異常。為了保證異常被拋出的時候進程不會立即退出,我們可以使用try...catch
代碼塊來捕獲異常並且返回false。
timeout(time:150,unit:'SECONDS'){
waitUntil{
try{
sh "docker exec ${containerid} curl --silent http://127.0.0.1:8080/api/v1/registry >/test/output/url.txt"
return true
}
catch(err)
return false
}
}
5.Stash暫存:實現跨節點文件共享
在Jenkins的DSL中,stash
和unstash
函數允許在流水線的節點間和階段間保存或獲取文件。
基本用法格式:
stash name:"<name>" [includes:"<pattern>" excludes:"<pattern>"]
unstash "<name>"
我們通過名稱或模式來指定一個被包括或被排除的文件的集合。給這些文件的暫存處命名,以便後面通過這個名稱使用這些文件。
提到stash,很多讀者可能會把Jenkins stash和Git stash功能弄混,需要說明一下,Jenkins stash
和Git stash
功能是不同的。Git stash
函數是為了暫存一個工作目錄的內容,緩存那些還沒有提交到本地代碼倉庫的代碼。而Jenkins stash
函數是為了暫存文件,以便在節點間共享。
例如,master節點和node節點,實現跨主機共享文件:
pipeline{
agent none
stages{
stage('stash'){
agent { label "master" }
steps{
writeFile file: "test.txt", text: "$BUILD_NUMBER"
stash name: "test", includes: "test.txt"
}
}
stage('unstash'){
agent { label "node" }
steps{
script{
unstash("test")
def content = readFile("test.txt")
echo "${content}"
}
}
}
}
}
如果你覺得文章還不錯,請大家點贊分享下。你的肯定是我最大的鼓勵和支持。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※教你寫出一流的銷售文案?
※別再煩惱如何寫文案,掌握八大原則!