一、說在前面的話
本文是CI篇的上文,因為上一篇已經(jīng)作了總體設計,就不再贅述,有需要的請看前文。
我們將演示,使用CI工具–jenkins,怎么和CD工具–argocd串聯(lián),重點是在Jenkins該怎么做。準備工作和argocd等相關(guān)事項,在前文已鋪墊ok。
Jenkins,我們是使用k8s來部署的一個master-slave結(jié)構(gòu)的集群。
在開發(fā)環(huán)境,Jenkins和argocd都是部署在同一個k8s集群。
接下來,我們的java應用服務,也都將部署在該K8S里。
二、關(guān)鍵技術(shù)
- jenkinsfile
- global pipeline library
依賴的jenkins插件:
- Workspace Cleanup
三、流水線步驟
流水線的各個步驟是有jenkinsfile來拼裝,每個流水,特別是構(gòu)建Docker鏡像、修改helm yaml、發(fā)送IM消息,都需要封裝到pipeline library里。
在詳細描述每個步驟前,我們需要預定服務發(fā)版的依據(jù),程序版本號,最好是每次打包都升級。但是,在開發(fā)環(huán)境,有時候我們會偷懶,所以就采用版本號+時間戳的方式。允許同一個版本號,提交多次代碼,均觸發(fā)部署。
四、詳細實現(xiàn)
4.1、配置pipeline libraray
本次k8s容器化部署,增加了一個java-k8s.jenkinsfile文件,讓程序的部署可以同時支持兩種部署方式。
下面詳細說一說這幾個pipeline libraray.
4.1.1、tools.groovy – 讀取java程序的版本號
讀取程序的版本號,讀取mvn package后的target目錄下的classes/git.properties文件里的版本號git.build.version。
// 讀取java應用的版本號
def getAppVersion(packagePath) {
def appVersion = sh returnStdout: true,
script: """
grep 'git.build.version' ${packagePath}/classes/git.properties | cut -d'=' -f2 | sed 's/\\r//'
"""
// trim()
return appVersion.trim()
}
那么git.properties是依賴哪個mvn插件呢?
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>4.0.0</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<verbose>true</verbose>
<dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<includeOnlyProperties>
<includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>
<includeOnlyProperty>^git.commit.id.(abbrev|full)$</includeOnlyProperty>
</includeOnlyProperties>
<commitIdGenerationMode>full</commitIdGenerationMode>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
</configuration>
</plugin>
考慮到java工程可以是多模塊的,那就不是簡單的target,而應該是${moduleName}/target了。
在mvn package后,見下圖:
git.properties內(nèi)容詳情參考:
#Generated by Git-Commit-Id-Plugin
#Wed May 17 11:20:13 CST 2023
git.build.time=2023-05-17T11\:20\:13+0800
git.build.version=1.0.7
git.commit.id.abbrev=3dab776
git.commit.id.full=3dab7764afac0e8222a2bef3d78dc8b3175f8caa
4.1.2、docker.groovy – 構(gòu)建鏡像并推送
封裝docker的幾個命令,考慮到變量比較多,如果你不是很明白,對比著示例來看,具體見前文。
package com.xxtech.devops
// 保證jenkins slave節(jié)點上安裝了docker
// 第一個參數(shù)是服務名稱,第二個參數(shù)是端口號,第四個是xxx.jar所在的目錄,都是在docker build的時候需要
// 第三個參數(shù)是docker image的版本號(一般是程序的版本號),第五個參數(shù)是dockerfile所在的位置
//
def buildAndPushImage(appName, port, version, packagePath, dockerfileName, repoDomain = "192.168.5.6:8086") {
def repoProject = "xxx"
// 1、構(gòu)建
sh """
docker build --build-arg APPNAME="${appName}" --build-arg PORT="${port}" --build-arg PACKAGE_PATH="${packagePath}" -f ${dockerfileName} -t ${repoProject}/${appName}:${version} .
"""
// 2、登錄、打標簽、推送
// 這里需要配置jenkins憑證,用戶名和密碼方式。具體配置,在jenkins系列文章另說。
// 只有這樣,在jenkins job控制臺,才不會輸出了密碼字段的內(nèi)容。
withCredentials([usernamePassword(passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME', credentialsId: "e4681123-7da2-4bd5-9bc2-7dd68375c406",)]) {
sh """
docker login ${repoDomain} -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}
docker tag ${repoProject}/${appName}:${version} ${repoDomain}/${repoProject}/${appName}:${version}
docker push ${repoDomain}/${repoProject}/${appName}:${version}
"""
}
// 3、刪除本地鏡像
sh """
docker rmi -f ${repoProject}/${appName}:${version}
docker rmi -f ${repoDomain}/${repoProject}/${appName}:${version}
"""
}
4.1.3、helm.groovy – 觸發(fā)helm部署
有兩種方式,
- 一種是調(diào)用helm命令,helm install至k8s。-- 不是很建議,缺乏界面,而且各應用的配置不相同,維護成本高。
- 另外一種是修改git代碼并提交,利用gitops思想,去觸發(fā)argocd部署。-- 本文是采用這種方式,也是本系列的設計思路。(讓CI的歸jenkins,讓CD的歸argocd,jenkins這里只是一個觸發(fā)動作,當然你是手動的話,你也可以略過本步驟。但是多少有點不完美)
package com.xxtech.devops
// helm yaml所在的git工程是 http://192.168.8.28:9980/root/argocd-helm-yaml.git
// 第一個參數(shù)是git代碼下的一個目錄,第二個參數(shù)的image的tag值
def updateYaml(appName, imageTag, repoUrl = "192.168.8.28:9980/root/argocd-helm-yaml.git") {
// jenkins憑證
def gitlabCredentialsId = "12116269-430c-4921-b63b-18a490f7531c"
// 拉取git代碼
checkout([$class : 'GitSCM',
branches : [[name: '*/master']],
extensions : [],
userRemoteConfigs: [[
credentialsId: "${gitlabCredentialsId}",
url : "http://${repoUrl}"
]]]
)
// 修改values.yaml中的image.tag
withCredentials([usernamePassword(credentialsId: "${gitlabCredentialsId}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
sh """
// 替換image.tag的值,必須和上一次的不一樣,否則后面的git提交會報錯
sed -i 's/tag:.*/tag: ${imageTag}/g' ${appName}/values.yaml
git add ${appName}/values.yaml
// 在git commit前,進行全局配置
git config --global user.name "admin"
git config --global user.email "admin@example.com"
git commit -m "modify image tag to ${imageTag}"
// git push的時候不會讓你再次輸入用戶名和密碼
git push http://$GIT_USERNAME:$GIT_PASSWORD@${repoUrl} HEAD:master
"""
}
}
4.1.4、http.groovy – 發(fā)送Im消息
我們需要把jenkins job的結(jié)果告知工程的相關(guān)人員。這就屬于im的范疇了,實現(xiàn)就是調(diào)用一個http請求,能發(fā)送http請求即可,具體處理是交由消息接收服務去發(fā)送。
有人要問,為什么不直接在jenkinsfile里發(fā),還要多引入一個服務呢?
這里,主要的原因是,我們的需求不是簡單的通過rebot機器人,把發(fā)送消息至企業(yè)微信群里。
而是會給應用打標簽,根據(jù)所屬標簽,而企業(yè)微信會維護標簽下有哪些人。
package com.xxtech.devops
def request(reqType, reqUrl, reqBody, reqFile = '', contentType = "APPLICATION_JSON_UTF8") {
def response = httpRequest httpMode: reqType,
contentType: contentType,
consoleLogResponseBody: true,
ignoreSslErrors: true,
uploadFile: reqFile,
requestBody: reqBody,
url: "${reqUrl}",
timeout: 600
//quiet: true
return response
}
def imNotify(projectName, result, buildEnv, message, branch, buildBy, robotKey = '') {
def reqBody = """ {"projectName": "${projectName}",
"buildResult": "${result}",
"branch": "${branch}",
"buildBy": "${buildBy}",
"env": "${buildEnv}",
"reason": "${message}",
"robotKey": "${robotKey}",
"buildUrl": "${env.BUILD_URL}" } """
def url = "http://192.168.10.47/devops/api/jenkins/notify"
request("POST", url, reqBody)
}
4.2、docker in docker 容器的配置
docker image的構(gòu)建和推送,要求你有docker環(huán)境,而不幸的是,我們無論是jnlp還是已安裝的容器都不具備。
所以,我額外引入了一個容器docker:dind
這里,著重說明下我在使用過程中,遇到的幾個問題。
4.2.1、未分配偽終端
報錯信息是Container docker was terminated (Exit Code: 0, Reason: Completed),導致Jenkins job直接沒跑起來。
4.2.2、未配置volumes
// dind運行,必須配置volumes如下。
volumes: [
hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')
]
4.2.3、dind運行不起來
dind沒有/bin/bash, 且必須增加參數(shù)–privileged
docker run -d --privileged docker:dind
docker exec -it {容器ID} sh
而jenkins containerTemplate中的容器,默認值: --privileged: true,要不然就用不了docker:dind文章來源:http://www.zghlxwxcb.cn/news/detail-549126.html
五、未完待續(xù)
我們把CI篇分為兩篇,本文是先介紹了pipeline library,是Jenkinsfile的一個基礎。
下一篇,我們將詳細說明java-k8s.jenkinsfile要怎么寫。文章來源地址http://www.zghlxwxcb.cn/news/detail-549126.html
到了這里,關(guān)于Devops系列五(CI篇之pipeline libraray)jenkins將gitlab helm yaml和argocd 串聯(lián),自動部署到K8S的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!