Pipeline As Code With Jenkins2.0

CodeSheep發表於2018-03-20

Jenkins2.0 Pipeline匯入

Pipeline as Code是Jenkins 2.0版本的精華所在,是幫助Jenkins實現從CI到CD華麗轉身的關鍵工具。

所謂Pipeline,簡單來說,就是一套執行於Jenkins上的工作流框架,將原本獨立執行於單個或者多個節點的任務連線起來,實現單個任務難以完成的複雜釋出流程

Pipeline實現複雜釋出流程

Pipeline的實現方式是一套Groovy DSL(類似Gradle),任何釋出流程都可以表述為一段Groovy指令碼,並且Jenkins支援從程式碼庫直接讀取指令碼,從而實現了Pipeline as Code的理念。

注: 本文首發於 My 公眾號 CodeSheep ,可 長按掃描 下面的 小心心 來訂閱 ↓ ↓ ↓

CodeSheep · 程式羊


為什麼要使用Jenkins2.0 Pipeline

這裡主要結合我自己對傳統Jenkins Job使用的一些痛點來說:

  • 傳統的Jenkins Job難以靈活高效地並行(Job間、節點間、任務間、甚至任務內四個維度的並行)

  • 傳統的Jenkins Job日益失控的趨勢讓我們措手不及,Job太多,CI指令碼太離散,維護成本實在太高了,而且很危險,一單Jenkins Server掛了,一切都Game Over了

  • 新拉分支的分支程式碼CI部署太麻煩了

  • 傳統的Jenkins Job顯示真的是不太直觀啊

我想這些理由應該足以讓我們把目光轉向Jenkins2.0的Pipeline!

Pipeline的功能和優點:

  1. 永續性:在jenkins的master按計劃和非計劃的重啟後,pipeline的job仍然能夠工作,不受影響。其實理解起來也很簡單,jenkins的master和agent通過ssh連線,如果你知道nohup或disown的話,就可以理解為啥master的重啟不會影響agent上的job繼續執行。
  2. 可暫停性:pipeline基於groovy可以實現job的暫停和等待使用者的輸入或批准然後繼續執行。 3. 更靈活的並行執行,更強的依賴控制,通過groovy指令碼可以實現step,stage間的並行執行,和更復雜的相互依賴關係。
  3. 可擴充套件性:通過groovy的程式設計更容易的擴充套件外掛。
  4. 設計Pipeline = 設計程式碼,很優雅
  5. As Code:集中管理CI指令碼、用程式碼庫來管理指令碼、從程式碼庫直接讀取指令碼,從而可以將專案CI迅速拉起來!

Pipeline原理與流程

Pipeline為使用者設計了三個最最基本的概念:

  • Stage:一個Pipeline可以劃分為若干個Stage,每個Stage代表一組操作。注意,Stage是一個邏輯分組的概念,可以跨多個Node。
  • Node:一個Node就是一個Jenkins節點,或者是Master,或者是Agent,是執行Step的具體執行期環境。
  • Step:Step是最基本的操作單元,小到建立一個目錄,大到構建一個Docker映象,由各類Jenkins Plugin提供。

一個典型的Stage View如下圖所示:

典型的Stage View

從圖中可以十分方便地看到哪些Stage通過,哪些Stage失敗,以及構建的時間。

Jenkins2.0的Pipeline搭建使用的是Groovy指令碼,通過Groovy指令碼實現工作流管理的步驟如下:

  • 去Jenkins主介面建立Pipeline任務

建立PipeLine任務

實際上更常用的是MultiBranch Pipeline,上面的圖中截圖沒有包含,但與普通Pipeline基本類似。

  • 使用Groovy指令碼自定義工作流

使用Groovy自定義工作流

上圖的例項指令碼如下:

node { 
    stage('Checkout Code') { // for display purposes 
        // Get some code from a GitHub repository 
        git 'https://github.com/jglick/simple-maven-project-with-tests.git' 
    }
 
    stage('Build') { 
        // Run the maven build 
        if (isUnix()) { 
            sh "'${MAVEN_HOME}/bin/mvn' -Dmaven.test.failure.ignore clean package" 
        } else { 
            bat(/"${MAVEN_HOME}\bin\mvn" -Dmaven.test.failure.ignore clean package/) 
        } 
    } 
 
    stage('Unit test') { 
        junit '**/target/surefire-reports/TEST-UT.xml' 
        archive 'target/*.jar' 
    } 
} 
複製程式碼
  • 開始執行Pipeline

構建過程的stage View如下:

stage View

很明顯可以看出,這裡顯示的和Groovy指令碼中格式化的程式碼是一致的,會實時顯示各個工作流的執行進度和結果,直觀易懂。滑鼠移上去,能看到日誌資訊的縮圖,單擊可以調到對應stage的console中。

總而言之,一切都是那麼地優雅!


Jenkins2.0 Pipeline關鍵DSL語法及示例

在這裡總結一下Pipeline中的關鍵DSL語法,利用Groovy對其進行組合可以完成任何一項複雜的CI/CD流程,熟悉它們大有裨益。

  • archiveArtifacts

歸檔檔案,舉例:

archiveArtifacts 'target/*.jar'
複製程式碼
  • bat

執行windows平臺下的批處理檔案,如

bat "call example.bat"
複製程式碼
  • build

觸發構建一個jenkins job,如

build 'TEST_JOB'
複製程式碼
  • checkout

從SCM系統中checkout repo,如:

