您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“微服務(wù)CI/CD實(shí)踐之GitOps完整設(shè)計(jì)與實(shí)現(xiàn)的方法教程”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“微服務(wù)CI/CD實(shí)踐之GitOps完整設(shè)計(jì)與實(shí)現(xiàn)的方法教程”吧!
單應(yīng)用與環(huán)境
多應(yīng)用與環(huán)境
CI持續(xù)集
成首先準(zhǔn)備一個(gè)代碼庫:https://github.com/DevOpsCICDCourse/microservicescicd/blob/main/microservice-demo-service-master.zip
我們來梳理一下CI流水線的步驟:
由于此次實(shí)現(xiàn)的代碼倉庫類型為單一存儲(chǔ)庫,即一個(gè)存儲(chǔ)庫存放多個(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)。
工作流程是:當(dāng)我在Gitlab提交了代碼,會(huì)通過GitLab webhook 觸發(fā)Jenkins Scheduler 作業(yè), 會(huì)將此次提交代碼所產(chǎn)生的hook data數(shù)據(jù)信息以POST的方式傳給Jenkins Job。此時(shí)Jenkins job可以編寫使用Generic Hook插件獲取此次POST請(qǐng)求傳輸過來的請(qǐng)求體Body信息。是一段JSON數(shù)據(jù), 該job運(yùn)行后編寫Pipeline 解析JSON中的數(shù)據(jù)拿到所變更的服務(wù)模塊信息。最后觸發(fā)對(duì)應(yīng)服務(wù)的CI作業(yè)進(jìn)行構(gòu)建。
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倉庫中。我們的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++) { //下載版本庫文件 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) }
images
GitOps-CD部分
CD-Scheduler作業(yè)
此作業(yè)其實(shí)也是接收GitLab的webhook請(qǐng)求, 與CI-scheduler作業(yè)類似。不同的是這個(gè)CD-scheduler作業(yè)是用來接收環(huán)境倉庫的代碼變更。開啟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)境庫配置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" """ } } } } }
到此,相信大家對(duì)“微服務(wù)CI/CD實(shí)踐之GitOps完整設(shè)計(jì)與實(shí)現(xiàn)的方法教程”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。