整合GitLab-CI+Jenkins+K8S实现代码的自动化审核、持续集成部署

一、前言
随着新项目的迭代开发,研发团队规模的日益增大,在一个项目的中后期会逐渐面临着项目代码混乱,多版本代码混合提交,代码质量如何保障,如何减少运维工作快速发布新版本等等问题。

本文整合Gitlab、Jenkins和Kubernetes来做到CICD(持续集成持续部署)。通过使用GitLab-CI+Sonarqube来完成提交代码的自动化审核、代码质量检测,通过控制代码分支的合并来防止低质量的代码提交到我们的开发代码主干上。通过使用Jenkins来完成新提交代码的自动化部署、通过Kubernetes+Docker对部署程序、部署中间件进行容器化和容器编排,减少系统更新发布的人工运维工作量,同时对不同环境下使用同一个交付镜像,保障在开发、测试、生产的每个环境下,所交付的程序是一致的。

本文主要重点介绍的是GitLab+Jenkins来完成CICD的部分,Kubernetes容器化部分不做过多的介绍。

二、系统架构图

三、Gitlab配置

在GitLab上,我们对每个项目创建了Master和Develop两个主干分支,并设置这两个分支为Protected Branches ,只有Maintainer的项目成员有权限进行提交和合并。

于是项目组的开发成员加入项目的时候,便从Develop分支中拉取最新的开发代码,并转化到自己的分支中开发。

我们使用GitLab-CI+Sonarqube进行提交代码的持续集成和代码质量管理,具体GitLab-CI配置文件模板如下(gitlab-ci.yml,gitlab-ci语法详见GitLab CI/CD 基础教程(一)官网介绍):

before_script:
  - mvn clean

stages:
  - install
  - test
  - analysis
  - deploy

variables:
  MAVEN_OPTS: "-Dsonar.gitlab.commit_sha=$CI_BUILD_REF -Dsonar.gitlab.ref_name=$CI_BUILD_REF_NAME -Dsonar.gitlab.project_id=$CI_PROJECT_ID"
# 编译安装
mvn_install:
  script:
    - mvn install -Dmaven.test.skip=true
  stage: install
  only:
    - /.+$/
    - develop
    - master

# 单元测试
unit_test:
  script:
    - echo "Skip Test!"
  stage: test
  only:
    - /.+$/
    - develop
    - master
    
# 集成测试
integration_test:
  script:
    - echo "Skip Test!"
  stage: test
  only:
    - develop
    - master

# 分支合并bug检测
sonarqube_preview:
  script:
    - mvn --batch-mode verify sonar:sonar -Dsonar.analysis.mode=preview -Dmaven.test.skip=true
  stage: analysis
  except:
    - /.+$/
    - develop
    - master

# bug检测
sonarqube:
  script:
    - mvn --batch-mode verify sonar:sonar -Dmaven.test.skip=true
  stage: analysis
  only:
    - /.+$/
    - develop
    - master

# 发布快照版本
mvn_snapshots:
  script:
    - mvn deploy -DaltDeploymentRepository=ylzpay-snapshot::default::https://nexus.ylzpay.com/repository/maven-snapshots/ -Dmaven.test.skip=true
# 容器化需要推送至镜像仓库,例子如下:
#   - docker build -t shemg/yhgw:v20200630001 . && docker push shemg/yhgw:v20200630001
  stage: deploy
  only:
    - develop

# 发布正式版本
mvn_releases:
  script:
    - mvn deploy -DaltDeploymentRepository=ylzpay-release::default::https://nexus.ylzpay.com/repository/maven-releases/ -Dmaven.test.skip=true
# 容器化需要推送至镜像仓库,例子如下:
#   - docker build -t shemg/yhgw:v20200630001 . && docker push shemg/
  stage: deploy
  only:
    - master

after_script:
  - mvn clean

将gitlab-ci.yml提交至项目根目录,在每个分支提交代码时,都会进行打包测试、单元测试和代码质量检测;将个人分支合并至Develop、Develop分支和合并至Master时,还会进行集成测试、发布代码仓库以及镜像打包并推送至镜像仓库。

Developer角色的项目成员可对自己在个人分支完成的代码发起一个merge_requests(分支合并请求),请求将代码合并至Develop分支。Maintainer的项目成员接收到请求,选择是否合并,在同意合并后,GitLab-CI进行自动化测试,测试不通过将拒绝代码合并。

sonarqube代码检测如图所示:

四、Jenkins配置

在Jenkins上,我们配置master和slave集群,master运行在Jenkins服务器上负责调度,slave运行在部署服务器上(非容器部署服务器和容器化部署服务器K8S Master上)

通过在Jenkins配置webhook来监测Gitlab develop或者master分支的代码提交变化

1、首先Jenkins需要安装两个gitlab的插件:Gitlab Hook Plugin和gitlab,插件的安装可以直接在线安装,也可以下载插件之后上传,不在详细介绍。

2、在Jenkins上面新建一个流水线项目,进行如下配置:

点击图中的高级按钮,生成一个Secret token

3、在Gitlab设置webhook
打开gitlab,找到对应的project,打开Settings,进入到Web hooks,在URL处填写jenkins上图中红色的url,Secret Token填上图生成的token,如下图:

配置url和token

点击保存后可以点击测试->Push event,提示Hook executed successfully: HTTP 200即完成代码提交检测

五、Kubernetes配置

kubernetes的配置详见YHOPStack/docker-services项目,这里就不过多介绍

1、Jenkins流水线自动化容器化部署DEMO:

node(node-89-centos7) {

    stage(Prepare) {
        echo 1.Prepare Stage
        provider_name = gnw-location-provider
        consumer_name = gnw-location-consumer
        build_tag = sh(returnStdout: true, script: echo $(date +%Y%m%d)\$(git rev-parse --short HEAD)).trim()
        docker_host = registry.ylzpay.com
        consumer_docker_img_name = ${docker_host}/${consumer_name}:${build_tag}
        provider_docker_img_name = ${docker_host}/${provider_name}:${build_tag}
        echo consumer_docker_img_name: ${consumer_docker_img_name}
        echo provider_docker_img_name: ${provider_docker_img_name}
    }

    stage(Test) {
        echo 2.Test Stage
    }

    stage(Push) {
        echo 4.Push Docker Image Stage
        withCredentials([usernamePassword(credentialsId: docker-register-150, passwordVariable: dockerPassword, usernameVariable: dockerUser)]) {
            sh docker login -u ${dockerUser} -p ${dockerPassword} ${docker_host}
            sh docker push ${consumer_docker_img_name}
            sh docker push ${provider_docker_img_name}
        }
        sh docker rmi ${consumer_docker_img_name}
        sh docker rmi ${provider_docker_img_name}
        echo push docker image: ${consumer_docker_img_name}
        echo push docker image: ${provider_docker_img_name}
        sh /data/ent/jenkins/register_images_version.sh ${consumer_name} ${consumer_docker_img_name}
        sh /data/ent/jenkins/register_images_version.sh ${provider_name} ${provider_docker_img_name}
    }

}