一、 发布流程分析

  1. 拉取代码
  2. NPM编译打包
  3. 代码质量检查 
  4. 容器镜像封装
  5. 推送镜像到镜像仓库
  6. 发布到k8s集群平台

二、 Jenkins 配置如下:

三、 Pipeline1 脚本如下 

pipeline {
    agent { 
        label 'master' 
    }
    // 构建参数
    parameters {
        gitParameter(branchFilter: 'origin.*/(master|feature.*|hotfix.*|release.*)', name: 'BRANCH_TAG', defaultValue: 'master',selectedValue: 'DEFAULT', type: 'PT_BRANCH_TAG')
        choice(name: '请选择操作', choices: [ "拉取代码-编译",'-----' ,"拉取代码-编译-单元测试", '-----',"重启服务",'-----' ,"停止服务",'-----',"扩容服务",'-----',"回滚服务"])
        // string(name: 'version_id', defaultValue: '1', description: '构建生产历史版本号,1表示回滚到上一个版本,2表示回滚上两个版本')
        string(name: 'pod_num', defaultValue: '2', description: '默认pod数据数量2,输入扩容副本数量')
    }
 

    environment {

        // 参数选项
        ACTION = "${params.请选择操作}"

        // 项目名称 
        PROJECT_NAME= "cnh"
        // 指定代码所在GitLab仓库地址
        // GITLAB_URL = "ssh://git@gitlab.iotcloud.wl:2222/xxx.git"
        GITLAB_URL = "https://xxx.git"

        // 指定GitLab仓库的分支
        // GITLAB_BRANCH = "master"
        
        // GitLab拉取代码的用户认证文件ID
        // GITLAB_SERCET = "11d180dd-1afc-4e40-952a-d0d9db29ffde"
        GITLAB_SERCET = "f07ce25c-490b-475f-adce-1dd19d13149f"
        // docker镜像仓库地址
        // DOCKER_REGISTRY_URL = "172.16.0.100:5000" 
        // DOCKER_LOCAL_REGISTRY_URL = "172.16.0.100:8090"
        // DOCKER_REGISTRY_LOGIN_USER = "docker"
        // DOCKER_REGISTRY_LOGIN_PASSWORD= "CNH@Docker2025"
        DOCKER_REGISTRY_URL = "registry.iotcloud.wl" 
        DOCKER_LOCAL_REGISTRY_URL = "registry.iotcloud.wl"
        DOCKER_REGISTRY_LOGIN_USER = "docker"
        DOCKER_REGISTRY_LOGIN_PASSWORD= "CNH@Docker2025"
        DOCKER_JDK_BASE_IMAGE = "${DOCKER_LOCAL_REGISTRY_URL}/cnmirrors/openjdk:8u201-alpine-tools"

        // // 指定Dockerfile文件所在路径
        DOCKER_BUILD_FILE = "cnh-iconnect-system/${JOB_BASE_NAME}/Dockerfile"
        // // 指定构建Docker容器时所在路径
        DOCKER_BUILD_PATH = "cnh-iconnect-system/${JOB_BASE_NAME}"
        // K8S命令的路径
        K8S_COMMAND_PATH = "${JENKINS_HOME}/tools/kubectl"
        // NPM_COMMAND = "${JENKINS_HOME}/node-v14.15.0-linux-x64/bin/npm"
        // MVN_COMMAND = "/var/jenkins_home/maven"
        // 配置K8S执行命令的环境
        K8S_CONFIG_SERCET = "k8s-jenkins-prod"
        // 配置K8S服务开放容器的端口
        K8S_PORT = "8080"
        // K8s 名称空间 
        K8S_NAMESPACE_NAME = "cnh"
        // 创建服务的复本数量
        K8S_POD_NUM = 2
        // RELEASE 发布标签
        // K8S_RELEASE = 'dev'
        // PROFILES_ACTIVE="sit"
        JVM_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0  -Xms512m -Xmn512M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M  -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45 -XX:ParallelGCThreads=2 -XX:ConcGCThreads=1 -Djava.security.egd=file:/dev/./urandom -Dapp=${JOB_BASE_NAME} -Dspring.profiles.active=prod"
        // 显示选择镜像版本号的数量
        DOCKER_NUM = 10
        // SonarQube Scanner
        scannerHome = "${JENKINS_HOME}/tools/sonar-scanner-4.3.0.2102"
    }

    // pipeline选项
    options {
        // 保存最近历史构建记录数量
        buildDiscarder(logRotator(numToKeepStr: '10'))
        // 禁止pipeline同时执行
        disableConcurrentBuilds()
        // 失败时重试总次数
        // retry(2)
        // pipeline执行时间过长,超过timeout时间就停止掉pipeline
        timeout(time: 30, unit: 'MINUTES') 
    }
        tools {
              maven "mvn";
              jdk "jdk8"
             
        } 
   stages {

        stage("清理工作空间") {
            steps {
                deleteDir() // 清理工作空间
            }
        }
        
        stage('GitLab代码拉取') {
            
            when {
                anyOf {
                        environment name: 'ACTION', value: '拉取代码-编译-单元测试';
                        environment name: 'ACTION', value: '拉取代码-编译'
                }
            }
            
            steps {
                script {
                // git branch: env.GITLAB_BRANCH, credentialsId: env.GITLAB_SERCET, url: env.GITLAB_URL
                checkout([$class: 'GitSCM', 
                    branches: [[name: "${params.BRANCH_TAG}"]], 
                    doGenerateSubmoduleConfigurations: false, 
                    extensions: [], submoduleCfg: [], 
                    userRemoteConfigs: [[credentialsId: env.GITLAB_SERCET, url: env.GITLAB_URL]]])  

                    // 获取最新的标签
                    // def VERSION = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()"
                    // def VERSION = sh(script: 'git tag | sort -V | tail -n 1', returnStdout: true).trim()
                    // 获取当前分支的最近一次提交的哈希值
                    def commitHash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
                    // // 获取当前分支的名称
                    // // def branchName = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()
                    def branchName = "${params.BRANCH_TAG}"
                    echo "${params.BRANCH_TAG}"
      
                    // 将这些值设置为环境变量,以便在后续步骤中使用
                    env.COMMIT_HASH = commitHash
                    env.BRANCH_NAME = branchName
                    env.VERSION = "${COMMIT_HASH}"
                    // 根据分支名称设置不同的标签版本
                    def TAG_VERSION
                    if (branchName == 'master') {
                        TAG_VERSION = "stable-${VERSION}"
                    } else if (branchName ==~ /feature.+/) {
                        TAG_VERSION = "feature-${VERSION}"
                    } else if (branchName ==~ /hotfix.+/) {
                        TAG_VERSION = "hotfix-${VERSION}"
                    } else if (branchName ==~ /release.+/) {
                        TAG_VERSION = "release-${VERSION}"
                    } else {
                        TAG_VERSION = "custom-${VERSION}"
                    }
                    env.TAG_VERSION = TAG_VERSION

                    // 构建完整的版本号
                    def BUILD_NUMBER = env.BUILD_NUMBER
                    def BUILD_TIMESTAMP = sh(script: 'date "+%Y%m%d%H%M"', returnStdout: true).trim()
                    def FULL_VERSION = "${TAG_VERSION}-${BUILD_NUMBER}-${BUILD_TIMESTAMP}"

                    // 设置 Docker 镜像名称
                    DOCKER_IMAGE_NAME = "${DOCKER_REGISTRY_URL}/${PROJECT_NAME}/${JOB_BASE_NAME}:${FULL_VERSION}"
                    DOCKER_DEPLOY_IMAGE_NAME = "${DOCKER_LOCAL_REGISTRY_URL}/${PROJECT_NAME}/${JOB_BASE_NAME}:${FULL_VERSION}"
                    env.DOCKER_IMAGE_NAME = DOCKER_IMAGE_NAME
                    env.DOCKER_DEPLOY_IMAGE_NAME = DOCKER_DEPLOY_IMAGE_NAME

                    // 打印信息
                    // echo "Latest tag: ${env.TAG_VERSION}"
                    // echo "Commit hash: ${env.COMMIT_HASH}"
                    // echo "Branch name: ${env.BRANCH_NAME}"
                    echo "DEPLOY IMAGES NAME : ${DOCKER_DEPLOY_IMAGE_NAME}"
                }
            }
        }

          stage ('并行处理依赖项目') {
              
            when {
                    environment name: 'ACTION', value: '拉取代码-编译'
            }    

            parallel {

                stage("cdh-common依赖处理") { 
                  steps {
                    sh '''
                    cd cnh-common
                    mvn clean compile
                    mvn install -DskipTests=true 
                    '''
                }
            }
        }
    }
        stage('拉取代码-编译') { 

            when {
                    environment name: 'ACTION', value: '拉取代码-编译'
            }    

            tools {
                  maven "mvn";
                  jdk "jdk8"
            } 
            steps {
            sh '''
            cd cnh-iconnect-system/${JOB_BASE_NAME}
            mvn clean compile
            mvn -Pprod install -DskipTests=true  
            '''
          }
        }

        stage('拉取代码-编译-单元测试') { 

            when {
                    environment name: 'ACTION', value: '拉取代码-编译-单元测试'
            }    
            tools {
                  maven "mvn";
                  jdk "jdk8"
            } 
            steps {
            sh '''
            cd cnh-iconnect-system/${JOB_BASE_NAME}
            mvn clean compile
            mvn -Pprod install -DskipTests=true  
            mvn deploy
            '''
          }
        }

        // stage('代码检查') {

        //     when {
        //         anyOf {
        //                 environment name: 'ACTION', value: '拉取代码-编译-单元测试';
        //                 environment name: 'ACTION', value: '拉取代码-编译'
        //         }
        //     }
   
        //     tools {
        //           maven "mvn";
        //           jdk "jdk8"
        //         }
                
        //     steps {
        //         echo "启动sonar代码分析......"
        //         withSonarQubeEnv('sonar7.9') {
        //             //注意这里withSonarQubeEnv()中的参数要与之前SonarQube servers中Name的配置相同

        //             sh """
        //               cd cnh-iconnect-system/${JOB_BASE_NAME}
        //               ${scannerHome}/bin/sonar-scanner \
        //                     -Dsonar.projectKey=${JOB_BASE_NAME} \
        //                     -Dsonar.projectName=${JOB_BASE_NAME} \
        //                     -Dsonar.projectBaseDir=./ \
        //                     -Dproject.settings=../../sonar-project.properties
                                                        
        //             """
        //             // sh """
        //             //     cd ${JOB_BASE_NAME}
        //             //     mvn clean verify sonar:sonar 
        //             // """    
        //             // sh """
        //             //     cd ${JOB_BASE_NAME}
        //             //     mvn clean verify sonar:sonar \
        //             //     -Dsonar.core.codeCoveragePlugin=jacoco \
        //             //     -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco-merged-test-coverage-report/jacoco.xml
        //             // """
        //             //指定了maven插件,如果不指定使用最新的
        //             //   sh 'cd ${JOB_BASE_NAME}'
        //             //   sh "mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:3.6.0.1398:sonar "
        //             // sh """
        //             //   cd cnh-api-gateway/${JOB_BASE_NAME}
        //             //   ${scannerHome}/bin/sonar-scanner \
        //             //         -Dsonar.projectKey=${JOB_BASE_NAME} \
        //             //         -Dsonar.projectName=${JOB_BASE_NAME} \
        //             //         -Dsonar.sources=. \
        //             //         -Dsonar.sources=src \
        //             //         -Dsonar.exclusions=**/bean/**,**/vo/**,**/dto/**,**/po/**,**/test/**,**/**/*Test \
        //             //         -Dsonar.java.binaries=./target/classes \
        //             //         -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml \
        //             //         -Dsonar.issue.ignore.multicriteria.e1.ruleKey=java:S482 \
        //             //         -Dsonar.issue.ignore.multicriteria.e1.resourceKey=**/** \
        //             //         -Dsonar.issue.ignore.multicriteria.e2.ruleKey=java:S1214 \
        //             //         -Dsonar.issue.ignore.multicriteria.e2.resourceKey=**/** \
        //             //         -Dsonar.issue.ignore.multicriteria.e3.ruleKey=java:S125 \
        //             //         -Dsonar.issue.ignore.multicriteria.e3.resourceKey=**/** \
        //             //         -Dsonar.issue.ignore.multicriteria.e4.ruleKey=java:S1488 \
        //             //         -Dsonar.issue.ignore.multicriteria.e4.resourceKey=**/** 
                            
        //             // """
                        
        //         }    
        //     }
        // }

        stage('Docker镜像构建') {

            when {
                anyOf {
                        environment name: 'ACTION', value: '拉取代码-编译-单元测试';
                        environment name: 'ACTION', value: '拉取代码-编译'
                }
            }    

             steps {
                 sh """
                 echo "FROM ${DOCKER_JDK_BASE_IMAGE}" >${DOCKER_BUILD_FILE}
                 echo "COPY target/*.jar /app.jar" >>${DOCKER_BUILD_FILE}
                 echo "ENV SW_AGENT_NAME=${JOB_BASE_NAME}" >>${DOCKER_BUILD_FILE}
                 echo "ENV APP_NAME=${JOB_BASE_NAME}" >>${DOCKER_BUILD_FILE}
                 """
                 sh """
                  docker buildx build -t ${DOCKER_IMAGE_NAME}  --file=${DOCKER_BUILD_FILE} ${DOCKER_BUILD_PATH}
                  """
             }
         }

        stage('推送Docker镜像到镜像仓库') {

            when {
                anyOf {
                        environment name: 'ACTION', value: '拉取代码-编译-单元测试';
                        environment name: 'ACTION', value: '拉取代码-编译'
                }
            }

             steps {
                 sh """
                 echo "${DOCKER_REGISTRY_LOGIN_PASSWORD}" | docker login --username ${DOCKER_REGISTRY_LOGIN_USER} --password-stdin  ${DOCKER_REGISTRY_URL} 
                 docker push ${DOCKER_IMAGE_NAME}
                 docker rmi ${DOCKER_IMAGE_NAME}
                 """
             }
         }

        stage('重启K8S微服务') {

            when {
                    environment name: 'ACTION', value: '重启服务'
            }    

            steps {
                script {
                    withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
                        def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
                        env.RETURN_VALUME = "$RETURN_VALUME"
                        if (env.RETURN_VALUME != ''){
                          sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} rollout restart deployments ${JOB_BASE_NAME}'
                        } else {
                          sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
                          sh 'exit 1'
                        }
                    }
                }
            }
        }

        stage('停止K8S微服务') {

            when {
                    environment name: 'ACTION', value: '停止服务'
            }    

            steps {
                script {
                    withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
                        def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
                        env.RETURN_VALUME = "$RETURN_VALUME"
                        if (env.RETURN_VALUME != ''){
                           sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} scale --replicas=0 deployment ${JOB_BASE_NAME}'
                        } else {
                          sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
                          sh 'exit 1'
                        }
                    }
                }
            }
        }


        stage('扩容K8S微服务') {

            when {
                    environment name: 'ACTION', value: '扩容服务'
            }    

            steps {
                script {
                    withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
                        def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
                        env.RETURN_VALUME = "$RETURN_VALUME"
                        if (env.RETURN_VALUME != ''){
                           sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} scale --replicas=${pod_num} deployment ${JOB_BASE_NAME}'
                        } else {
                          sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
                          sh 'exit 1'
                        }
                    }
                }
            }
        }

        stage("回滚微服务") {
            when {
                    environment name: 'ACTION', value: '回滚服务'
            }   
            steps {
                script {
                    dir("${JENKINS_HOME}/shell"){
                        env.DOCKER_VERSIONS = sh(returnStdout: true, script: 'python3 pull_images_tag.py  ${DOCKER_LOCAL_REGISTRY_URL} ${JOB_BASE_NAME} ${PROJECT_NAME} ${DOCKER_NUM}')
                        println env.DOCKER_VERSIONS
                    }

                    env.RELEASE_SCOPE = input message: '选择要发布的版本号', ok: '继续!',
                            parameters: [choice(name: 'RELEASE_SCOPE', choices: env.DOCKER_VERSIONS, description: '在测试环境经过发布过的版本号')]
                     // 定义镜像       
                    env.ROLLBACK_IMAGE_NAME = "${DOCKER_REGISTRY_URL}/${PROJECT_NAME}/${JOB_BASE_NAME}:${RELEASE_SCOPE}"
                    println "${JOB_BASE_NAME} 回滚版本号为: ${env.RELEASE_SCOPE}"
                    println "${JOB_BASE_NAME} 镜像地址: ${env.ROLLBACK_IMAGE_NAME}"

                    withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
                        def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
                        env.RETURN_VALUME = "$RETURN_VALUME"
                        if (env.RETURN_VALUME != ''){
                           sh """
                             ${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} set image deployment ${JOB_BASE_NAME} ${JOB_BASE_NAME}=${ROLLBACK_IMAGE_NAME}
                        """
                        } else {
                          sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
                          sh 'exit 1'
                        }
                    }
                }
            }
        }

        stage('微服务部署到K8S') {
            when {
                anyOf {
                        environment name: 'ACTION', value: '拉取代码-编译-单元测试';
                        environment name: 'ACTION', value: '拉取代码-编译'
                }
            }
            steps {
                script {
                    withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
                        def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
                        env.RETURN_VALUME = "$RETURN_VALUME"
                        if (env.RETURN_VALUME == ''){
                            sh '${JENKINS_HOME}/yaml/k8s_build  ${JENKINS_HOME}/yaml/deployment_template-nacos-check.yaml ${JOB_BASE_NAME} --namespace ${K8S_NAMESPACE_NAME} --replicas=${K8S_POD_NUM}  --env JVM_OPTIONS="${JVM_OPTIONS}" --image-name ${PROJECT_NAME}/${JOB_BASE_NAME}:${BUILD_TIMESTAMP} --docker-registry ${DOCKER_LOCAL_REGISTRY_URL} --limit_memory 3Gi'
                            sh '${K8S_COMMAND_PATH} apply -f ${JOB_BASE_NAME}-dep.yaml'
                            // HPA
                            //sh '${JENKINS_HOME}/yaml/k8s_build  ${JENKINS_HOME}/yaml/template_autoscale.yaml ${JOB_BASE_NAME} --type hpa  --memory_scale 80 --min_replica 1 --max_replica 3'
                            //sh '${K8S_COMMAND_PATH} apply -f ${JOB_BASE_NAME}-hpa.yaml'
                            
                            // sh '''
                            //     ${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} run ${JOB_BASE_NAME}-${VERSION_NUM} --image=${DOCKER_IMAGE_NAME} --replicas=${K8S_POD_NUM} --labels="app=${JOB_BASE_NAME},version=${VERSION_NUM}" --overrides='{"spec": {"minReadySeconds": 120, "strategy": {"type": "RollingUpdate", "rollingUpdate": {"maxSurge": 1, "maxUnavailable": 0}}}}'
                            // '''
                        } else {
                          sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} set image deployment ${JOB_BASE_NAME} ${JOB_BASE_NAME}=${DOCKER_DEPLOY_IMAGE_NAME}'
                          sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} scale --replicas=${K8S_POD_NUM} deployment ${JOB_BASE_NAME}'
                        }
                        
                        def SVC_STATUS = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get service ${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
                        env.SVC_STATUS = "$SVC_STATUS"
                        if (env.SVC_STATUS == ''){
                            sh '${JENKINS_HOME}/yaml/k8s_build  ${JENKINS_HOME}/yaml/service_template.yaml ${JOB_BASE_NAME} --namespace ${K8S_NAMESPACE_NAME}  --type=service --port=${K8S_PORT} '
                            sh '${K8S_COMMAND_PATH} apply -f ${JOB_BASE_NAME}-svc.yaml'
                        //    sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} create service clusterip ${JOB_BASE_NAME} --tcp=${K8S_PORT1}:${K8S_PORT1} --tcp=${K8S_PORT2}:${K8S_PORT2}'
                        }
                    }
                }
            }
        }
    }
}

 四、 验证功能

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