高速流水線,Jenkins Shared Pipeline
前言
不知道你的 team 當(dāng)中是否采用敏捷開發(fā),總之我們的 team 貫徹敏捷方法很徹底

隨著敏捷方法的步伐加快,如何加快軟件的交付速度變得極為重要,快速交付離不開 DevOps,而 DevOps 技術(shù)棧中,Jenkins 絕對是 CI 過程的核心角色。要充分高效的使用 Jenkins,自然離不開 Jenkinsfile
什么是 Jenkinsfile?
在 Jenkins 的世界中,pipeline(流水線)是我們可以和 Jenkins 互動的最小單元,而 Jenkins pipeline 既可以使用 Jenkinsfile 來定義,也可以通過 GUI 來定義。
通過 GUI 來簡單認(rèn)識或了解 Jenkins 是沒問題的,真要用起來 Jenkins,還得用 Jenkinsfile,一句話解釋:
Jenkinsfile 就是用來定義 Jenkins pipeline 的文本文件,最終按照我們的要求的步驟和 Jenkins 互動起來
兩個最簡單的要領(lǐng)就能區(qū)分出寫 Jenkinsfile 的兩種玩法:
Declarative Pipeline(聲明式):Jenkinsfile 以
pipeline開始Scripted Pipeline(腳本式):Jenkinsfile 以
node開始

推薦使用 Declarative Pipeline,因為它提供了很多高級/便利的特性, Jenkinsfile 雖好,寫多了也是非常頭疼的,如何減少寫 code 是關(guān)鍵
為什么要用 Jenkins Shared Library?
當(dāng)今微服務(wù)的世界,大個頭的單體應(yīng)用都被拆分成了多個小的應(yīng)用,每個應(yīng)用基本都會有相同的 Pipeline 步驟:Build,Test,Deploy。Declarative Pipeline 雖好,但每個小的應(yīng)用都要寫那么多重復(fù)的內(nèi)容,作為懶惰的程序猿是忍不了的,因為他們可能僅僅可能是 Git Repo 等參數(shù)不同罷了
所以我們要做的就是把這些共同的內(nèi)容提取出來,寫在一個文件里,形成一個 shared library 讓其他 Jenkinsfile 引用,這就避免了大量重復(fù)代碼
這個shared library 也沒什么神秘的,本質(zhì)上還是一個 Jenkinsfile,只不過表現(xiàn)形式變成了一個 Groovy scripts
如何創(chuàng)建 Jenkins Shared Library?
創(chuàng)建 Jenkins Shared Library 很簡單,只需要三步:
在 shared library Repo 的根目錄創(chuàng)建一個名為 vars 的文件夾
在文件夾中創(chuàng)建你自己定義名稱的 groovy 文件
.
├── Jenkinsfile
├── README.md
└── vars
├── javaProjectCommonLibrary.groovy
├── javaProjectMultiBranchAndParamsCommonLibrary.groovy
├── javaProjectMultiParamsCommonLibrary.groovy
├── nodeProjectCommonLibrary.groovy
└── pythonProjectCommonLibrary.groovy
1 directory, 7 files在groovy 文件中定義一個名為 call 的方法
//javaProjectCommonLibrary.groovy
def call(String name = 'rgyb') {
// 定義自己的通用步驟
echo "Hello, ${name}."
}
這就可以 push 你的 shared library 到 remote 了,在 Jenkins 中再配置一下這個 library 就好了
如何配置 Shared Library?
進(jìn)入 Jenkins,依次點擊 Manage Jenkins -> Configure System -> 找到 Global Pipeline Libraries, 添加 library 就好,然后 Save 就好了

接下來就創(chuàng)建一個 Pipeline job 來引用這個 library
引用 Jenkins Shared Library
如下圖,創(chuàng)建一個 pipeline 類型的 job

在 Pipeline 位置添加如下內(nèi)容:
@Library('first-shared-pipeline') _
javaProjectCommonLibrary 'rgyb rubbish'
具體如下圖

Save 之后就可以 build 了, 打開 log console,可以看到醒目的幾個大字,大聲的讀出來:

到這里,自定義 Jenkins Shared Library 就已經(jīng)搞定了。可實際項目中怎么可能只有一個參數(shù)這么簡單的 pipeline,進(jìn)階玩法還是要知道的
Jenkins Shared Library 高級定義
接收多個參數(shù)很簡單,只需要改一下 call 方法的參數(shù)類型就可以,比如我們定義了一個名為 javaProjectMultiParamsCommonLibrary.groovy 的 library,參數(shù)類型為 Map
//javaProjectMultiParamsCommonLibrary.groovy
def call(Map pipelineParams) {
//自定義code
echo "Welcome, ${pipelineParams.name}."
echo "Let's talk about a story which names ${pipelineParams.storyName}."
}
修改一下 job 中傳入?yún)?shù)的方式:
@Library('first-shared-pipeline') _
javaProjectMultiParamsCommonLibrary(name: 'rubissh rgyb', storyName: 'Hello World')
看一下 build 結(jié)果:

