先前有介紹過 Jenkins - Pipeline Job 平行處理 ,大致說明了基本用法。 本篇稍為進階一點,透過 JSON 檔案控製 Pipeline Job 平行處理的執行順序。
情境 隨著系統規模越大,模組也會隨之增長,模組之間的相依關係必然會影響建置順序。 範例假設有 8 個模組,相依關係如下:
JSON 自製一個 JSON 檔案,把建置需要的參數及相依順序都定義在裡面,範例如下:
BuildDependency.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [ { "group" : 1 , "name" : "Module A" , "repository" : "https://*******/module-a.git" , "customParameters" : "..." , "..." : "..." } , { "group" : 2 , "name" : "Module B" , "repository" : "https://*******/module-b.git" , "customParameters" : "..." , "..." : "..." } , { "group" : 2 , "name" : "Module C" } , { "group" : 3 , "name" : "Module D" } , { "group" : 3 , "name" : "Module E" } , { "group" : 3 , "name" : "Module F" } , { "group" : 4 , "name" : "Application" } , { "group" : 4 , "name" : "Website" } ]
透過 group
表示建置的相依順序,同一個 group
可以平行處理,當該 group
的模組全部建置完成後,才能往下建置下一個 group
。
名稱跟需要的參數都可依需求自行定義。
Groovy Script 排序 透過 JsonSlurperClassic
讀取 JSON 內容,然後放到另一個以 group
為 key 的 HashMap,再以 sort()
排序 key 值。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import groovy.json.JsonSlurperClassicdef getJsonObject() { def jsonPath = "${env.JENKINS_HOME}\\properties\\BuildDependency.json" def jsonSlurper = new JsonSlurperClassic() def jsonObject = jsonSlurper.parseText(new File(jsonPath).getText("UTF-8" )) return jsonObject } def jsonObject = getJsonObject()def buildDependency = [:]jsonObject.each({ def group = it.group if (!buildDependency[group]){ buildDependency[group] = [] } buildDependency[group].push(it) }) buildDependency = buildDependency.sort() buildDependency.each({ group, val -> echo "group = ${group}" for (module in val) { echo "module.name = ${module.name}" } })
輸出結果可以看到 group
已確實排序,如預期:
建立平行處理工作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import groovy.json.JsonSlurperClassicdef getJsonObject() { def jsonPath = "${env.JENKINS_HOME}\\properties\\BuildDependency.json" def jsonSlurper = new JsonSlurperClassic() def jsonObject = jsonSlurper.parseText(new File(jsonPath).getText("UTF-8" )) return jsonObject } def jsonObject = getJsonObject()def buildDependency = [:]jsonObject.each({ def group = it.group if (!buildDependency[group]){ buildDependency[group] = [] } buildDependency[group].push(it) }) buildDependency = buildDependency.sort() buildDependency.each({ group, val -> def jobs = [:] for (module in val) { jobs[module.name] = { stage(module.name) { } } } parallel(jobs) })
NotSerializableException 上面範例執行後發生了 NotSerializableException
,錯誤訊息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 an exception which occurred: in field locals in field parent in field caller in field parent in field parent in field caller in field parent in field parent in field parent in field parent in field parent in field capture in field def in field closures in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@18f5ae0 Caused: java.io.NotSerializableException: java.util.TreeMap$Entry at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860) at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65) at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56) at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.java:50) at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.java:179) at java.io.ObjectOutputStream.writeObject(Unknown Source) at java.util.HashMap.internalWriteEntries(Unknown Source) at java.util.HashMap.writeObject(Unknown Source) at sun.reflect.GeneratedMethodAccessor306.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) ...
因為 buildDependency 經過 sort()
處理後,已由原本的 LinkedHashMap
型態,轉變為 TreeMap
型態,而 TreeMap
無法被序列化。 前篇 Jenkins - Pipeline Job 平行處理發生 JSON 例外 有說明,Pipeline Job 平行處理不能使用到無法被序列化的物件。
TreeMap to ArrayList 解決方是只要把 TreeMap
轉換成 ArrayList
即可,建立一個簡短的轉換方法:
1 def entrySet(m) { m.collect { k, v -> [ k, v ] } }
套用範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import groovy.json.JsonSlurperClassicdef entrySet(m) { m.collect { k, v -> [ k, v ] } }def getJsonObject() { def jsonPath = "${env.JENKINS_HOME}\\properties\\BuildDependency.json" def jsonSlurper = new JsonSlurperClassic() def jsonObject = jsonSlurper.parseText(new File(jsonPath).getText("UTF-8" )) return jsonObject } def jsonObject = getJsonObject()def buildDependency = [:]jsonObject.each({ def group = it.group if (!buildDependency[group]){ buildDependency[group] = [] } buildDependency[group].push(it) }) buildDependency = entrySet(buildDependency.sort()) buildDependency.each({ item -> def jobs = [:] for (module in item[1 ]) { jobs[module.name] = { stage(module.name) { } } } parallel(jobs) })
用 Blue Ocean 看執行結果如下:
第一個 Parallel 只有一個工作,顯示的圖形變向右延伸,看起來怪怪的。
判斷是否需要平行處理 透過 size()
取得 jobs 集合的數量,如果只有一個 Job 就使用 node()
執行工作,反之使用 parallel()
執行工作。
完整範例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import groovy.json.JsonSlurperClassicdef entrySet(m) { m.collect {k, v -> [k, v]} }def getJsonObject() { def jsonPath = "${env.JENKINS_HOME}\\properties\\BuildDependency.json" def jsonSlurper = new JsonSlurperClassic() def jsonObject = jsonSlurper.parseText(new File(jsonPath).getText("UTF-8" )) return jsonObject } def jsonObject = getJsonObject()def buildDependency = [:]jsonObject.each({ def group = it.group if (!buildDependency[group]){ buildDependency[group] = [] } buildDependency[group].push(it) }) buildDependency = entrySet(buildDependency.sort()) buildDependency.each({ item -> def jobs = [:] def moduleName = "" for (module in item[1 ]) { moduleName = module.name jobs[moduleName] = { stage(moduleName) { } } } if (jobs.size()==1 ){ node(jobs[moduleName]) } else { parallel(jobs) } })
執行結果
相關文章 Jenkins - Pipeline Job 平行處理 Jenkins - Groovy 從 JSON 檔案讀取變數 Jenkins - Pipeline Job 平行處理發生 JSON 例外