目前我司的CICD的架構基本如圖:
其實該架構是一個很常見的架構,但是我在這裏主要考慮瞭如何最大化利用該架構 實現CICD的便捷使用以及後續如何快速集成到運維平臺。
思考的問題:
1.因考慮到CICD的可靠性、安全性,需要將pipeline腳本和業務代碼分離。
2.如何快速的批量生成項目
3.在使用pipeline scm的時候如何獲取業務倉庫的cpmmit-id(CI可以直接獲取到,但CD 不拷貝業務代碼的情況下如何獲取? 目前開發測試環境使用commit作爲版本號 線上預發用tag作爲版本號)
4.發佈到kubernetes平臺的時候 一般都是k8s去harbor倉庫自動拉取鏡像,如果沒有該鏡像那麼對於開發測試是無法感知到的。
解決:
目前我司用一個gitlab倉庫來管理所有的pipeline腳本以及kubernetes的yml文件,jenkins使用pipeline SCM部署,這個時候有個問題就是業務代碼如何克隆到該項目的workspace下進行編譯構建,這個時候我們需要使用pipeline的dir函數去生成一個目錄將業務代碼clone到該目錄下進行構建,例如java項目:
dir('project') {
echo "cloning project from $projecturl with branch: $branch"
final gitinfo = git([branch: "$branch", credentialsId: "$credentialsId", url: "$projecturl"])
echo "gitinfo: $gitinfo"
version = "${gitinfo.GIT_COMMIT}".substring(0, 6)
echo "version:$version"
imageName = "$imageName:$version"
}
stage('compile') {
docker.image('maven:3-alpine').withRun('-v /data/.m2:/root/.m2') {
dir('project') {
echo "mvn clean package module $modulename"
withAnt(jdk: 'jdk8') {
sh "java -version"
sh "which java"
sh "which mvn"
sh "mvn -version"
echo "--------------------------------"
sh "mvn clean package -pl $modulename -Dautoconfig.interactive=false -U -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -am"
}
}
}
}
stage('build and push') {
try {
echo "build docker images and push to custom Registry"
sh "docker version"
docker.withRegistry('http://10.30.30.22:9080', '88a0c6e5-128b-49fb-89c4-7cbe7c8a7593') {
dir('build') {
def serviceImage = docker.build("$imageName", "-f Dockerfile --build-arg app=$modulename .")
/* Push the container to the custom Registry */
serviceImage.push()
}
}
} finally {
dir('build') {
/* Clean up */
deleteDir()
}
}
}
2.如何快速生成項目
正常情況下 在生成項目的時候我們需要新建項目-配置SCM-選擇分支-選擇jenkinsfile文件,但這樣太繁瑣了 並且如果使用grovvy去批量生成項目的話需要更改的配置會非常多容易出錯。
所以我在這裏的設計是所有的腳本都依賴於project name,通過jenkins自帶的JOB_NAME變量去區分不同的項目,並且所有項目的入口都是主的jenkinsfile,通過不同的JOB_NAME路由到不同項目的jenkinsfile。
如圖該腳本路徑永遠是不會變的。
這裏我們只需要更改所屬分支就可以了
然後我們可以通過jenkins的腳本命令行-grovvy腳本批量生成項目
腳本內容如下:
def hi = Hudson.instance
def allView = hi.getView("all")
def devNestedView = hi.getView("dev");
def buildNestedView = devNestedView.getView("構建");
def javaListView = buildNestedView.getView("java");
println("====================source items======================")
for (item in javaListView.getItems()) {
println("OUTPUT: " + item.getName())
}
println("======================================================")
println("====================target items======================")
def targetTestNestedView = hi.getView("test");
def targetBuildNestedView = targetTestNestedView.getView("構建");
def targetJavaListView = targetBuildNestedView.getView("java");
//拷貝一個視圖下的JOB
for (item in javaListView.getItems()) {
//create the new project name
newName = item.getName().replace("ci_dev", "ci_test")
// copy the job, disable and save it
def job
try {
//因爲第一次導入後報錯,所以添加了try-catch 跳過已存在的job
job = Hudson.instance.copy(item, newName)
} catch (IllegalArgumentException e) {
println("WARN: $newName job is exists")
def newItem = allView.getItem(newName)
if (!targetJavaListView.contains(newItem)) {
//add job to view
targetJavaListView.add(newItem)
}
continue
} catch (Exception e) {
println("ERROR: " + item.getName() + " copy failed")
continue
}
job.disabled = true
job.save()
//add job to view
targetJavaListView.add(job)
println(" INFO: $item.name copied as $newName")
}
for (item in targetJavaListView.getItems()) {
println("OUTPUT: " + item.getName())
}
println("==================================================");
3.在使用pipeline scm的時候如何獲取業務倉庫的cpmmit-id(CI可以直接獲取到,但CD 不拷貝業務代碼的情況下如何獲取? 目前開發測試環境使用commit作爲版本號 線上預發用tag作爲版本號)
在這種情況下現有的git插件只能獲取到不同倉庫的tag或者branch 但是不能獲取到commit id,所以這裏我在Active Choices Parameter插件使用了grovvy script的形式去獲取git裏的commit,效果:
腳本內容:
def command = "sh /root/getTags.sh development/bi-tools-service"
def proc = command.execute()
proc.waitFor()
def lw = "${proc.in.text}"
def lww= Arrays.asList(lw.split(","))
return lww
getTags.sh的腳本內容大致就是curl的形式獲取gitlab的commit的列表,我這裏因爲是docker 所以直接取獲取了harbor的api接口的列表。
4.發佈到kubernetes平臺的時候 一般都是k8s去harbor倉庫自動拉取鏡像,如果沒有該鏡像那麼對於開發測試是無法感知到的。
在pipeline腳本先檢測該鏡像是否存在:
stage("check images") {
sh "sh scripts/cd/getImage.sh ${environment}/${JOB_PATH}:${TAGId}"
}
#!/bin/bash
USER="admin"
PASS="Harbor12345"
HURL="http://10.30.30.22:9080"
MTAG=`echo $1 | awk -F ":" '{print $1}'`
checkTag=`echo $1 | awk -F ":" '{print $2}'`
ttoken=$(curl -iksL -X GET -u $USER:$PASS $HURL/service/token?account=${USER}\&service=harbor-registry\&scope=repository:${MTAG}:pull|grep "token" |awk -F '"' '{print $4}')
#echo $ttoken
flag=0
tlist=$(curl -ksL -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $ttoken" ${HURL}/v2/${MTAG}/tags/list|awk -F '[' '{print $2}'|awk -F ']' '{print $1}'|sed 's/"//g')
tlist1=$(curl -ksL -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $ttoken" ${HURL}/v2/${MTAG}/tags/list)
tlist2=`echo $tlist1|jq '.errors'`
if [[ $tlist2 == null ]]; then
getTag=`echo $tlist1 | jq ".tags"|cut -d '[' -f 1 | cut -d ']' -f 1 | sed 's/[[:space:]]//g' | grep -v "^$" | sed -e 's/\"//g'|tr ',' '\n'`
for i in $getTag
do
if [[ $checkTag == $i ]]; then
echo "鏡像匹配成功!"
exit 0
else
flag=1
fi
done
if [[ flag==1 ]];then
echo "無法找到對應鏡像:$MTAG:$checkTag"
exit 1
fi
else
echo "無法找到對應鏡像:$MTAG:$checkTag"
exit 1
# echo $tlist|sed 's/,/\n/g'
fi