微服务CI/CD实践-GitOps完整设计与实现

人工智能 2022-06-19 08:06www.robotxin.com人工智能专业

单应用与环境

多应用与环境

CI持续集成

,准备一个代码库

https://github./DevOpsCICDCourse/microservicescicd/blob/main/microservice-demo-service-master.zip

我们来梳理一下CI流水线的步骤

由于此次实现的代码仓库类型为单一存储库,即一个存储库存放多个服务模块代码,每个子目录为一个服务模块。 ,我们的持续集成流水线需要能够正确获取,当前的mit是哪个服务的代码。 确定好服务,然后下载该服务的代码,进行编译打包、单元测试、代码扫描和构建镜像等步骤。

如何获取mit的服务信息?这里我们使用GitLab WebHook功能和Jenkins 的job 构建触发器对接来实现。

工作流程是当我在Gitlab提交了代码,会通过GitLab ebhook 触发Jenkins Scheduler 作业, 会将此次提交代码所产生的hook data数据信息以POST的方式传给Jenkins Job。此时Jenkins job可以编写使用Generic Hook插件获取此次POST请求传输过来的请求体Body信息。是一段JSON数据, 该job运行后编写Pipeline 解析JSON中的数据拿到所变更的服务模块信息。触发对应服务的CI作业进行构建。

CI-Scheduler 作业

此作业只需要开启ebhook, 配置触发token(唯一性)。生成hookurlhttp://jenkins.idevops.site/generic-ebhook-trigger/invoke?token=microservicecicd-scheduler-CI