checkout([$class: 'SubversionSCM', additionalCredentials: [], excludedCommitMessages: '', excludedRegions: '', excludedRevprop: '', excludedUsers: '', filterChangelog: false, ignoreDirPropChanges: false, includedRegions: '', locations: [[credentialsId: '30e6c1e5-1035-4bdd-8a44-05ba8f885158', depthOption: 'infinity', ignoreExternalsOption: true, local: '.', remote: 'svn://xxxxxx']], workspaceUpdater: [$class: 'UpdateUpdater']]) 
複製程式碼
  • deleteDir()

從workspace中刪除當前目錄

  • dir

切換目錄,如

dir('/home/jenkins') { // 切換到/home/jenkins目錄中做一些事情
    // some block
}
複製程式碼
  • echo

列印資訊,如 echo 'hello world'

  • emailtext

利用Jenkins傳送郵件,內容、主題全都可以自定義,如

emailext body: 'Subject_test', subject: 'Subject_test', to: 'hansonwang99@163.com.cn'
// 郵件的正文body,主題subject,收件人to等可以進行自定義
複製程式碼
  • error

丟擲一個錯誤訊號,可以自行在程式碼裡丟擲,如 error 'read_error'

  • fileExists

檢查工作空間某個路徑裡是否存在某個file,舉例:

fileExists '/home/test.txt'  // 檢查是否存在test.txt
複製程式碼
  • input

等待外界使用者的互動輸入,舉例:

input message: '', parameters: [string(defaultValue: '預設值', description: '版本號', name: 'version')] // 在某一步驟,等待使用者輸入version引數才能往下執行
複製程式碼
  • isUnix

用於判斷當前任務是否執行於Unix-like節點上,舉例:

def flag = isUnix()
if( flag == false ) { // 可以據此進行判斷
	echo "not run on a unix node !"
}
複製程式碼
  • load

呼叫一個外部groovy指令碼,舉例:

load 'D:\\jenkins\\workspace\\test.groovy'
複製程式碼
  • node

分配節點給某個任務執行,舉例:

node('節點標籤') { // 在對應標籤的節點上執行某項任務
	Task()
}
複製程式碼
  • parallel

並行地執行任務,可以說是最實用高效的工具了,舉例:

parallel(   //並行地執行android unit tests和android e2e tests兩個任務
	'android unit tests': {
		runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-android-docker-unit-tests.sh', '--privileged --rm')
	},
	'android e2e tests': {
	runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-ci-e2e-tests.sh --android --js', '--rm')
	}
)
複製程式碼
  • properties

設定Job的屬性,舉例:

properties([parameters([string(defaultValue: '1.0.0', description: '版本號', name: 'VERSION')]), pipelineTriggers([])]) // 為job設定了一個VERSION引數
複製程式碼
  • pwd 顯示當前目錄

  • readFile

從工作空間中讀取檔案,舉例:

def editionName = readFile '/home/Test/exam.txt'
複製程式碼
  • retry

重複body內程式碼N次,舉例:

retry(10) {
    // some block
}
複製程式碼
  • sh

執行shell指令碼,如:sh "sh test.sh"

  • sleep

延時,如延時2小時:sleep time: 2, unit: 'HOURS'

  • stage

建立任務的stage,舉例:

stage('stage name') {
    // some block
}
複製程式碼
  • stash

存放檔案為後續構建使用,舉例:

dir('target') {
	stash name: 'war', includes: 'x.war'
}
複製程式碼
  • unstash

將stash步驟中存放的檔案在當前工作空間中重建,舉例:

def deploy(id) {
    unstash 'war'
    sh "cp x.war /tmp/${id}.war"
}
複製程式碼
  • timeout

時間限制,舉例

timeout(time: 4, unit: 'SECONDS') {
    // some block
}
複製程式碼
  • timestamps

用於在控制檯加時間戳,舉例:

timestamps {
    // some block
}
複製程式碼
  • touch

建立檔案,舉例:

touch file: 'TEST.txt', timestamp: 0
複製程式碼
  • unzip

解壓檔案,舉例:

unzip dir: '/home/workspace', glob: '', zipFile: 'TEST.zip'
複製程式碼
  • validateDeclarativePipeline

檢查給定的檔案是否包含一個有效的Declarative Pipeline,返回T或者F

validateDeclarativePipeline '/home/wospace'
複製程式碼
  • waitUntil

等待,直到條件滿足

waitUntil {
    // some block
}
複製程式碼
  • withCredentials

使用憑據

withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) {
    sh '''
      set +x
      curl -u $USERPASS https://private.server/ > output
    '''
}
複製程式碼
  • withEnv

設定環境變數,注意近本次執行有效!

withEnv(['MYTOOL_HOME=/usr/local/mytool']) {
    sh '$MYTOOL_HOME/bin/start'
}
複製程式碼
  • writeFile

寫檔案到某個路徑

writeFile file: '/home/workspace', text: 'hello world'
複製程式碼
  • writeJSON

寫JSON檔案,用法基本同上

  • zip

建立zip檔案

zip dir: '/home/workspace', glob: '', zipFile: 'TEST.zip'
複製程式碼
  • ws

自定義工作空間,在其中做一些工作,效果類似於Dir命令,舉例:

ws('/home/jenkins_workspace') {
    // some block
}
複製程式碼

後記

作者更多的SpringBt實踐文章在此:


如果有興趣,也可以抽點時間看看作者一些關於容器化、微服務化方面的文章:


CodeSheep · 程式羊


相關文章