业务范围

基于jenkins docker实现CI/CD实践

发布日期:2024-10-14 13:21    点击次数:120

温馨提示

Tips

项目简介

利用 Jenkins、Docker、SonarQube 和 Harbor 技术,搭建一个完整的 CI/CD 管道,实现持续集成、持续交付和持续部署的流程。通过自动化构建、测试、代码质量检查和容器化部署,将开发人员从繁琐的手动操作中解放出来,提高团队的开发效率、软件质量和安全性,实现持续更新迭代和持续部署交付。

CICD流程图

图片

jenkins.drawio.png流程说明 开发人员将代码提交到Gitlab代码仓库时,gitlab请求jenkins的webhook地址,触发持续构建和持续部署流程。Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后调用SonarQube完成代码扫描。扫描完成调用docker打包成容器镜像,并推送至Harbor镜像仓库。Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。Jenkins完成CICD流程后,将结果邮件通知给开发和运维人员。用户访问项目服务器。服务器列表 服务器名称主机名IP部署服务代码托管服务器gitlab192.168.10.72Gitlab持续集成服务器jenkins192.168.10.73Jenkins、Maven、Docker代码审查服务器sonarqube192.168.10.71SonarQube镜像仓库服务器harbor192.168.10.100Docker、harbor服务部署服务器springboot192.168.10.74Docker项目代码仓库地址

gitee:https://gitee.com/cuiliang0302/sprint_boot_demo

github:https://github.com/cuiliang0302/sprint-boot-demo

服务部署(rpm方式)gitlab部署

参考文档:https://www.cuiliangblog.cn/detail/section/92727905

jenkins部署

参考文档:https://www.cuiliangblog.cn/detail/section/15130009

docker部署

参考文档:https://www.cuiliangblog.cn/detail/section/26447182

harbor部署

参考文档:https://www.cuiliangblog.cn/detail/section/15189547

SonarQube部署

参考文档:https://www.cuiliangblog.cn/detail/section/131467837

harbor项目权限配置创建项目

Harbor的项目分为公开和私有的: 

公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。 

私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。我们可以为微服务项目创建一个新的项目

图片

创建用户

创建一个普通用户cuiliang。

图片

配置项目用户权限

在spring_boot_demo项目中添加普通用户cuiliang,并设置角色为开发者。

图片

权限说明角色权限访客对项目有只读权限开发人员对项目有读写权限维护人员对项目有读写权限、创建webhook权限项目管理员除上述外,还有用户管理等权限上传下载镜像测试

可参考文章https://www.cuiliangblog.cn/detail/section/15189547,此处不再赘述。

gitlab项目权限配置

具体gitlab权限配置参考文档:https://www.cuiliangblog.cn/detail/section/131513569

创建开发组develop,用户cuiliang,项目springboot demo

创建组

管理员用户登录,创建群组,组名称为develop,组权限为私有

图片

创建项目

创建sprint boot demo项目,并指定develop,项目类型为私有

图片

创建用户

创建一个普通用户cuiliang

图片

用户添加到组中

将cuiliang添加到群组develop中,cuiliang角色为Developer

图片

开启分支推送权限

图片

image.png用户权限验证

使用任意一台机器模拟开发人员拉取代码,完成开发后推送至代码仓库。拉取仓库代码

[root@tiaoban opt]# git clone https://gitee.com/cuiliang0302/sprint_boot_demo.git正克隆到 'sprint_boot_demo'...remote: Enumerating objects: 69, done.remote: Counting objects: 100% (69/69), done.remote: Compressing objects: 100% (54/54), done.remote: Total 69 (delta 15), reused 0 (delta 0), pack-reused 0接收对象中: 100% (69/69), 73.15 KiB | 1.49 MiB/s, 完成.处理 delta 中: 100% (15/15), 完成.[root@tiaoban opt]# cd sprint_boot_demo/[root@tiaoban sprint_boot_demo]# lsemail.html  Jenkinsfile  LICENSE  mvnw  mvnw.cmd  pom.xml  readme.md  sonar-project.properties  src  test

推送至gitlab仓库