貌似完美,但還是有問題:
shared library 是供其它 Jenkinsfile 引用的,每個項目都應(yīng)該有自己可維護的 Jenkinsfile,直接像上面寫道 Jenkins Pipeline script 中,顯然不是很好的方式,我們也需要將每個項目的 Jenkinsfile 維護在相應(yīng)的Repo 中 大的項目可能有非常多的參數(shù),當(dāng)參數(shù)多的時候,以這種方式調(diào)用,不是很優(yōu)雅,我們希望項目中的 Jenkinsfile 是一種配置文件形式,即 key=value 一個項目可能有多個 branch,每個 branch 的使用應(yīng)該互不影響
為了解決上述問題,我們再創(chuàng)建一個名為 javaProjectMultiBranchAndParamsCommonLibrary.groovy 的 shared library,內(nèi)容如下:
//javaProjectMultiBranchAndParamsCommonLibrary.groovy
def call(body) {
// evaluate the body block, and collect configuration into the object
def pipelineParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
echo "${pipelineParams.NAME}"
echo "${pipelineParams.STORY_NAME}"
echo "${pipelineParams.VERSION}"
}
body 參數(shù)應(yīng)該是一個閉包,它將閉包中的值設(shè)置為對應(yīng)的配置對象,這樣我們只需要在 Jenkinsfile 中按照 key=value 向 Map 中填寫參數(shù)就可以了
在 Repo 根目錄創(chuàng)建 Jenkinsfile,內(nèi)容如下:
@Library('second-shared-pipeline') _
javaProjectMultiBranchAndParamsCommonLibrary {
NAME = 'rgyb rubbish'
STORY_NAME = 'hello world'
VERSION = '1.1.0'
}創(chuàng)
這里引用 shared library 的名稱為:
second-shared-pipeline
接下來創(chuàng)建一個 Multibranch Pipeline 類型的 job

填寫 Jenkinsfile 所在 Branch 的信息

填寫 shared library 的信息,這里名稱為 second-shared-pipeline

點擊 Save 就會掃描當(dāng)前 Repo 的所有 branch,這里目前只有 main branch

進(jìn)入 main branch,點擊 build,就會正常看到結(jié)果了

上面在 Jenkinsfile 的變量引用上,我們都要用 "${pipelineParams.XXXXX}" 這種形式,當(dāng)參數(shù)變多時,也在不斷重復(fù)寫pipelineParams , 所以我們要繼續(xù)優(yōu)化,將所有的變量放到內(nèi)置的 env 對象當(dāng)中去,這樣就可以直接使用變量名稱了。結(jié)合 pipeline 的 Build, Test,Deploy 步驟,我們再修改一下 shared library(注意 Load Variables stage)內(nèi)容如下:
def call(body) {
// evaluate the body block, and collect configuration into the object
def pipelineParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
echo "${pipelineParams.NAME}"
echo "${pipelineParams.STORY_NAME}"
echo "${pipelineParams.VERSION}"
pipeline {
agent none
stages {
stage('Load Variables') {
steps {
script {
pipelineParams.each {
k, v -> env."${k}" = "${v}"
}
}
}
}
stage('Build') {
steps {
echo "${BUILD_STAGE}"
}
}
stage('Test') {
steps {
echo "${TEST_STAGE}"
}
}
stage('Deploy') {
steps {
echo "${DEPLOY_STAGE}"
}
}
}
}
}
然后在 Jenkinsfile 中添加幾個參數(shù):
@Library('second-shared-pipeline') _
javaProjectMultiBranchAndParamsCommonLibrary {
NAME = 'rgyb rubbish'
STORY_NAME = 'hello world'
VERSION = '1.1.0'
BUILD_STAGE = 'this is build stage'
TEST_STAGE = 'this is test stage'
DEPLOY_STAGE = 'this is deploy stage'
}
再次 build,將會看到如下結(jié)果


相信到這里你已經(jīng)掌握如何自定義 shared libray 以及如何在 Jenkinsfile 中引用 shared library 了
總結(jié)
我們從當(dāng)今服務(wù)形式出發(fā),發(fā)現(xiàn) DevOps CI 的痛點,結(jié)合 Demo,一點點掌握了自定義 shared library,以及實際項目中的規(guī)范,過往的 Jenkins 的內(nèi)容并不多,結(jié)合文章開頭的兩篇文章,玩轉(zhuǎn) Jenkins Pipeline 就已經(jīng)不是問題了,如果你項目中有類似需要優(yōu)化的情況,擼起袖子干吧,做可以幫組內(nèi)成員省力氣的事情,都是加分項哦......
2. Springboot 日志、配置文件、接口數(shù)據(jù)如何脫敏?老鳥們都是這樣玩的!
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點“在看”,關(guān)注公眾號并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。
謝謝支持喲 (*^__^*)

