jenkins 原理篇——pipeline流水線 宣告式語法詳解

2023-10-23 15:03:53

大家好,我是藍胖子,相信大家平時專案中或多或少都有用到jenkins,它的piepeline模式能夠對專案的釋出流程進行編排,優化部署效率,減少錯誤的發生,如何去寫一個pipeline指令碼呢,今天我們就來簡單看看pipeline的語法。

先拿一個hello world的pipeline指令碼舉例,我們來看看pipeline指令碼的組成

pipeline {
    agent any

    stages {
        stage('Hello') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

指令碼都是以pipeline的關鍵字開頭,接著看下pipeline內部具體由哪幾部分組成。

agent

agent 部分指明瞭pipeline指令碼在哪臺機器或者容器內執行,因為jenkins的工作模式是master-agent模式,master可以把流水線任務的執行放到其代理節點上執行。

同時jenkins的節點(master節點或者agent代理節點)可以打上標籤,如下表示的是pipeline指令碼需要在標籤為jdk8的節點上執行。

pipeline {
    agent {
	    node {label:'jdk8'}
    }

    stages {
        stage('Hello') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

agent模組也可以寫到stage裡,表示特定stage模組都在指定的agent節點上執行,如下所示,

pipeline {
   agent any
    stages {
        stage('Hello') {
             agent {
	    node {label:'jdk8'}
		    }
            steps {
                echo 'Hello World'
            }
        }
    }
}

在文章開頭的hello world的指令碼中,agent我們指定了any,這表示可以在任意節點上執行pipeline指令碼。agent模組不可省略不寫。

stages

接著來看下stages模組,stages模組由多個stage組成,一個stage表示一個階段,比如我們通常將釋出的整個流程分為編譯,傳輸,部署等幾個階段。

stage

一個階段由多個步驟組成,在pipeline語法中,步驟通過steps模組表示,steps包含了一個或多個步驟,在上述hello world的pipeline指令碼中,echo 'Hello World' 就是一個步驟,比如我們想要執行shell命令就要執行sh步驟,如下所示,

pipeline {
    agent any

    stages {
        stage('Hello') {
            steps {
                sh 'ping 127.0.0.1'
            }
        }
    }
}

pipeline的步驟是可插拔的,可以通過安裝某些外掛來執行特定的步驟。

post

除了上述模組,還可以在stages或者steps模組後面定義post模組來表示整個pipeline執行完成或者單個stage完成後需要執行的動作。

如下所示,

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                sh 'echo Build stage ...'
            }
            post {
	        	always {
	        		echo "post condition executed: always ..."
	        	}        	
        	}
        }
    }
        post {
    	unstable {
    		echo "post condition executed: unstable ..."
    	}
    	failure {
    		echo "post condition executed: unsuccessful ..."
    	}
    	cleanup {
    		echo "post condition executed: cleanup ..."
    	}
    }
  }

post模組可以定義多個條件塊,條件塊裡寫上要執行的步驟,條件塊有以下幾種,

  • always: 無論當前執行結果是什麼狀態都執行
  • changed: 當前完成狀態和上一步完成狀態不同則執行
  • fixed: 上一步為失敗或不穩定(unstable) ,當前狀態為成功時
  • regression: 上一次完成狀態為成功,當前完成狀態為失敗、不穩定或中止(aborted)時執行。
  • aborted: 當前執行結果是中止狀態時(一般為人為中止)執行。
  • failure:當前完成狀態為失敗時執行。
  • success:當前完成狀態為成功時執行。
  • unstable:當前完成狀態為不穩定時執行,通常是
  • cleanup:清理條件塊。不論當前完成狀態是什麼,在其他所有條件塊執行完成後都執行。

這裡再著重解釋下unstable 狀態,通常我們將測試失敗的狀態設定為unstable,可以把它理解成紀錄檔等級的warn狀態。如下我們可以主動設定stage和構建結果為unstable狀態。

pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                warnError('watch it'){
                  sh '''
                          echo 'Running tests...'
                          exit 1
                          
                        '''   
                }

            }
        }
        stage('Hello2') {
            steps {
                echo 'hello'
            }
        }
    }
    
    post {
        failure {
            echo "failure"
        }
        success {
            echo "success"
        }
        unstable {
            echo "unstable"
        }
    }
}

