在Google Kubernetes集群创建分布式Jenkins(二)

上一篇博客在Google Kubernetes集群创建分布式Jenkins(一)-CSDN博客我介绍了如何在GCP的K8S集群上部署一个分布式的Jenkins,并实现了一个简单的Pipeline的运行。

在实际的开发中,我们通常都会按照以下的CICD流程来设置Pipeline

在我司的实际实践中,通常包括如下的步骤:

  1. 往Git仓库提交开发分支的代码,创建Pull request来进行code review
  2. Git仓库配置hook,当收到PR时触发jenkins任务,拉取代码到Jenkins进行编译和UT检查
  3. Jenkins把UT检查结果回写到PR的comments,如果UT检查通过,自动Approve+1
  4. 当其他人对PR的code review完成,Approve+2,自动或手动合并代码到主分支。
  5. 合并到主分支触发Jenkins的打包任务,Jenkins拉取主分支代码运行打包任务
  6. 打包任务完成后,把打包的镜像推送到registry
  7. 更新部署的manifest,更新镜像的标签
  8. 把应用部署到GKE

这里我将按照以上任务来设置相应的pipeline

Jenkins安装插件

要完成以上的任务,我们需要安装以下的一些插件

Generic webhook trigger plugin

Github plugin

Credential binding plugin

Git仓库设置webhook

我选择GitHub作为示例,在github上建立一个简单的Java Springboot项目,实现一个rest接口,完成一个简单的计算任务,代码仓库在https://github.com/gzroy/webdemo.git。在这个repo的页面上,点击Settings-\>webhooks,然后选择添加webhook,在Payload URL里面输入jenkins URL,例如我的Jenkins URL是http://123.123.123.123/jenkins/generic-webhook-trigger/invoke?token=abc,ContentType选择application/json,然后点击"Let me select individual events",选择触发hook的事件。这里我选择Pull request。

设置CICD项目

因为我打算以后其他项目都统一用这个CICD的流程,所以新建一个CICD的项目,把所需要的Jenkins pipeline的代码都放到这个项目中。以后其他项目只需要引用这个项目的相关文件即可完成CICD的任务。代码仓库在https://github.com/gzroy/cicd.git

创建PR自动触发测试的Pipeline

我们要实现的功能是,当开发者新建一个分支,修改完代码,commit提交并创建PR之后,应该要自动触发CI的pipeline来进行UT测试,并把测试结果写到PR的comments里面,这样其他reviewer可以查看这个测试的结果。

在CICD项目里面,我新建了一个ut_pipeline.groovy文件,内容如下:

