?微服務(wù) CI/CD 實(shí)踐-GitOps 完整設(shè)計(jì)與實(shí)現(xiàn)
感謝這安靜的環(huán)境,沒有它們我是無法完成這篇文章的。
單應(yīng)用與環(huán)境

多應(yīng)用與環(huán)境

CI持續(xù)集成
首先準(zhǔn)備一個(gè)代碼庫(kù):https://github.com/DevOpsCICDCourse/microservicescicd/blob/main/microservice-demo-service-master.zip

我們來梳理一下CI流水線的步驟:

由于此次實(shí)現(xiàn)的代碼倉(cāng)庫(kù)類型為單一存儲(chǔ)庫(kù),即一個(gè)存儲(chǔ)庫(kù)存放多個(gè)服務(wù)模塊代碼,每個(gè)子目錄為一個(gè)服務(wù)模塊。 首先,我們的持續(xù)集成流水線需要能夠正確獲取,當(dāng)前的commit是哪個(gè)服務(wù)的代碼。 確定好服務(wù),然后下載該服務(wù)的代碼,進(jìn)行編譯打包、單元測(cè)試、代碼掃描和構(gòu)建鏡像等步驟。
如何獲取commit的服務(wù)信息?這里我們使用GitLab WebHook功能和Jenkins 的job 構(gòu)建觸發(fā)器對(duì)接來實(shí)現(xiàn)。

CI-Scheduler 作業(yè)
此作業(yè)只需要開啟webhook, 配置觸發(fā)token(唯一性)。生成hookurl:http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=microservicecicd-scheduler-CI