# 修改远程仓库地址[root@tiaoban sprint_boot_demo]# git remote set-url origin http://192.168.10.72/develop/sprint-boot-demo.git[root@tiaoban sprint_boot_demo]# git remote -vorigin  http://192.168.10.72/develop/sprint-boot-demo.git (fetch)origin  http://192.168.10.72/develop/sprint-boot-demo.git (push)# 推送代码至gitlab[root@tiaoban sprint_boot_demo]# git push --set-upstream origin --allUsername for 'http://192.168.10.72': cuiliangPassword for 'http://cuiliang@192.168.10.72': 枚举对象中: 55, 完成.对象计数中: 100% (55/55), 完成.使用 4 个线程进行压缩压缩对象中: 100% (34/34), 完成.写入对象中: 100% (55/55), 71.51 KiB | 71.51 MiB/s, 完成.总共 55(差异 10),复用 52(差异 9),包复用 0To http://192.168.10.72/develop/sprint-boot-demo.git * [new branch]      main -> main分支 'main' 设置为跟踪 'origin/main'。

查看验证

图片

jenkins流水线配置拉取gitlab仓库代码

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/127410630

此处以账号密码验证为例,并启用webhook配置。jenkins流水线配置如下

图片

拉取代码部分的jenkinsfile如下pipeline {    agent any    stages {        stage('拉取代码') {            environment {                // gitlab仓库信息                GITLAB_CRED = 'gitlab-cuiliang-password'                GITLAB_URL = 'http://192.168.10.72/develop/sprint-boot-demo.git'            }            steps {                echo '开始拉取代码'                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: '${GITLAB_CRED}', url: '${GITLAB_URL}']])                echo '拉取代码完成'            }        }    }}

当git仓库提交代码后,Gitlab会自动请求Jenkins的webhook地址,自动触发流水线,执行结果如下:

图片

Maven打包编译

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/131898197

打包编译部分的jenkinsfile如下

pipeline {    agent any    stages {        stage('拉取代码') {            ……        }        stage('打包编译') {            steps {                echo '开始打包编译'                sh 'mvn clean package'                echo '打包编译完成'            }        }    }}

触发流水线结果如下

图片

SonarQube代码审查

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/165534414

代码审查阶段的jenkinsfile如下

pipeline {    agent any    stages {        stage('拉取代码') {            ……        }        stage('打包编译') {            ……        }        stage('代码审查') {            environment {                // SonarQube信息                SONARQUBE_SCANNER = 'SonarQubeScanner'                SONARQUBE_SERVER = 'SonarQubeServer'            }            steps{                echo '开始代码审查'                script {                    def scannerHome = tool '${SONARQUBE_SCANNER}'                    withSonarQubeEnv('${SONARQUBE_SERVER}') {                        sh '${scannerHome}/bin/sonar-scanner'                    }                }                echo '代码审查完成'            }        }    }}

触发流水线结果如下

图片

代码审查结果如下

图片

构建并推送镜像至仓库

具体步骤可参考文档:https://www.cuiliangblog.cn/detail/section/166573065

构建并推送镜像的jenkinsfile如下

pipeline {    agent any    stages {        stage('拉取代码') {            ……        }        stage('打包编译') {            ……        }        stage('代码审查') {            ……        }        stage('构建镜像') {            environment {                // harbor信息                HARBOR_CRED = 'harbor-cuiliang-password'                HARBOR_URL = 'harbor.local.com'                HARBOR_PROJECT = 'spring_boot_demo'                // 镜像信息                IMAGE_APP = 'demo'                IMAGE_TAG = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()                IMAGE_NAME = '${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_APP}:${IMAGE_TAG}'            }            steps {                echo '开始构建镜像'                script {                    docker.build '${IMAGE_NAME}'                }                echo '构建镜像完成'                echo '开始推送镜像'                script {                    docker.withRegistry('https://${HARBOR_URL}', '${HARBOR_CRED}') {                        docker.image('${IMAGE_NAME}').push()                    }                }                echo '推送镜像完成'                echo '开始删除镜像'                script {                    sh 'docker rmi -f ${IMAGE_NAME}'                }                echo '删除镜像完成'            }        }    }}

触发流水线结果如下

图片

查看harbor镜像仓库,已上传镜像

图片

docker运行服务

远程执行命令具体内容可参考文档:https://www.cuiliangblog.cn/detail/section/166296541

部署运行阶段的jenkinsfile如下

pipeline {    agent any    environment {        // 全局变量        HARBOR_CRED = 'harbor-cuiliang-password'        IMAGE_NAME = ''        IMAGE_APP = 'demo'    }     stages {        stage('拉取代码') {            ……        }        stage('打包编译') {            ……        }        stage('代码审查') {            ……        }        stage('构建镜像') {            ……        }        stage('项目部署') {            environment {                // 目标主机信息                HOST_NAME = 'springboot1'            }            steps {                echo '开始部署项目'                // 获取harbor账号密码                withCredentials([usernamePassword(credentialsId: '${HARBOR_CRED}', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {                    // 执行远程命令                    sshPublisher(publishers: [sshPublisherDesc(configName: '${HOST_NAME}', transfers: [sshTransfer(                        cleanRemote: false, excludes: '', execCommand: 'sh -x /opt/jenkins/springboot/deployment.sh ${HARBOR_USERNAME} ${HARBOR_PASSWORD} ${IMAGE_NAME} ${IMAGE_APP}',                         execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/opt/jenkins/springboot',                         remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'deployment.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false                    )])                }                echo '部署项目完成'            }        }    }}    

触发流水线后运行结果如下

图片

登录springboot服务器验证
[root@springboot ~]# docker psCONTAINER ID   IMAGE                                            COMMAND                CREATED              STATUS                          PORTS                                       NAMESe80896487125   harbor.local.com/spring_boot_demo/demo:8880a30   'java -jar /app.jar'   About a minute ago   Up About a minute (unhealthy)   0.0.0.0:8888->8888/tcp, :::8888->8888/tcp   demo[root@springboot ~]# curl 127.0.0.1:8888/<h1>Hello SpringBoot</h1><p>Version:v2 Env:test</p>[root@springboot1 ~]# [root@springboot ~]# ls /opt/jenkins/springboot/deployment.sh  Dockerfile  email.html  Jenkinsfile  LICENSE  mvnw  mvnw.cmd  pom.xml  readme.md  sonar-project.properties  src  target  test
添加邮件通知推送

发送邮件配置具体内容可参考文档:https://www.cuiliangblog.cn/detail/section/133029974

在项目根路径下新增email.html文件,内容如下

<!DOCTYPE html><html lang='en'>  <head>    <meta charset='UTF-8'>    <meta name='viewport' content='width=device-width, initial-scale=1.0'>    <title>${ENV, var='JOB_NAME'}-第${BUILD_NUMBER}次构建日志</title>    <style>      body {        font-family: Arial, sans-serif;        line-height: 1.6;        margin: 0;        padding: 0;      }      .container {        max-width: 1000px;        margin: 20px auto;        padding: 20px;        border: 1px solid #ccc;        border-radius: 5px;      }      .container h2 {        text-align: center;      }      .logo img {        max-width: 150px;        height: auto;      }      .content {        padding: 20px;        background-color: #f9f9f9;        border-radius: 5px;      }      .footer {        margin-top: 20px;        text-align: center;      }    </style>  </head>  <body>    <div class='container'>      <div class='content'>        <h2>Jenkins ${PROJECT_NAME}项目构建结果</h2>        <p>尊敬的用户:</p>        <p>${PROJECT_NAME}项目构建结果为<span style='color:red;font-weight: bold;'>${BUILD_STATUS}</span>,以下是详细信息:</p>        <h4>构建信息</h4>        <hr/>        <ul>          <li>项目名称:${PROJECT_NAME}</li>          <li>构建编号:第${BUILD_NUMBER}次构建</li>          <li>触发原因:${CAUSE}</li>          <li>构建状态:${BUILD_STATUS}</li>          <li>构建日志:<a href='${BUILD_URL}console'>${BUILD_URL}console</a></li>          <li>构建Url:<a href='${BUILD_URL}'>${BUILD_URL}</a></li>          <li>工作目录:<a href='${PROJECT_URL}ws'>${PROJECT_URL}ws</a></li>          <li>项目Url:<a href='${PROJECT_URL}'>${PROJECT_URL}</a></li>        </ul>        <h4>失败用例</h4>        <hr/>        <p>$FAILED_TESTS</p>        <h4>最近提交</h4>        <hr/>        <ul>          ${CHANGES_SINCE_LAST_SUCCESS, reverse=true, format='%c', changesFormat='<li>%d [%a] %m</li>'}        </ul>        <h4>提交详情</h4>        <hr/>        <p><a href='${PROJECT_URL}changes'>${PROJECT_URL}changes</a></p>        <p style='margin-top:50px'>如有任何疑问或需要帮助,请随时联系我们。</p>      </div>      <div class='footer'>        <p>此为系统自动发送邮件,请勿回复。</p>      </div>    </div>  </body></html>

完整的jenkinsfile如下

pipeline {    agent any    environment {        // 全局变量        HARBOR_CRED = 'harbor-cuiliang-password'        IMAGE_NAME = ''        IMAGE_APP = 'demo'    }     stages {        stage('拉取代码') {            environment {                // gitlab仓库信息                GITLAB_CRED = 'gitlab-cuiliang-password'                GITLAB_URL = 'http://192.168.10.72/develop/sprint-boot-demo.git'            }            steps {                echo '开始拉取代码'                checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: '${GITLAB_CRED}', url: '${GITLAB_URL}']])                echo '拉取代码完成'            }        }        stage('打包编译') {            steps {                echo '开始打包编译'                sh 'mvn clean package'                echo '打包编译完成'            }        }        stage('代码审查') {            environment {                // SonarQube信息                SONARQUBE_SCANNER = 'SonarQubeScanner'                SONARQUBE_SERVER = 'SonarQubeServer'            }            steps{                echo '开始代码审查'                script {                    def scannerHome = tool '${SONARQUBE_SCANNER}'                    withSonarQubeEnv('${SONARQUBE_SERVER}') {                        sh '${scannerHome}/bin/sonar-scanner'                    }                }                echo '代码审查完成'            }        }        stage('构建镜像') {            environment {                // harbor仓库信息HARBOR_URL = 'harbor.local.com'                HARBOR_PROJECT = 'spring_boot_demo'                // 镜像名称                IMAGE_TAG = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()            }            steps {                echo '开始构建镜像'                script {                    IMAGE_NAME = '${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_APP}:${IMAGE_TAG}'                     docker.build '${IMAGE_NAME}'                }                echo '构建镜像完成'                echo '开始推送镜像'                script {                    docker.withRegistry('https://${HARBOR_URL}', '${HARBOR_CRED}') {                        docker.image('${IMAGE_NAME}').push()                    }                }                echo '推送镜像完成'                echo '开始删除镜像'                script {                    sh 'docker rmi -f ${IMAGE_NAME}'                }                echo '删除镜像完成'            }        }        stage('项目部署') {            environment {                // 目标主机信息                HOST_NAME = 'springboot1'            }            steps {                echo '开始部署项目'                // 获取harbor账号密码                withCredentials([usernamePassword(credentialsId: '${HARBOR_CRED}', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {                    // 执行远程命令                    sshPublisher(publishers: [sshPublisherDesc(configName: '${HOST_NAME}', transfers: [sshTransfer(                        cleanRemote: false, excludes: '', execCommand: 'sh -x /opt/jenkins/springboot/deployment.sh ${HARBOR_USERNAME} ${HARBOR_PASSWORD} ${IMAGE_NAME} ${IMAGE_APP}',                         execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/opt/jenkins/springboot',                         remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'deployment.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false                    )])                }                echo '部署项目完成'            }        }    }    post {        always {            echo '开始发送邮件通知'            // 构建后发送邮件            emailext(                subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!',                body: '${FILE,path='email.html'}',                to: 'cuiliang0302@qq.com'            )            echo '邮件通知发送完成'        }    }}    

触发流水线后运行结果如下

图片

邮件通知内容如下

图片

至此,整个CICD流程完成。 

WeChat group

图片

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报。

上一篇:Ubuntu20.04 配置OpenGL开发环境(并验证)
下一篇:没有了