最終產生的構建檢視如下:

指令

pipeline除了上述的基本結構外,還提供了一些其他指令作為其基本結構的補充,

Jenkins pipeline支援的指令有:

  • environment:用於設定環境變數,可定義在stage或pipeline部分。
  • tools:可定義在pipeline或stage部分。它會自動下載並安裝我們指定的工具,並將其加入PATH變數中。
  • input:定義在stage部分,會暫停pipeline,提示你輸入內容。
  • options:用於設定Jenkins pipeline本身的選項,比如options {retry(3)}指當pipeline失敗時再重試2次。options指令可定義在stage或pipeline部分。
  • parallel:並行執行多個step。在pipeline外掛1.2版本後,parallel開始支援對多個階段進行並行執行。
  • parameters:與input不同,parameters是執行pipeline前傳入的一些引數。
  • triggers:用於定義執行pipeline的觸發器。
  • when:當滿足when定義的條件時,階段才執行。

在使用指令時,需要注意的是每個指令都有自己的「作用域」。如果指令使用的位置不正確,Jenkins將會報錯。

更多的設定案例請參考 流水線語法 (jenkins.io)

嵌入式指令碼

在pipeline 宣告式語法中,當需要執行程式碼塊條件判斷時除了使用when指令,還可以使用groovy語法的指令碼,指令碼還可以執行for迴圈的操作,設定程式碼如下,指令碼需要被script塊包括起來

寫script塊內的指令碼需要先簡單瞭解下groovy的語法

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"
                    }
                }
            }
        }
    }
}

函數呼叫

pipeline指令碼同樣可以定義函數,然後通過呼叫函數來執行一段邏輯,函數的定義遵循groovy的語法,如下,我定義了一個hello的函數,然後對其進行呼叫。

注意,函數定義是放到pipeline外面的。因為要接收函數返回值,所以整個函數呼叫是放到了script塊裡,groovy的語法中雙引號中可以用$變數名來參照特定的變數。

def hello(String name){
    echo "$name"
    return "Yes"
}
pipeline {
    agent any

    stages {
        stage('Hello2') {
            steps {
             script {
                def  res = hello('Beatiful')
                echo "$res"
             }
            }
        }
    }
    
}

共用庫

雖然pipeline指令碼能夠定義函數提取重複邏輯,但是如果有100個pipeline指令碼都需要同一個函數,不可能定義100遍這樣的函數,所以jenkins提供了共用庫的機制,來讓我們方便去定義一個函數可以供多個pipeline指令碼使用。下面介紹下寫一個共用庫的基本步驟,

建立一個程式碼庫,並上傳git

我們要寫的函數就定義在vars目錄下,在vars目錄中的檔名如果有多個單詞需遵循駝峰格式的命名,到時候參照函數的函數名和vars目錄下的檔名一致。

BoxJenkins
├── README.md
├── src
└── vars
    ├── hello.groovy

hello.groovy 的檔案內容如下,注意裡面的函數名call是固定的,不可變動。

def call(String name){  
    echo "$name"  
}

jenkins 引入共用庫

在Jenkins的Manage Jenkins→Configure System→Global Pipeline Libraries中設定共用庫的git地址。

其中Name則是到時候我們要引入的庫名稱

pipeline 中引入共用庫

引入共用庫的語法為 在pipeline塊外部使用@Library('lanpangzi-lib@master') _ ,以下是設定範例,其中@master 中的master代表庫的版本,可以由分支名,tag名,乃至commit id代替。

@Library('lanpangzi-lib@master') _

pipeline {
    agent any

    stages {
        stage('Hello2') {
            steps {
             hello('wudi')
            }
        }
    }
    
}

關於共用庫還有更多的用法,例如在共用庫中定義pipeline模板,以及使用共用庫中src的程式碼,這裡就不再展開了。

總結

這一節,基本上對jenkins的pipeline指令碼語法做了比較完整的介紹,在以後再看pipeline指令碼時,可能還會接觸到許多外掛提供的函數或更多的指令,但是它們都逃不開pipeline指令碼的基本結構,掌握了基礎語法,後面才能更上一層樓。