Jenkinsfile
pipeline?{
?agent?any?
?stages{
??stage("GetData"){
???steps{
????script?{
?????echo?"${webHookData}"
?????data?=?readJSON??text:?"${webHookData}"
?????println(data)
?????env.branchName?=?data.ref?-?"refs/heads/"
?????env.commitId?=?data.checkout_sha
?????env.projectId?=?data.project_id
?????commits?=?data["commits"]
?????println("${env.branchName}")
?????println("${env.commitID}")
?????println("${env.projectId}")
?????//env.moduleName?=?"service01"
?????changeServices?=?[]
????????????????????for(commit?in?commits)?{
????????????????????????println(commit.id)
????????????????????????//added
????????????????????????for?(add?in?commit.added)?{
????????????????????????????s?=?add.split("/")?as?List
????????????????????????????if?(s.size()?>?1){
????????????????????????????????if?(changeServices.indexOf(s[0])?==?-1){
????????????????????????????????????changeServices.add(s[0])
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????????//modified
????????????????????????for?(m?in?commit.modified)?{
????????????????????????????s?=?m.split("/")?as?List
????????????????????????????//?println?s
????????????????????????????//?println?s.size()
????????????????????????????//?println?s[0]
????????????????????????????if?(s.size()?>?1){
????????????????????????????????//?println?changeServices.indexOf(s[0])
????????????????????????????????if?(changeServices.indexOf(s[0])?==?-1){
????????????????????????????????????changeServices.add(s[0])
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????????//removed
????????????????????????for?(r?in?commit.removed)?{
????????????????????????????s?=?r.split("/")?as?List
????????????????????????????println?s
????????????????????????????if?(s.size()?>?1){
????????????????????????????????if?(changeServices.indexOf(s[0])?==?-1){
????????????????????????????????????changeServices.add(s[0])
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????}
????????????????????println(changeServices)
????????????????????//currentBuild.description?=?"?Trigger?by??${eventType}?${changeServices}?
????}
???}
??}
??stage('DefineService')?{
????????????steps?{
????????????????script{
????????????????????println(changeServices)
????????????????????//服務(wù)構(gòu)建順序控制
????????????????????services?=?['service02',?'service01']
????????????????????for?(service?in?services){
????????????????????????if?(changeServices.indexOf(service)?!=?-1){
????????????????????????????jobName?=?'microservicecicd-'+service+'-service-CI'
????????????????????????????build?job:?jobName,?wait:?false,??parameters:?[string(name:?'branchName',?value:?"${env.branchName}"?),
???????????????????????????????????????????????????????????????????????????string(name:?'commitId',???value:?"${env.commitId}"?),?
???????????????????????????????????????????????????????????????????????????string(name:?'projectId',??value:?"${env.projectId}"?)]
????????????????????????}
????????????????????}
????????????????}
????????????}
????????}
?}
}
GitLab 配置WebHook
開啟webhook,配置hookurl:http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=microservicecicd-scheduler-CI

CI流水線-CI作業(yè)
每個(gè)微服務(wù)創(chuàng)建一個(gè)CI作業(yè),具有三個(gè)字符串參數(shù):分支名稱、commitID、項(xiàng)目ID。

Jenkinsfile
String?branchName?=?"${env.branchName}"
String?moduleName?=?"${JOB_NAME}".split("/")[1].split("-")[1]
String?srcUrl?=?"http://gitlab.idevops.site/microservicecicd/microservicecicd-demo-service.git"
String?commitId?=?"${env.commitId}"
String?projectId?=?"${env.projectId}"
pipeline?{
????agent?{?node?{?label?"build"?}?}
????stages?{
????????stage('GetCode')?{
????????????steps?{
????????????????script?{
????????????????????checkout([$class:?'GitSCM',?
????????????????????????????branches:?[[name:?"${branchName}"]],?
????????????????????????????doGenerateSubmoduleConfigurations:?false,
????????????????????????????extensions:?[[$class:?'SparseCheckoutPaths',?
????????????????????????????????????????sparseCheckoutPaths:?[[path:?"${moduleName}"],[path:?'Dockerfile']]]],?
????????????????????????????submoduleCfg:?[],?
????????????????????????????userRemoteConfigs:?[[credentialsId:?'gitlab-admin-user',
????????????????????????????????????????????????url:?"${srcUrl}"]]])
????????????????}
????????????????
????????????}
????????}
????????stage("Build&Test"){
????????????steps{
????????????????script{
????????????????????echo?"Build..........."
????????????????????sh?"""
????????????????????cd?${moduleName}?
????????????????????mvn?clean?package
????????????????????"""
????????????????}
????????????}
????????????post?{
????????????????always?{
????????????????????junit?"${moduleName}/target/surefire-reports/*.xml"
????????????????}
????????????}
????????}
????????stage("SonarScan"){
????????????steps{
????????????????script{
????????????????????def?sonarDate?=?sh?returnStdout:?true,?script:?'date??+%Y%m%d%H%M%S'
????????????????????sonarDate?=?sonarDate?-?"\n"
????????????????????withCredentials([string(credentialsId:?'sonar-admin-user',?variable:?'sonartoken'),
????????????????????????????????????string(credentialsId:?'gitlab-user-token',?variable:?'gitlabtoken')])?{
????????????????????????//?some?block
????????????????????????sh?"""
????????????????????????cd?${moduleName}?
????????????????????????sonar-scanner?\
????????????????????????-Dsonar.projectKey=${JOB_NAME}?\
????????????????????????-Dsonar.projectName=${JOB_NAME}?\
????????????????????????-Dsonar.projectVersion=${sonarDate}?\
????????????????????????-Dsonar.ws.timeout=30?\
????????????????????????-Dsonar.projectDescription="xxxxxxx"?\
????????????????????????-Dsonar.links.homepage=http://www.baidu.com?\
????????????????????????-Dsonar.sources=src?\
????????????????????????-Dsonar.sourceEncoding=UTF-8?\
????????????????????????-Dsonar.java.binaries=target/classes?\
????????????????????????-Dsonar.java.test.binaries=target/test-classes?\
????????????????????????-Dsonar.java.surefire.report=target/surefire-reports?\
????????????????????????-Dsonar.host.url="http://sonar.idevops.site"?\
????????????????????????-Dsonar.login=${sonartoken}?\
????????????????????????-Dsonar.gitlab.commit_sha=${commitId}?\
????????????????????????-Dsonar.gitlab.ref_name=${branchName}?\
????????????????????????-Dsonar.gitlab.project_id=${projectId}?\
????????????????????????-Dsonar.dynamicAnalysis=reuseReports?\
????????????????????????-Dsonar.gitlab.failure_notification_mode=commit-status?\
????????????????????????-Dsonar.gitlab.url=http://gitlab.idevops.site?\
????????????????????????-Dsonar.gitlab.user_token=${gitlabtoken}?\
????????????????????????-Dsonar.gitlab.api_version=v4
????????????????????????"""
????????????????????}
?
????????????????}
????????????}
????????}
????????stage("BuildImage"){
????????????steps{
????????????????script{
?????????????????????withCredentials([usernamePassword(credentialsId:?'aliyun-registry-admin',?passwordVariable:?'password',?usernameVariable:?'username')])?{
????????????????
?????????????????????????env.nowDate?=?sh??returnStdout:?true,?script:?'date??+%Y%m%d%H%M%S'
?????????????????????????env.nowDate?=?env.nowDate?-?"\n"
?????????????????????????env.releaseVersion?=?"${env.branchName}"
?????????????????????????env.imageTag?=?"${releaseVersion}-${nowDate}-${commitId}"
?????????????????????????env.dockerImage?=?"registry.cn-beijing.aliyuncs.com/microservicecicd/microservicecicd-${moduleName}-service:${env.imageTag}"
?????????????????????????env.jarName?=?"${moduleName}-${branchName}-${commitId}"
?????????????????????????sh?"""
?????????????????????????????docker?login?-u?${username}?-p?${password}??registry.cn-beijing.aliyuncs.com
?????????????????????????????cd?${moduleName}?&&?docker?build?-t?${dockerImage}?-f?../Dockerfile?--build-arg?SERVICE_NAME=${jarName}?.
?????????????????????????????sleep?1
?????????????????????????????docker?push?${dockerImage}
?????????????????????????????sleep?1
?????????????????????????????docker?rmi?${dockerImage}
??????????????????????????"""
????????????????????}
????????????????}
????????????}
????????}
????????
????}
}
GitOps-CI擴(kuò)展部分
在原始CI作業(yè)的步驟基礎(chǔ)上,增加了一個(gè)更新環(huán)境的步驟。GitOps實(shí)踐會(huì)將當(dāng)前的基礎(chǔ)環(huán)境部署文件存放到一個(gè)Git倉(cāng)庫(kù)中。我們的CI作業(yè)在完成鏡像上傳后,同時(shí)更新環(huán)境部署文件中的鏡像標(biāo)簽信息。(所以我們需要先獲取該環(huán)境文件并更新上傳)

stage("PushFile"){
??????????//?when?{
??????????//???expression?{?"${env.branchName}".contains("RELEASE-")?}
??????????//?}
??????????steps{
????????????script{
??????????????if?("${env.branchName}".contains("RELEASE-")){
????????????????println("branchName?=?branchName")
????????????????env.branchName?=?"master"
??????????????}?else?{
????????????????env.branchName?=?"feature"
??????????????}
????????????????for?(i?=?0;?i?3;?i++)?{
????????????????????//下載版本庫(kù)文件?
????????????????????response?=?GetRepoFile(40,"${moduleName}%2fvalues.yaml",?"${env.branchName}")
????????????????????//println(response)
????????????????????
????????????????????//替換文件中內(nèi)容
????????????????????yamlData?=?readYaml?text:?"""${response}"""
????????????????????println(yamlData.image.version)
????????????????????println(yamlData.image.commit)
????????????????????yamlData.image.version?=?"${releaseVersion}-${env.nowDate}"
????????????????????yamlData.image.commit??=?"${commitId}"
????????????????????println(yamlData.toString())
????????????????????sh?"rm?-fr?test.yaml"
????????????????????writeYaml?charset:?'UTF-8',?data:?yamlData,?file:?'test.yaml'
????????????????????newYaml?=?sh?returnStdout:?true,?script:?'cat?test.yaml'
????????????????????
????????????????????println(newYaml)
????????????????????//更新gitlab文件內(nèi)容
????????????????????base64Content?=?newYaml.bytes.encodeBase64().toString()
????????????????????//?會(huì)有并行問題,同時(shí)更新報(bào)錯(cuò)
????????????????????try?{
??????????????????????UpdateRepoFile(40,"${moduleName}%2fvalues.yaml",base64Content,?"${env.branchName}")
??????????????????????break;
????????????????????}?catch(e){
??????????????????????sh?"sleep?2"
??????????????????????continue;
????????????????????}
????????????????}
????????????}
??????????}
????????}
????????
?//封裝HTTP請(qǐng)求
def?HttpReq(reqType,reqUrl,reqBody){
????def?gitServer?=?"http://gitlab.idevops.site/api/v4"
????withCredentials([string(credentialsId:?'gitlab-token',?variable:?'gitlabToken')])?{
??????result?=?httpRequest?customHeaders:?[[maskValue:?true,?name:?'PRIVATE-TOKEN',?value:?"${gitlabToken}"]],?
????????????????httpMode:?reqType,?
????????????????contentType:?"APPLICATION_JSON",
????????????????consoleLogResponseBody:?true,
????????????????ignoreSslErrors:?true,?
????????????????requestBody:?reqBody,
????????????????url:?"${gitServer}/${reqUrl}"
????????????????//quiet:?true
????}
????return?result
}
//獲取文件內(nèi)容
def?GetRepoFile(projectId,filePath,branchName){
????apiUrl?=?"projects/${projectId}/repository/files/${filePath}/raw?ref=${branchName}"
????response?=?HttpReq('GET',apiUrl,'')
????return?response.content
}
//更新文件內(nèi)容
def?UpdateRepoFile(projectId,filePath,fileContent,?branchName){
????apiUrl?=?"projects/${projectId}/repository/files/${filePath}"
????reqBody?=?"""{"branch":?"${branchName}","encoding":"base64",?"content":?"${fileContent}",?"commit_message":?"update?a?new?file"}"""
????response?=?HttpReq('PUT',apiUrl,reqBody)
????println(response)
}

GitOps-CD部分

CD-Scheduler作業(yè)
此作業(yè)其實(shí)也是接收GitLab的webhook請(qǐng)求, 與CI-scheduler作業(yè)類似。不同的是這個(gè)CD-scheduler作業(yè)是用來接收環(huán)境倉(cāng)庫(kù)的代碼變更。開啟webhook, 配置觸發(fā)token。生成hookurl:http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=microservicecicd-scheduler-CD


Jenkinsfile
pipeline?{
????agent?any
????stages?{
????????stage('GetCommitService')?{
????????????steps?{
????????????????script{
????????????????????echo?'Hello?World'
????????????????????echo?"${WebHookData}"
????????????????????
????????????????????//?Git?Info
????????????????????webhookdata?=?readJSON?text:?"""${WebHookData}"""
????????????????????eventType?=?webhookdata["object_kind"]
????????????????????commits?=?webhookdata["commits"]
????????????????????branchName?=?webhookdata["ref"]?-?"refs/heads/"
????????????????????projectID?=?webhookdata["project_id"]
????????????????????commitID?=?webhookdata["checkout_sha"]
????????????????????changeServices?=?[]
????????????????????for(commit?in?commits)?{
????????????????????????println(commit.id)
????????????????????????//added
????????????????????????for?(add?in?commit.added)?{
????????????????????????????s?=?add.split("/")?as?List
????????????????????????????if?(s.size()?>?1){
????????????????????????????????if?(changeServices.indexOf(s[0])?==?-1){
????????????????????????????????????changeServices.add(s[0])
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????????//modified
????????????????????????for?(m?in?commit.modified)?{
????????????????????????????s?=?m.split("/")?as?List
????????????????????????????//?println?s
????????????????????????????//?println?s.size()
????????????????????????????//?println?s[0]
????????????????????????????if?(s.size()?>?1){
????????????????????????????????//?println?changeServices.indexOf(s[0])
????????????????????????????????if?(changeServices.indexOf(s[0])?==?-1){
????????????????????????????????????changeServices.add(s[0])
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????????//removed
????????????????????????for?(r?in?commit.removed)?{
????????????????????????????s?=?r.split("/")?as?List
????????????????????????????println?s
????????????????????????????if?(s.size()?>?1){
????????????????????????????????if?(changeServices.indexOf(s[0])?==?-1){
????????????????????????????????????changeServices.add(s[0])
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????}
????????????????????println(changeServices)
????????????????????currentBuild.description?=?"?Trigger?by??${eventType}?${changeServices}?"
????????????????}
????????????}
????????}
????????stage('DefineService')?{
????????????steps?{
????????????????script{
????????????????????println(changeServices)
????????????????????//服務(wù)構(gòu)建順序控制
????????????????????services?=?['service02',?'service01']
????????????????????for?(service?in?services){
????????????????????????if?(changeServices.indexOf(service)?!=?-1){
????????????????????????????jobName?=?'microservicecicd-'+service+'-service-CD'
????????????????????????????build?job:?jobName,?wait:?false,??parameters:?[string(name:?'branchName',?value:?"${branchName}"?)]
????????????????????????}
????????????????????}
????????????????}
????????????}
????????}
????}
}
環(huán)境庫(kù)配置webhook
開啟webhook,配置hookurl:http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=microservicecicd-scheduler-CD

CD流水線-CD作業(yè)

Jenkinsfile
String?serviceName?="${JOB_NAME}".split("-")[1]
String?nameSpace?=?"${JOB_NAME}".split("-")[0].split("/")[-1]
//pipeline
pipeline{
????agent?{?node?{?label?"k8s"}}
????
????stages{
???????stage("GetCode"){
????????????steps{
????????????????script{
????????????????????println("${branchName}")
????????????????????println("${env.branchName}".contains("RELEASE-"))
????????????????????println?"獲取代碼"
????????????????????checkout([$class:?'GitSCM',?branches:?[[name:?"${env.branchName}"]],?
??????????????????????????????????????doGenerateSubmoduleConfigurations:?false,?
??????????????????????????????????????extensions:?[[$class:?'SparseCheckoutPaths',?
????????????????????????????????????????????????????sparseCheckoutPaths:?[[path:?"${serviceName}"]]]],?
??????????????????????????????????????submoduleCfg:?[],?
??????????????????????????????????????userRemoteConfigs:?[[credentialsId:?'gitlab-admin-user',?url:?"http://gitlab.idevops.site/microservicecicd/microservicecicd-env.git"]]])
????????????????}
????????????}
????????}
????????stage("HelmDeploy"){
????????????steps{
????????????????script{
??????????????????sh?"""
??????????????????????kubectl?create?ns?"${nameSpace}-uat"??||?echo?false
??????????????????????helm?install?"${serviceName}"?--namespace?"${nameSpace}-uat"?./"${serviceName}"?||??helm?upgrade?"${serviceName}"?--namespace?"${nameSpace}-uat"?./"${serviceName}"
??????????????????????helm?list?--namespace?"${nameSpace}-uat"
??????????????????????helm?history?"${serviceName}"?--namespace?"${nameSpace}-uat"
??????????????????"""
????????????????}
????????????}
????????}
????}
}

優(yōu)點(diǎn)知識(shí)春節(jié)課程特惠活動(dòng)?

?點(diǎn)擊屏末?|?閱讀原文?|?即刻學(xué)習(xí)