Jenkinsfile pipeline {  agent any    stages{    stage("GetData"){    steps{     script {      echo "${ebHookData}"       data = readJSON  text: "${ebHookData}"       println(data)       env.branchName = data.ref - "refs/heads/"      env.mitId = data.checkout_sha      env.projectId = data.project_id      mits = data["mits"]       println("${env.branchName}")      println("${env.mitID}")      println("${env.projectId}")       //env.moduleName = "service01"      changeServices = []                     for(mit in mits) {                         println(mit.id)                          //added                         for (add in mit.added) {                             s = add.split("/") as List                             if (s.size() > 1){                                 if (changeServices.indexOf(s[0]) == -1){                                     changeServices.add(s[0])                                 }                             }                         }                          //modified                         for (m in mit.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 mit.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)                     //服务构建顺序控制                     services = ['service02', 'service01']                     for (service in services){                         if (changeServices.indexOf(service) != -1){                             jobName = 'microservicecicd-'+service+'-service-CI'                             build job: jobName, ait: false,  parameters: [string(name: 'branchName', value: "${env.branchName}" ),                                                                            string(name: 'mitId',   value: "${env.mitId}" ),                                                                             string(name: 'projectId',  value: "${env.projectId}" )]                         }                     }                 }             }         }  } }  GitLab 配置WebHook

开启ebhook,配置hookurlhttp://jenkins.idevops.site/generic-ebhook-trigger/invoke?token=microservicecicd-scheduler-CI

CI流水线-CI作业

每个微服务创建一个CI作业,具有三个字符串参数分支名称、mitID、项目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 mitId = "${env.mitId}" 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 {                 alays {                     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 - " "                      ithCredentials([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.s.timeout=30                          -Dsonar.projectDescription="xxxxxxx"                          -Dsonar.links.homepage=http://.baidu.                          -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.mit_sha=${mitId}                          -Dsonar.gitlab.ref_name=${branchName}                          -Dsonar.gitlab.project_id=${projectId}                          -Dsonar.dynamicAnalysis=reuseReports                          -Dsonar.gitlab.failure_notification_mode=mit-status                          -Dsonar.gitlab.url=http://gitlab.idevops.site                          -Dsonar.gitlab.user_token=${gitlabtoken}                          -Dsonar.gitlab.api_version=v4                          """                      }                   }             }         }          stage("BuildImage"){             steps{                 script{                       ithCredentials([usernamePassord(credentialsId: 'aliyun-registry-admin', passordVariable: 'passord', usernameVariable: 'username')]) {                                           env.noDate = sh  returnStdout: true, script: 'date  +%Y%m%d%H%M%S'                          env.noDate = env.noDate - " "                           env.releaseVersion = "${env.branchName}"                          env.imageTag = "${releaseVersion}-${noDate}-${mitId}"                          env.dockerImage = "registry.-beijing.aliyuncs./microservicecicd/microservicecicd-${moduleName}-service:${env.imageTag}"                          env.jarName = "${moduleName}-${branchName}-${mitId}"                          sh """                              docker login -u ${username} -p ${passord}  registry.-beijing.aliyuncs.                              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扩展部分

在原始CI作业的步骤基础上,增加了一个更新环境的步骤。GitOps实践会将当前的基础环境部署文件存放到一个Git仓库中。我们的CI作业在完成镜像上传后,更新环境部署文件中的镜像标签信息。(所以我们需要先获取该环境文件并更新上传)

stage("PushFile"){           // hen {           //   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)                                          //替换文件中内容                     yamlData = readYaml text: """${response}"""                      println(yamlData.image.version)                     println(yamlData.image.mit)                     yamlData.image.version = "${releaseVersion}-${env.noDate}"                     yamlData.image.mit  = "${mitId}"                      println(yamlData.toString())                      sh "rm -fr test.yaml"                     riteYaml charset: 'UTF-8', data: yamlData, file: 'test.yaml'                     neYaml = sh returnStdout: true, script: 'cat test.yaml'                                          println(neYaml)                     //更新gitlab文件内容                     base64Content = neYaml.bytes.encodeBase64().toString()                      // 会有并行问题,更新报错                     try {                       UpdateRepoFile(40,"${moduleName}%2fvalues.yaml",base64Content, "${env.branchName}")                       break;                     } catch(e){                       sh "sleep 2"                       continue;                     }                 }             }           }         }           //封装HTTP请求 def HttpReq(reqType,reqUrl,reqBody){     def gitServer = "http://gitlab.idevops.site/api/v4"     ithCredentials([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 }   //获取文件内容 def GetRepoFile(projectId,filePath,branchName){     apiUrl = "projects/${projectId}/repository/files/${filePath}/ra?ref=${branchName}"     response = HttpReq('GET',apiUrl,'')     return response.content }  //更新文件内容 def UpdateRepoFile(projectId,filePath,fileContent, branchName){     apiUrl = "projects/${projectId}/repository/files/${filePath}"     reqBody = """{"branch": "${branchName}","encoding":"base64", "content": "${fileContent}", "mit_message": "update a ne file"}"""     response = HttpReq('PUT',apiUrl,reqBody)     println(response)  } 

images

GitOps-CD部分

CD-Scheduler作业

此作业其实也是接收GitLab的ebhook请求, 与CI-scheduler作业类似。不同的是这个CD-scheduler作业是用来接收环境仓库的代码变更。开启ebhook, 配置触发token。生成hookurlhttp://jenkins.idevops.site/generic-ebhook-trigger/invoke?token=microservicecicd-scheduler-CD

Jenkinsfile pipeline {     agent any      stages {         stage('GetCommitService') {             steps {                 script{                     echo 'Hello World'                     echo "${WebHookData}"                                          // Git Info                     ebhookdata = readJSON text: """${WebHookData}"""                     eventType = ebhookdata["object_kind"]                     mits = ebhookdata["mits"]                     branchName = ebhookdata["ref"] - "refs/heads/"                     projectID = ebhookdata["project_id"]                     mitID = ebhookdata["checkout_sha"]                       changeServices = []                     for(mit in mits) {                         println(mit.id)                          //added                         for (add in mit.added) {                             s = add.split("/") as List                             if (s.size() > 1){                                 if (changeServices.indexOf(s[0]) == -1){                                     changeServices.add(s[0])                                 }                             }                         }                          //modified                         for (m in mit.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 mit.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)                     //服务构建顺序控制                     services = ['service02', 'service01']                     for (service in services){                         if (changeServices.indexOf(service) != -1){                             jobName = 'microservicecicd-'+service+'-service-CD'                             build job: jobName, ait: false,  parameters: [string(name: 'branchName', value: "${branchName}" )]                         }                     }                 }             }         }     } }  环境库配置ebhook

开启ebhook,配置hookurlhttp://jenkins.idevops.site/generic-ebhook-trigger/invoke?token=microservicecicd-scheduler-CD

CD流水线-CD作业

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"                    """                 }             }         }     } } 

  

Copyright © 2016-2025 www.robotxin.com 人工智能机器人网 版权所有 Power by