Groovy 复制代码
pipeline {
  agent{
    kubernetes{
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          volumes:
          - name: maven-pv-storage
            persistentVolumeClaim:
              claimName: maven-repo-storage
          containers:
          - name: maven
            image: maven:3.8.3-openjdk-17
            tty: true
            imagePullPolicy: "IfNotPresent"
            volumeMounts:
            - mountPath: '/root/.m2/repository' 
              name: maven-pv-storage
            command:
            - cat
        '''
    }
  }
  triggers {
    GenericTrigger(
      genericVariables: [
        [key: 'action', value: '$.action', expressionType: 'JSONPath'],
        [key: 'clone_url', value: '$.pull_request.base.repo.clone_url', expressionType: 'JSONPath'],
        [key: 'ref', value: '$.pull_request.head.ref', expressionType: 'JSONPath'],
        [key: 'sha', value: '$.pull_request.head.sha', expressionType: 'JSONPath'],
        [key: 'number', value: '$.number', expressionType: 'JSONPath'],
        [key: 'comments_url', value: '$.pull_request.comments_url', expressionType: 'JSONPath']
      ],
      token: 'abc'
    )
  }
  environment {
    CREDENTIAL = credentials("${CREDENTIAL_ID}")
  }
  stages{
    stage("git checkout") {
      when {
        expression {
          return action=="opened" || action=="synchronize"
        }
      }
      steps {
        script {
          git(
            url: clone_url,
            credentialsId: CREDENTIAL_ID,
            branch: ref
          )
        }
      }
    }
    stage("test"){
      when {
        expression {
          return action=="opened" || action=="synchronize"
        }
      }
      steps{
        container('maven') {
          script{
            sh 'mvn test '
          }
        }
      }
      post {
        failure {
          sh """
          (curl -L -X POST \
          -H \"Accept: application/vnd.github+json\" \
          -H \"Authorization: Bearer ${env.CREDENTIAL_PSW}\" \
          -H \"X-GitHub-Api-Version: 2022-11-28\" \
          ${comments_url} \
          -d \'{\"body\": \"UT test failure for commit ${sha}\"}\')
          """
        }
        success {
          sh """
          (curl -L -X POST \
          -H \"Accept: application/vnd.github+json\" \
          -H \"Authorization: Bearer ${env.CREDENTIAL_PSW}\" \
          -H \"X-GitHub-Api-Version: 2022-11-28\" \
          ${comments_url} \
          -d \'{\"body\": \"UT test success for commit ${sha}\"}\')
          """
        }
      }
    }
  }
}

解释一下这个程序。在Agent模块里面定义了一个运行Jenkins Agent的Pod,其中container除了默认的agent container之外,还包括一个maven container,在test这个stage里面,将运行这个maven container的mvn test命令来执行UT测试。

在triggers模块中,定义了GenericTrigger从webhook的回调API中获取body的数据,把其中需要的数据赋值到变量,给后续的stage使用。

在environment模块中,定义了一个名为CREDENTIAL的全局变量,通过credentials helper函数来获取Jenkins保存的凭证,这里的CREDENTIAL_ID是在Jenkins System里面定义的全局变量。注意这里必须要加上双引号,使得Groovy可以预先用Jenkins变量的值来替代这个变量,如果用单引号则不行。

在Stages模块中,定义了两个Stage,这两个操作都是当PR新建或已有PR上发生新的commit时才执行。第一个Stage执行从Github仓库checkout的操作。第二个Stage是执行maven测试,在Post里面定义了测试成功或失败,需要给PR加上相应的comment。这里都使用了Github API来进行操作,我们需要预先把Github用户的access token保存到Jenkins的credentials中。

回到Jenkins的控制台,新建一个webdemo_ut的Job,选择Pipeline类型,然后在Github项目里面填入https://github.com/gzroy/webdemo.git,在构建触发器里面选择Generic Webhook Trigger,Token也要填上,这个Token需要和Github配置webhook的token一致。定义Pipeline里面选择Pipeline script from SCM,填入CICD项目的地址和Script文件的路径,Branch to build里面输入*/*,然后保存即可。

之后我们在https://github.com/gzroy/webdemo.git这个项目上可以执行新建分支,然后改动代码,提交之后创建一个PR,可以看到Jenkins将自动触发一个任务,进行测试后把结果回写到PR的comments中。

合并到主分支自动触发打包的pipeline

创建一个package_pipeline.groovy的文件,当收到Webhook回调时,判断如果是Merge的事件,那么将拉取主分支的代码进行package,并进行Docker构建,然后把镜像推送到相应的registry。

以下是pipeline的代码:

Groovy 复制代码
pipeline {
  agent{
    kubernetes{
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          volumes:
          - name: maven-pv-storage
            persistentVolumeClaim:
              claimName: maven-repo-storage
          containers:
          - name: maven
            image: maven:3.8.3-openjdk-17
            tty: true
            imagePullPolicy: "IfNotPresent"
            volumeMounts:
            - mountPath: '/root/.m2/repository' 
              name: maven-pv-storage
            command:
            - cat
          - name: kaniko
            image: gcr.io/kaniko-project/executor:debug
            command:
            - sleep
            args:
            - 9999999
          serviceAccountName: "jenkins-sa"
      '''
    }
  }
  triggers {
    GenericTrigger(
      genericVariables: [
        [key: 'action', value: '$.action', expressionType: 'JSONPath'],
        [key: 'clone_url', value: '$.pull_request.base.repo.clone_url', expressionType: 'JSONPath'],
        [key: 'ref', value: '$.pull_request.head.ref', expressionType: 'JSONPath'],
        [key: 'name', value: '$.pull_request.base.repo.name', expressionType: 'JSONPath']
      ],
      token: 'abc'
    )
  }
  environment {
    CREDENTIAL = credentials("${CREDENTIAL_ID}")
    PACKAGE_STATUS = "success"
  }
  stages{
    stage("git checkout") {
      when {
        expression {
          return action=="closed"
        }
      }
      steps {
        script {
          git(
            url: clone_url,
            credentialsId: CREDENTIAL_ID,
            branch: ref
          )
        }
      }
    }
    stage("package"){
      when {
        expression {
          return action=="closed"
        }
      }
      steps{
        container('maven') {
          script{
            sh 'mvn clean package'
            sh 'mkdir target/extracted'
            sh 'java -Djarmode=layertools -jar target/*.jar extract --destination target/extracted'
          }
        }
      }
    }
    stage("Build image and push to registry") {
      when {
        expression {
          return action=="closed"
        }
      }
      steps {
        container('kaniko') {
          script {
            sh "/kaniko/executor --verbosity debug --context `pwd` --destination asia-southeast1-docker.pkg.dev/PROJECT_ID/REPO_NAME/${name}:0.0.1"
          }
        }
      }
    }
  }
}

这个pipeline的Container里面增加了一个Kaniko的镜像,这是一个开源的项目,可以在K8S的环境下来打包镜像。在Stage package里面,除了用maven来创建jar文件,还解压了其分层信息,这样可以帮助我们更好的构建镜像。在最后的stage里面,调用kaniko镜像来进行镜像构建。

需要注意的是,在agent template里面我们还需要指定这个Pod所使用的service account,这个Service account已经和GCP的service account进行了绑定,并且GCP的service account已经赋予Storage Object Admin的权限,这样就可以用workload identity的方式来访问Google的container registry了。

新建一个webdemo_package的job,配置和上面类似,只是pipeline script选择package_pipeline.groovy文件。当我们合并PR到主分支之后,就会触发一个pr closed的webhook API调用,可以看到Jenkins将自动触发package任务,生成镜像并推送到registry。

部署镜像的pipeline

这个步骤比较简单,就略过了,只要修改一下部署的manifest文件,把镜像的版本改为上一步构建的镜像版本即可。

总结

以上就是设置一个自动触发Jenkins任务的CICD流程。

相关推荐
可观测性用观测云1 天前
云原生网关 Ingress-Nginx 链路追踪实战:OpenTelemetry 采集与观测云集成方案
nginx·kubernetes
回家路上绕了弯1 天前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
蝎子莱莱爱打怪3 天前
GitLab CI/CD + Docker Registry + K8s 部署完整实战指南
后端·docker·kubernetes
初次攀爬者6 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
蝎子莱莱爱打怪6 天前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes
阿里云云原生7 天前
Kubernetes 官方再出公告,强调立即迁移 Ingress NGINX
kubernetes
至此流年莫相忘7 天前
Kubernetes实战篇之配置与存储
云原生·容器·kubernetes
断手当码农7 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
IAR Systems7 天前
松下电工借助IAR CI/CD解决方案,实现品质与效率双重飞跃
ci/cd
Cherry的跨界思维7 天前
【AI测试全栈:质量】47、Vue+Prometheus+Grafana实战:打造全方位AI监控面板开发指南
vue.js·人工智能·ci/cd·grafana·prometheus·ai测试·ai全栈