基于Jenkins Pipeline构建企业级CI/CD

Lark Notice Plugin 是一个用于 Jenkins 的构建通知插件,可以将 Jenki
首页 新闻资讯 行业资讯 基于Jenkins Pipeline构建企业级CI/CD

案例介绍

本案例通过若依项目作为案例,通过Jenkins构建企业级CI/CD平台。

若依服务列表:

  • ruoyi-auth

  • ruoyi-system

  • ruoyi-gateway

  • ruoyi-ui

若依环境列表:

  • DEV

  • UAT

  • PROD

环境准备工作:

  • nacos安装并配置完成

  • MySQL部署完成并初始化

  • Redis部署完成

  • Harbor镜像仓库

  • Gitlab部署完成

  • Kubernetes部署完成

  • Ingress部署完成

设计思路

触发构建设计:

本设计通过Jenkins Generic Webhook Trigger 插件实现了基于Webhook自动触发流水线构建。

图片图片

流程说明:

  • 研发项目负责人代码开发完成后进行合并代码并生成Tag

  • Gitlab通过Webhook自动触发Jenkins Pipeline构建

流水线设计:

图片图片

Jenkins流水线完整图:

图片图片

自定义基础镜像

在实际企业环境中,基础镜像都会根据具体得需求定义适合自己得基础镜像。

定义Maven镜像:

用于代码构建编译打包,会把Ruoyi相关依赖包打到基础镜像内,避免分层构建失败。

# 拉取源代码并切换分支$ https://gitee.com/y_project/RuoYi-Cloud.git$ git checkout v3.6.3$ cd..# 定义Dockerfile$ cat DockerfileFROMmaven:3.8.6-openjdk-8ADDRuoYi-Cloud/opt/RuoYi-Cloud
RUN cd/opt/RuoYi-Cloud&&mvn clean install-DskipTests
RUN rm-rf/opt/RuoYi-Cloud# 构建镜像$ docker build uhub.service.ucloud.cn/kubesre/maven:jdk8.$ docker push uhub.service.ucloud.cn/kubesre/maven:jdk8

定义Java基础镜像:

根据需求定义适合自己的基础镜像。通过变量传递让配置变得更灵活!

# 创建个目录$ mkdir base&&cd base# 创建启动脚本$ cat docker-entrypoint.sh#!/bin/shjava-server-Xms$JVM_XMS-Xmx$JVM_XMX-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:/data/gc.log-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/data/heapdump.hprof-jar app.jar--server.port=$SERVICE_PORT --spring.profiles.active=$PROFILES_ACTIVE --spring.cloud.nacos.config.server-addr=$NACOS_ADDRESS --spring.cloud.nacos.config.namespace=$NACOS_NAMESPACE_ID --spring.cloud.nacos.config.username=$NACOS_USERNAME --spring.cloud.nacos.config.password=$NACOS_PASSWORD --spring.cloud.nacos.discovery.server-addr=$NACOS_ADDRESS --spring.cloud.nacos.discovery.namespace=$NACOS_NAMESPACE_ID --spring.cloud.nacos.discovery.username=$NACOS_USERNAME --spring.cloud.nacos.discovery.password=$NACOS_PASSWORD# 创建down-nacos脚本cat down-nacos.sh#!/bin/shipTrue=falsejava_service_ip=""code=falsegetPodIp(){
    java_service_ip=`ip a  | grep inet | grep -v inet6 | grep -v '127.0.0.1' | awk '{print $2}' | awk -F / '{print$1}'`grep-w"${java_service_ip}"/etc/hosts>/dev/nullif[$?-eq0];thenecho"get java service ip success"ipTrue=trueelseecho"get java service ip failed"fi
}

