CI/CD流程設計及實現

目前我司的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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章