感谢这个安静的环境,没有它我无法完成这篇文章。单应用和环境多应用和环境CI持续集成首先准备一个代码库:https://github.com/DevOpsCICDCourse/microservicescicd/blob/main/microservice-demo-service-master.zip下面我们来梳理一下CI的步骤pipeline:由于本次实现的代码仓库类型为单仓库,即一个仓库存放多个服务模块代码,每个子目录为一个服务模块。首先,我们的持续集成流水线需要能够正确获取到当前commit是哪个服务的代码。确定服务,然后下载服务的代码,进行编译打包、单元测试、代码扫描、镜像构建等步骤。如何获取commit服务信息?这里我们使用GitLab的WebHook功能和Jenkins的jobbuildtrigger对接来实现。工作流程是:当我在Gitlab中提交代码时,会通过GitLabwebhook触发JenkinsSchedulerjob,将提交代码产生的hookdata数据信息以POST的形式发送给JenkinsJob。此时Jenkins作业可以编写使用GenericHook插件获取POST请求传输的请求体信息。它是一段JSON数据。作业运行后,编写Pipeline解析JSON中的数据,获取变更后的服务模块信息。最后触发对应服务的CI作业进行构建。CI-Schedulerjob这个job只需要开启webhook和配置triggertoken(唯一性)。生成hookurl:http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=microservicecicd-scheduler-CIJenkinsfilepipeline{agentanystages{stage("GetData"){steps{script{echo"${webHookData}"data=readJSONtext:"${webHookData}"println(data)env.branchName=data.ref-"refs/heads/"env.commitId=data.checkout_shaenv.projectId=data.project_idcommits=data["commits"]println("${env.branchName}")println("${env.commitID}")println("${env.projectId}")//env.moduleName="service01"changeServices=[]for(commitincommits){println(commit.id)//addedfor(addincommit.added){s=add.split("/")asListif(s.size()>1){if(changeServices.indexOf(s[0])==-1){changeServices.add(s[0])}}}//modifiedfor(mincommit.modified){s=m.split("/")asList//printlns//printlns.size()//printlns[0]if(s.size()>1){//printlnchangeServices.indexOf(s[0])if(changeServices.indexOf(s[0])==-1){changeServices.add(s[0])}}}//removedfor(rincommit.removed){s=r.split("/")asListprintlnsif(s.size()>1){if(changeServices.indexOf(s[0])==-1){changeServices.add(s[0])}}}}println(changeServices)//currentBuild.description="Triggerby${eventType}${changeServices}}}}stage('DefineService'){steps{script{println(changeServices)//服务构建顺序控制services=['service02','service01']for(serviceinservices){if(changeServices.indexOf(service)!=-1){jobName='microservicecicd-'+service+'-service-CI'buildjob: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-CICIpipeline-CI-job为每个微服务创建一个CI作业,三个字符串参数:分支名称、commitID、项目IDJenkinsfileStringbranchName="${env.branchName}"StringmoduleName="${JOB_NAME}".split("/")[1].split("-")[1]StringsrcUrl="http://gitlab.idevops.site/microservicecicd/microservicecicd-demo-service.git"StringcommitId="${env.commitId}"StringprojectId="${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}mvncleanpackage"""}}post{always{junit"${moduleName}/target/surefire-reports/*.xml"}}}stage("SonarScan"){steps{script{defsonarDate=shreturnStdout: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')]){//someblocksh"""cd${moduleName}声纳扫描仪\-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:'用户名')]){env.nowDate=shreturnStdout: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"""dockerlogin-u${用户名}-p${密码}registry.cn-beijing.aliyuncs.comcd${moduleName}&&dockerbuild-t${dockerImage}-f../Dockerfile--build-argSERVICE_NAME=${jarName}.sleep1dockerpush${dockerImage}sleep1dockerrmi${dockerImage}"""}}}}}}GitOps-CI扩展在原CIjobGitOps实践会步骤的基础上增加了更新环境的步骤将当前的基础环境部署文件存放在Git仓库中,我们的CI作业上传完镜像后,也会更新环境部署文件中的镜像标签信息。(所以需要先获取环境文件,更新上传)stage("PushFile"){//when{//expression{"${env.branchName}".contains("RELEASE-")}///步骤{脚本{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=readYamltext:"""${response}"""println(yamlData.image.version)println(yamlData.image.commit)yamlData.image.version="${releaseVersion}-${env.nowDate}"yamlData.image.commit="${commitId}"println(yamlData.toString())sh"rm-frtest.yaml"writeYamlcharset:'UTF-8',data:yamlData,文件:'test.yaml'newYaml=shreturnStdout:true,script:'cattest.yaml'println(newYaml)//更新gitlab文件内容base64Content=newYaml.bytes.encodeBase64().toString()//会出现并行问题,同时更新错误try{UpdateRepoFile(40,"${moduleName}%2fvalues.yaml",base64Content,"${env.branchName}")break;}catch(e){sh"sleep2"continue;}}}}}//封装HTTP请求defHttpReq(reqType,reqUrl,reqBody){defgitServer="http://gitlab.idevops.site/api/v4"withCredentials([string(credentialsId:'gitlab-token',variable:'gitlabToken')]){result=httpRequestcustomHeaders:[[maskValue:true,name:'PRIVATE-TOKEN',value:"${gitlabToken}"]],httpMode:reqType,contentType:"APPLICATION_JSON",consoleLogResponseBody:true,ignoreSslErrors:true,requestBody:reqBody,url:"${gitServer}/${reqUrl}"//quiet:true}returnresult}//获取文件内容defGetRepoFile(projectId,filePath,branchName){apiUrl="projects/${projectId}/repository/files/${filePath}/raw?ref=${branchName}"response=HttpReq('GET',apiUrl,'')returnresponse.content}//更新文件内容defUpdateRepoFile(projectId,filePath,fileContent,branchName){apiUrl="projects/${projectId}/repository/files/${filePath}"reqBody="""{"branch":"${branchName}","encoding":"base64","content":"${fileContent}","commit_message":"updateanewfile"}"""response=HttpReq('PUT',apiUrl,reqBody)println(response)}imagesGitOps-CD-CD-Scheduler作业的CD部分这个作业实际上是接收GitLab的webhook请求,类似于CI-schedulerjob,这个CD-schedulerjob用于接收环境仓库中的代码变更,启用webhook并配置triggertoken。生成hookurl:http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=microservicecicd-scheduler-CDJenkinsfilepipeline{agentanystages{stage('GetCommitService'){steps{script{echo'HelloWorld'echo"${WebHookData}"//GitInfowebhookdata=readJSONtext:"""${WebHookData}"""eventType=webhookdata["object_kind"]commits=webhookdata["commits"]branchName=webhookdata["ref"]-"refs/heads/"projectID=webhookdata["project_id"]commitID=webhookdata["checkout_sha"]changeServices=[]for(commitincommits){println(commit.id)//addedfor(addincommit.added){s=add.split("/")asListif(s.size()>1){if(changeServices.indexOf(s[0])==-1){changeServices.add(s[0])}}}//modifiedfor(mincommit.modified){s=m.split("/")asList//printlns//printlns.size()//printlns[0]if(s.size()>1){//printlnchangeServices.indexOf(s[0])if(changeServices.indexOf(s[0])==-1){changeServices.add(s[0])}}}//removedfor(rincommit.removed){s=r.split("/")asListprintlnsif(s.size()>1){if(changeServices.indexOf(s[0])==-1){changeServices.add(s[0])}}}}println(changeServices)currentBuild.description="Triggerby${eventType}${changeServices}"}}}stage('DefineService'){steps{script{println(changeServices)//服务构建顺序控制services=['service02','service01']for(serviceinservices){if(changeServices.indexOf(service)!=-1){jobName='microservicecicd-'+service+'-service-CD'buildjob:jobName,wait:false,parameters:[string(name:'branchName',value:"${branchName}")]}}}}}}环境库配置webhook开启webhook,配置hookurl:http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=microservicecicd-scheduler-CDCD流水线-CD作业JenkinsfileStringserviceName="${JOB_NAME}".split("-")[1]StringnameSpace="${JOB_NAME}".split("-")[0].split("/")[-1]//pipelinepipeline{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"""kubectlcreatens"${nameSpace}-uat"||echofalsehelminstall"${serviceName}"--namespace"${nameSpace}-uat"./"${serviceName}"||helmupgrade"${serviceName}"--namespace"${nameSpace}-uat"./"${serviceName}"helmlist--namespace"${nameSpace}-uat"helmhistory"${serviceName}"--namespace“${nameSpace}-uat""""}}}}}