downService(){
    accessToken=`curl -s -X POST http://$NACOS_ADDRESS/nacos/v1/auth/users/login --form username=$NACOS_USERNAME --form password=$NACOS_PASSWORD|jq -r .accessToken`curl-s-X  PUT"$NACOS_ADDRESS/nacos/v1/ns/instance?language=zh-CN&accessToken=$accessToken&username=$NACOS_USERNAME&serviceName=$JAVA_SERVICE_NAME&ip=$java_service_ip&port=$SERVICE_PORT&enabled=false&namespaceId=$NACOS_NAMESPACE_ID"if["$code"="ok"];thenecho"java service down from nacos success"code=trueelseecho"java service down from nacos failed"fi
}start(){
  getPodIpif$ipTrue;thendownService
    sleep30elseecho"down $JAVA_SERVICE_NAME failed">>down_service.log
  fi   
}start# 定义Dockerfile$ cat DockerfileFROMopenjdk:8-jre
WORKDIR/dataCOPY./down-nacos.sh.COPY./docker-entrypoint.sh.RUN chmod+x docker-entrypoint.sh&&chmod+x  down-nacos.sh
ENTRYPOINT["./docker-entrypoint.sh"]# 构建镜像$ docker build uhub.service.ucloud.cn/kubesre/java-base:v8.$ docker push uhub.service.ucloud.cn/kubesre/java-base:v8

变量说明:

  • JVM_XMS:最小JVM堆栈内存

  • JVM_XMX:最大JVM堆栈内存

  • SERVICE_PORT:应用服务端口

  • NACOS_ADDRESS:Nacos地址

  • NACOS_USERNAME:Nacos用户名

  • NACOS_PASSWORD:Nacos密码

  • NACOS_NAMESPACE_ID:Nacos命名空间ID

  • PROFILES_ACTIVE:环境名称

Dockerfile编写

分层构建好处:

  • 不依赖本地环境

  • 减小容器镜像大小

Java Dockerfile(分层构建):

FROMuhub.service.ucloud.cn/kubesre/maven:jdk8ASbuild
COPY  src/opt/src/COPY pom.xml/opt/RUN  cd/opt/&&mvn clean install-DskipTestsFROMuhub.service.ucloud.cn/kubesre/java-base:v8# 复制jar文件到路径COPY--from=build /opt/target/*.jar /data/app.jar

Vue Dockerfile(分层构建):

FROMnode:16ASbuilder# 设置工作目录WORKDIR/usr/src/app# 将项目文件复制到 Docker 镜像中COPY..# 安装项目依赖RUN npm install--registry=https://registry.npmmirror.com# 构建静态文件RUN npm run build:prod# 使用 Nginx 镜像作为基础镜像,用于托管静态文件FROMnginx:stable-alpine

WORKDIR/home/ruoyi/projects/ruoyi-ui# 将 VuePress 构建的静态文件复制到 Nginx 的网站目录COPY--from=builder /usr/src/app/dist /home/ruoyi/projects/ruoyi-uiCOPY./nginx/conf/nginx.conf/etc/nginx/nginx.conf# 暴露 80 端口EXPOSE80# 启动 NginxCMD["nginx","-g","daemon off;"]

Pipeline编写

如下所有Pipeline文件,需要自行修改内容:

  • credentialsId

  • robot

  • 镜像仓库地址

如何查找credentialsId:

图片图片

如何查找robot:

图片图片

Java Pipeline:

pipeline {
    agentanytriggers {
        GenericTrigger(genericVariables:[[key:'ref',value:'$.ref'],//获取分支[key:'user_username',value:'$.user_username'],//获取自动构建用户名[key:'GitRepository',value:'$.project.git_http_url'],//获取gitlab ssh项目地址[key:'project',value:'$.project.name'],//获取项目名称[key:'repository',value:'$.repository.name'],],token:"$JOB_NAME",causeString:'Triggered on $branch',printContributedVariables:true,printPostContent:true,silentResponse:false,)}
    environment {// pipeline配置路径pipeline_dir="/var/lib/jenkins/workspace/pipeline"// 项目版本Tag=sh(script:'echo "${ref}" | awk -F"/" \'{print $3}\'',returnStdout:true).trim()// 项目名称Project_Name="${project}"// 上一次版本Revsion_Prod=''//Depolyment名称DeploymentName=''// 生产名称空间Namespace_Prod=''// 灰度模式GrayHeaderMode=''// 灰度Depolyment名称GrayDeploymentName=''// 灰度Service名称GrayServiceName=''// 灰度Ingress名称GrayIngressName=''// 是否灰度GrayEnable='yes'}
    options {// 表示保留10次构建历史buildDiscarder(logRotator(numToKeepStr:'10'))}
    stages {
        stage('Pull Code'){// 拉取代码steps {
                checkout([$class:'GitSCM',branches:[[name:"$ref"]],doGenerateSubmoduleConfigurations:false,extensions:[],userRemoteConfigs:[[credentialsId:'ac66550d-6999-485c-af3a-7e6189f765f0',url:"$GitRepository"]]])script{
                    currentBuild.displayName="#${BUILD_NUMBER} - ${Project_Name} - ${Tag}"}
            }
        }//     // 代码构建//   stage('Build Code') {//         steps {//              sh '/application/maven/bin/mvn -f pom.xml -s settings.xml clean package -DskipTests'//         }//     }// 镜像构建stage('Build Image'){
            steps {
                sh'''
                /usr/bin/docker build -t uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag .
                /usr/bin/docker push  uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag
                '''}
            post {
                success {
                    wrap([$class:'BuildUser']){
                        lark(robot:"9f7c94cd-491e-4309-83b4-9290d01fc285",type:"CARD",title:"📢  Jenkins 镜像构建成功",text:["📋 **任务名称**:[${JOB_NAME}](${JOB_URL})","🔢 **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})","🌟 **构建状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: uhub.service.ucloud.cn/kubesre/$Project_Name","🕐 **构建用时**: ${currentBuild.duration} ms","👤 **执  行 者**: ${env.BUILD_USER}","<at id=all></at>"],buttons:[[title:"更改记录",url:"${BUILD_URL}changes"],[title:"控制台",type:"danger",url:"${BUILD_URL}console"]])}
                }
            }
        }
        stage('DeployDev'){
            steps {
                echo"部署开发环境"script {
                    def userInput=input(message:'确定要发布到DEV环境吗?',parameters:[choice(name:'操作',choices:['发布','跳过'])],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')if(userInput['操作']=='发布'){
                        echo"部署Dev环境开始"sh'''
                        echo $pipeline_dir
                        echo "打印编排文件详细信息"
                        if [ -e "$pipeline_dir/dev/$Project_Name/deployment.yml" ]; then
                        cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                        cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/dev/$Project_Name/service.yml" ]; then
                        cat $pipeline_dir/dev/$Project_Name/service.yml
                        cat $pipeline_dir/dev/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/dev/$Project_Name/ingress.yml" ]; then
                        cat $pipeline_dir/dev/$Project_Name/ingress.yml 
                        cat $pipeline_dir/dev/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''}else{
                        echo"不发布"}
                }
            }
        } 
        stage('DeployUat'){
            steps {
                echo"部署测试环境"script {
                    def userInput=input(message:'确定要发布到UAT环境吗?',parameters:[choice(name:'操作',choices:['发布','跳过'])],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')if(userInput['操作']=='发布'){
                        echo"发布"sh'''
                        echo $pipeline_dir
                        echo "打印编排文件详细信息"

                        if [ -e "$pipeline_dir/uat/$Project_Name/deployment.yml" ]; then
                        cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                        cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/uat/$Project_Name/service.yml" ]; then
                        cat $pipeline_dir/uat/$Project_Name/service.yml
                        cat $pipeline_dir/uat/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/uat/$Project_Name/ingress.yml" ]; then
                        cat $pipeline_dir/uat/$Project_Name/ingress.yml 
                        cat $pipeline_dir/uat/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''}else{
                        echo"不发布"}
                }
            }
        } 
        stage('DeployGray'){
            steps {
                echo"部署灰度环境"script {
                    def GraysMode=input(message:'确定要灰度验证吗?',parameters:[choice(name:'operation',choices:['基于权重灰度','基于请求头灰度','跳过'])],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')if(GraysMode['operation']=='基于权重灰度'){
                        def WeightMode=input(message:'请输入权重比例!',parameters:[string(name:'workload_weight',defaultValue:'',description:''),string(name:'grayload_weight',defaultValue:'',description:'')],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')sh"""
                        echo $pipeline_dir
                        echo "打印编排文件详细信息"
                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" 
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        echo "配置权重"

                        echo ${WeightMode['grayload_weight']}
                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed  "s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g" 
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed  "s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        """}if(GraysMode['operation']=='基于请求头灰度'){
                        GrayHeaderMode=input(message:'请输入请求头!',parameters:[string(name:'header_key',defaultValue:'',description:''),string(name:'header_value',defaultValue:'',description:'')],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')sh"""
                        echo ${GrayHeaderMode['header_value']}
                        echo $pipeline_dir
                        echo "打印编排文件详细信息"

                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" 
                        cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        echo "配置请求头"
                        echo ${GrayHeaderMode['header_key']}
                        echo ${GrayHeaderMode['header_value']}

                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress-gray-header.yml" ]; then
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed  "s/header-key/${GrayHeaderMode['header_key']}/g" | sed  "s/header-value/${GrayHeaderMode['header_value']}/g"
                        cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed  "s/header-value/${GrayHeaderMode['header_key']}/g" | sed  "s/header-value/${GrayHeaderMode['header_value']}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        """}// 默认模式为yes,如果跳过为noif(GraysMode['operation']=='跳过'){
                        GrayEnable='no'}
                }
            }
        } 
        stage('DeployProd'){
            steps {
                echo"部署生产环境"script {
                    def userInput=input(message:'确定要发布到生产环境吗?',parameters:[choice(name:'操作',choices:['发布','跳过'])],ok:'确定',submitter:'test',submitterParameter:'APPROVER')if(userInput['操作']=='发布'){
                        echo"发布"Namespace_Prod=sh(script:"cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep namespace | awk -F ':' '{print \$2}'",returnStdout:true).trim()DeploymentName=sh(script:"cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()Revsion_Prod=sh(script:"kubectl get deployment $DeploymentName -n ${Namespace_Prod} -o=jsnotallow='{.spec.template.spec.containers[*].image}' | awk -F ':' '{print \$NF}'",returnStdout:true).trim()GrayDeploymentName=sh(script:"cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()GrayServiceName=sh(script:"cat $pipeline_dir/prod/$Project_Name/service-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()GrayIngressName=sh(script:"cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()sh'''
                        echo $pipeline_dir
                        echo "开始部署生产环境"
                        echo "打印编排文件详细信息"

                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/service.yml" ]; then
                           cat $pipeline_dir/prod/$Project_Name/service.yml
                           cat $pipeline_dir/prod/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''if(GrayEnable=='yes'){
                            sh"""
                        kubectl delete deployment ${GrayDeploymentName} -n ${Namespace_Prod}
                        kubectl delete service  ${GrayServiceName} -n ${Namespace_Prod}
                        kubectl delete ingress  ${GrayIngressName} -n ${Namespace_Prod}
                        """}

                    }else{
                        echo"不发布"}
                }
            }
            post {
                success {
                    wrap([$class:'BuildUser']){
                    lark(robot:"9f7c94cd-491e-4309-83b4-9290d01fc285",type:"CARD",title:"📢  Jenkins 应用发布成功",text:["😋 **应用名称**:[${JOB_NAME}](${JOB_URL})","😜 **应用环境**:Prod","🤪 **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})","😘 **发布状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: harbor.kubesre.com:8443/kubesre/$Project_Name","😝 **执  行 者**: ${env.BUILD_USER}","<at id=all></at>"],buttons:[[title:"更改记录",url:"${BUILD_URL}changes"],[title:"控制台",type:"danger",url:"${BUILD_URL}console"]])}
                }
            }
        } 

        stage('RollBack'){
            steps {
                echo"版本回滚操作"script {
                  defRollBack=input(message:'确定要执行回滚操作吗?',parameters:[choice(name:'是否回滚',choices:['是','否']),string(name:'回滚版本',defaultValue: Revsion_Prod,description:'默认上一个版本')],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')if(RollBack['是否回滚']=='是'){
                        echo"版本回滚成功"echoRollBack['回滚版本']sh"""
                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${RollBack['回滚版本']}/g"
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${RollBack['回滚版本']}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        """}else{
                         echo"放弃版本回滚"echoRollBack['回滚版本']}
            }
         }
        }
    }
}

Vue Pipeline:

pipeline {
    agentanytriggers {
            GenericTrigger(genericVariables:[[key:'ref',value:'$.ref'],//获取分支[key:'user_username',value:'$.user_username'],//获取自动构建用户名[key:'GitRepository',value:'$.project.git_http_url'],//获取gitlab ssh项目地址[key:'project',value:'$.project.name'],//获取项目名称[key:'repository',value:'$.repository.name'],],token:"$JOB_NAME",causeString:'Triggered on $branch',printContributedVariables:true,printPostContent:true,silentResponse:false,)}
    environment {// pipeline配置路径pipeline_dir="/var/lib/jenkins/workspace/pipeline"// 项目版本Tag=sh(script:'echo "${ref}" | awk -F"/" \'{print $3}\'',returnStdout:true).trim()// 项目名称Project_Name="${project}"// 上一次版本Revsion_Prod=''//Depolyment名称DeploymentName=''// 生产名称空间Namespace_Prod=''// 灰度模式GrayHeaderMode=''// 灰度Depolyment名称GrayDeploymentName=''// 灰度Service名称GrayServiceName=''// 灰度Ingress名称GrayIngressName=''// 是否灰度GrayEnable='yes'}
    options {// 表示保留10次构建历史buildDiscarder(logRotator(numToKeepStr:'10'))}
    stages {
        stage('Pull Code'){// 拉取代码steps {
                checkout([$class:'GitSCM',branches:[[name:"$ref"]],doGenerateSubmoduleConfigurations:false,extensions:[],userRemoteConfigs:[[credentialsId:'ac66550d-6999-485c-af3a-7e6189f765f0',url:"$GitRepository"]]])script{
                    currentBuild.displayName="#${BUILD_NUMBER} - ${Project_Name} - ${Tag}"}
            }
        }//     // 代码构建//   stage('Build Code') {//         steps {//              sh '/application/maven/bin/mvn -f pom.xml -s settings.xml clean package -DskipTests'//         }//     }// 镜像构建stage('Build Image'){
            steps {
                 sh'''
                   /usr/bin/docker build -t uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag .
                   /usr/bin/docker push  uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag
                 '''}
            post {
                success {
                    wrap([$class:'BuildUser']){
                    lark(robot:"9f7c94cd-491e-4309-83b4-9290d01fc285",type:"CARD",title:"📢  Jenkins 镜像构建成功",text:["📋 **任务名称**:[${JOB_NAME}](${JOB_URL})","🔢 **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})","🌟 **构建状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: uhub.service.ucloud.cn/kubesre/$Project_Name","🕐 **构建用时**: ${currentBuild.duration} ms","👤 **执  行 者**: ${env.BUILD_USER}","<at id=all></at>"],buttons:[[title:"更改记录",url:"${BUILD_URL}changes"],[title:"控制台",type:"danger",url:"${BUILD_URL}console"]])}
                }
            }
        }
        stage('DeployDev'){
            steps {
                echo"部署开发环境"script {
                    def userInput=input(message:'确定要发布到DEV环境吗?',parameters:[choice(name:'操作',choices:['发布','跳过'])],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')if(userInput['操作']=='发布'){
                        echo"部署Dev环境开始"sh'''
                        echo $pipeline_dir
                        echo "打印编排文件详细信息"
                        if [ -e "$pipeline_dir/dev/$Project_Name/deployment.yml" ]; then
                            cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                            cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        
                        if [ -e "$pipeline_dir/dev/$Project_Name/service.yml" ]; then
                            cat $pipeline_dir/dev/$Project_Name/service.yml
                            cat $pipeline_dir/dev/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/dev/$Project_Name/ingress.yml" ]; then
                            cat $pipeline_dir/dev/$Project_Name/ingress.yml 
                            cat $pipeline_dir/dev/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''}else{
                        echo"不发布"}
                }
            }
        } 
        stage('DeployUat'){
            steps {
                echo"部署测试环境"script {
                    def userInput=input(message:'确定要发布到UAT环境吗?',parameters:[choice(name:'操作',choices:['发布','跳过'])],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')if(userInput['操作']=='发布'){
                        echo"发布"sh'''
                        echo $pipeline_dir
                        echo "打印编排文件详细信息"

                        if [ -e "$pipeline_dir/uat/$Project_Name/deployment.yml" ]; then
                            cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                            cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi
                        
                        if [ -e "$pipeline_dir/uat/$Project_Name/service.yml" ]; then
                            cat $pipeline_dir/uat/$Project_Name/service.yml
                            cat $pipeline_dir/uat/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/uat/$Project_Name/ingress.yml" ]; then
                            cat $pipeline_dir/uat/$Project_Name/ingress.yml 
                            cat $pipeline_dir/uat/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''}else{
                        echo"不发布"}
                }
            }
        } 
        stage('DeployProd'){
            steps {
                echo"部署生产环境"script {
                    def userInput=input(message:'确定要发布到生产环境吗?',parameters:[choice(name:'操作',choices:['发布','跳过'])],ok:'确定',submitter:'test',submitterParameter:'APPROVER')if(userInput['操作']=='发布'){
                        echo"发布"Namespace_Prod=sh(script:"cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep namespace | awk -F ':' '{print \$2}'",returnStdout:true).trim()DeploymentName=sh(script:"cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()Revsion_Prod=sh(script:"kubectl get deployment $DeploymentName -n ${Namespace_Prod} -o=jsnotallow='{.spec.template.spec.containers[*].image}' | awk -F ':' '{print \$NF}'",returnStdout:true).trim()GrayDeploymentName=sh(script:"cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()GrayServiceName=sh(script:"cat $pipeline_dir/prod/$Project_Name/service-gray.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()GrayIngressName=sh(script:"cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | grep name: |  head -n 1 | awk -F ':' '{print \$2}'",returnStdout:true).trim()sh'''
                        echo $pipeline_dir
                        echo "开始部署生产环境"
                        echo "打印编排文件详细信息"

                        if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g"
                          cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed  "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/service.yml" ]; then
                           cat $pipeline_dir/prod/$Project_Name/service.yml
                           cat $pipeline_dir/prod/$Project_Name/service.yml  | /usr/bin/kubectl apply -f  -
                        fi

                        if [ -e "$pipeline_dir/prod/$Project_Name/ingress.yml" ]; then
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml
                          cat $pipeline_dir/prod/$Project_Name/ingress.yml  | /usr/bin/kubectl apply -f  -
                        fi
                        '''}else{
                        echo"不发布"}
                }
            }
            post {
                success {
                    wrap([$class:'BuildUser']){
                    lark(robot:"9f7c94cd-491e-4309-83b4-9290d01fc285",type:"CARD",title:"📢  Jenkins 应用发布成功",text:["😋 **应用名称**:[${JOB_NAME}](${JOB_URL})","😜 **应用环境**:Prod","🤪 **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})","😘 **发布状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: harbor.kubesre.com:8443/kubesre/$Project_Name","😝 **执  行 者**: ${env.BUILD_USER}","<at id=all></at>"],buttons:[[title:"更改记录",url:"${BUILD_URL}changes"],[title:"控制台",type:"danger",url:"${BUILD_URL}console"]])}
                }
            }
        } 
    }
}

配置Jenkins

依赖的组件(自行安装):

  • Generic Webhook Trigger

  • Pipeline(所有以Pipeline开头的组件)

  • build user vars

  • Blue Ocean

  • Lark Notice(通过上传文件的方式安装)https://721806280.github.io/lark-notice-plugin-doc/

配置Lark Notice:

Lark Notice Plugin 是一个用于 Jenkins 的构建通知插件,可以将 Jenkins构建过程以及结果通知推送到 Lark、飞书、钉钉 协作平台。 可配置多个的通知时机,包括 构建启动时、构建中断、构建失败、构建成功时、构建不稳定 等。 支持多种不同类型的消息,包括 文本消息、图片消息, 群名片消息、富文本消息、卡片消息; 同时该插件还提供了自定义模板和变量的功能,使您能够根据自己的需求来定制通知消息的内容和格式。(本次案例是基于飞书进行验证)

准备工作,在飞书群新建一个机器人(https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot):

在飞书群,点击设置:

图片图片

然后点击群机器人:

图片图片

在飞书群,选择添加机器人

图片图片

填写相应配置信息并点击保存:

图片图片

图片图片

选择系统管理-Lark Notice:

图片图片

通知时机全部勾选:

图片图片

配置机器人信息并保存

图片图片

配置Java Pipeline

新建任务:

图片图片

填写任务名称,并选择流水线,点击确定:

图片图片

配置Pipeline SCM:

图片图片

修改脚本路径,点击确定:

图片图片

点击构建让配置生效:

图片图片

其他Java项目配置都一样!

配置Vue Pipeline

新建任务:

图片图片

填写任务名称,选择流水线:

图片图片

配置Pipeline SCM:

图片图片

修改脚本路径,点击确定:

图片图片

点击构建让配置生效:

图片图片

其他Vue项目配置都一样!

配置Gitlab Webhook

进入项目,选择webhook:

图片图片

选择添加Webhook:

图片图片

配置webhook URL,Token以Job Name:

图片图片

勾选标签推送事件,也就是说只有标签推送事件才会触发流水线:

图片图片

到此为止,Webook已配置完毕!所有项目配置都一样。

触发验证

触发Java Pipeline:

进入标签管理:

图片图片

新建标签:

图片图片

填写信息并点击创建标签(此标签名称也是容器镜像的Tag):

图片图片

进入Jenkins可以看到Gateway Pipeline已经触发了:

图片图片

图片图片

选择发布,并点击确定,将新版本发布到Dev环境:

图片图片

选择发布,并点击确定,将新版本发布到Uat环境:

图片图片

选择对应的灰度发布方式或者跳过:

图片图片

选择发布,并点击确定,将新版本发布到Prod环境:

图片图片

也可以回滚,默认是上一个版本也可修改成想要回滚到的版本:

图片图片

触发 Vue流水线:

进入标签管理:

图片图片

创建标签:

图片图片

填写信息并点击创建标签(此标签名称也是容器镜像的Tag):

图片图片

图片图片

进入Jenkins可以看到ruoyi-ui Pipeline已经触发了:

图片图片

选择发布,并点击确定,将新版本发布到Dev环境:

图片图片

选择发布,并点击确定,将新版本发布到Uat环境:

图片图片

选择发布,并点击确定,将新版本发布到Prod环境:

图片图片

构建通知

图片图片

12    2024-05-20 11:23:18    企业级 CI/CD Jenkins