jenkins2 pipeline高級


jenkins2 pipeline里groovy的高級用法。翻譯自:https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md

文章來自:http://www.ciandcd.com
文中的代碼來自可以從github下載: https://github.com/ciandcd

 

1. 在groovy里使用函數,條件控制,循環,異常捕獲等

node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def v = version()
if (v) {
echo "Building version ${v}"
}
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}
def version() {
def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
matcher ? matcher[0][1] : null
}

上面的代碼中:

def用來定義groovy變量或函數;

=~表示正則表達式的匹配;

master[0][1]表示取出匹配結果中第一個匹配項中的第一個group的值;

readFile為groovy的函數用來從workspace里讀取文件返回文件的內容,同時還可以使用writeFile來保存內容到文件,fileExists用來判斷文件是否存在;

 

注意 groovy腳本框下面的checkbox:use groovy sandbox,如果選中的話,groovy 腳本會在沙盒里運行有限的功能, 否則如果不是管理員運行的話會報錯或者仍然運行就的腳本,需要管理員來approve groovy script。

如果遇到RejectedAccessException權限問題,需要jenkins管理員在Manage Jenkins » In-process Script Approval中approve 權限staticMethod org.codehaus.groovy.runtime.ScriptBytecodeAdapter findRegex java.lang.Object java.lang.Object。

 

2. 本地變量的序列化

如下的代碼在運行時可能會遇到錯誤java.io.NotSerializableException: java.util.regex.Matcher,錯誤的原因是Matcher是不可序列化的類型。

node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
if (matcher) {
echo "Building version ${matcher[0][1]}"
}
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}

pipeline job為了支持能夠在jenkins重啟后恢復繼續運行,jenkins在后台定期地將job的運行狀態保存到硬盤。保存的動作一般在每個step結束后,或者在一些step的中間,例如sh step的中間。
jenkins保存的job的狀態,包括整個控制流程,例如局部變量,循環所在的位置,等等。正因為如此,groovy里的任何變量必須是number,string或可序列化的類型,其他的例如網絡連接等是不能夠序列化的。
如果你臨時地使用不可序列化的類型,則需要在使用完馬上釋放。如果局部變量在函數中,函數調用結束的時候局部變量也會被自動釋放。我們也可以顯示地釋放局部變量。 如下

node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
if (matcher) {
echo "Building version ${matcher[0][1]}"
}
matcher = null
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}

然而最安全的方法是將不可序列化的語句隔離到函數中,且在函數的前面增加屬性@NonCPS。通過這種方法pipeline將識別此函數為native且不保存對應的局部變量。另外使用了@NoCPS的函數中不能夠調用其他的pipeline steps,例如必須將readFile放到函數外面:
node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def v = version(readFile('pom.xml'))
if (v) {
echo "Building version ${v}"
}
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}
@NonCPS
def version(text) {
def matcher = text =~ '<version>(.+)</version>'
matcher ? matcher[0][1] : null
}

上面的加了@NoCPS的version函數將被正常的groovy運行時執行,所以任何的局部變量都是允許的。

 

3. 創建多線程

pipeline能夠使用parallel來同時執行多個任務。 parallel的調用需要傳入map類型作為參數,map的key為名字,value為要執行的groovy腳本。
為了測試parallel的運行,可以安裝parallel test executor插件。此插件可以將運行緩慢的測試分割splitTests。

用下面的腳本新建pipeline job:

node('remote') {
git url: 'https://github.com/jenkinsci/parallel-test-executor-plugin-sample.git'
archive 'pom.xml, src/'
}
def splits = splitTests([$class: 'CountDrivenParallelism', size: 2])
def branches = [:]
for (int i = 0; i < splits.size(); i++) {
def exclusions = splits.get(i);
branches["split${i}"] = {
node('remote') {
sh 'rm -rf *'
unarchive mapping: ['pom.xml' : '.', 'src/' : '.']
writeFile file: 'exclusions.txt', text: exclusions.join("\n")
sh "${tool 'M3'}/bin/mvn -B -Dmaven.test.failure.ignore test"
step([$class: 'JUnitResultArchiver', testResults: 'target/surefire-reports/*.xml'])
}
}
}
parallel branches

 

如果遇到RejectedAccessException錯誤,需要管理員approve權限staticMethod org.codehaus.groovy.runtime.ScriptBytecodeAdapter compareLessThan java.lang.Object java.lang.Object。

當第一次運行上面的pipeline job的時候,所有的測試順序執行。當第二次或以后執行的時候,splitTests將會將所有的測試分割為大概等價的兩份,然后兩個task並行運行。如果兩個task運行在不同的slave上,則可以看到job總的時間將會減半。

下面的等價語句用來打包pom.xml和源代碼:
archive 'pom.xml, src/'
step([$class: 'ArtifactArchiver', artifacts: 'pom.xml, src/'])

我們可以看到prallel里的語句使用了node,這意味着並行執行的任務將會在新的node/slave上執行,且使用不同的workspace,為了確保所有的node和workspace使用相同的代碼,所以才有了前面的打包archive和parallel里的解包unarchive。

上面的例子中我們可以看到同一個pipeline job里可以使用多個node,多個node會有不同的workspace,我們需要確保每個workspace的內容都是我們想要的內容。

另一個問題,如果在pipeline中使用env,環境變量的修改會在整個pipeline起作用,如果只修改parallel並行的線程的變量,可以使用withEnv。

在使用了parallel的console log里,並行的log都混在了一起,需要在job的pipeline steps頁面查看按邏輯分割的更情況的log。

 

4. 創建stage

默認地,pipeline的多個job可以並行地運行。使用stage可以限制job里的某些階段的並行數量。新的job用於更高的優先級,舊的job遇到stage的並行限制會直接退出。

並行性的限制有的時候很有用,例如部署到單個server,在同一時間只能有最新的一個job部署。

 

5. 從外部加載groovy腳本

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM