• Jenkins流水线部署模板,编译、部署、关闭、回滚流水线脚本


    1、Pipeline和Jenkinsfile

    在这里插入图片描述

    项目部署使用Pipeline的Jenkinsfile脚本代码方式会比基于界面配置好处:

    • 可以使用Git/SVN之类的版本工具把构建脚本也管理和控制;
    • 支持多人协作,也可以对脚本进行代码审核;
    • 构建脚本可以重用;

    使用脚本的方式唯一缺点就是脚本构建相对界面配置会稍有一点难度,需要pipeline语法,pipeline支持groovy代码,可以基于groovy实现功能更强大的操作。

    2、可以重用的构建脚本

    这个构建脚本模板,定义了编译、部署、关闭、回滚操作,适用于大多数java项目,支持多服务器部署,可用于生产环境。
    对于大多数项目,我们都是在安装Jenkins的这台服务器,完成编译和打包工具和制品管理,而在部署的服务器上,一般只安装java运行环境。对于大多公司可以使用Jenkins自带的archiveArtifactsa插件来对制品(部署文件、安装包等)管理,归档制品备份,回滚操作中可以直接使用归档的旧版本包,

    2.1、配置多版本JDK

    可以根据项目需要,配置多个版本的JAVA_HOME,比如本文是同时了OpenJDK8和OpenJDK11,构建时,可以通过在脚本中工具代码块中配置

    tools {
    	jdk 'JAVA_HOME8'  # 指定编译用的jdk版本
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    2.2、配置Maven

    同样可以通过在脚本中工具代码块中配置:

    tools {
         maven 'MAVEN_HOME'
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    2.3、远程服务器操作插件Publish Over SSH

    可以在Linux shell中通过scp命令上传文件,ssh命令连接远程服务器操作脚本,参考代码如下,但是使用Publish Over SSH插件会更方便
    Pipeline通过Linux shell上传文件和远程服务命令,注意需要先配置远程服务器免密登录。

    stage('部署') {
        steps {
            script{
                sh "scp target/JApiTest-1.0.0_56.zip root@192.168.28.129:$upload_dir/JApiTest-1.0.0_56.zip"
                sh "ssh -tt root@192.168.28.129 << remotessh
                    unzip -oq $upload_dir/${app_name}.zip -d ${deploy_dir}/${app_name}
                    cd ${deploy_dir}/${app_name}
                    chmod 0755 ./server.sh
                    ./server.sh restart
                    exit
                    remotessh"
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    不推荐上边直接在Linux Shell命令上传和远程执行命令,建议使用Publish Over SSH或SSH Pipeline Steps的方式更好。
    Publish Over SSH配置远程服务器连接信息,这里配置了两台部署服务器的连接信息。可以使用帐号密码的方式远程连接,也可以免密的方式远程连接。
    在这里插入图片描述

    2.4、创建Git帐号密码认证信息

    在这里插入图片描述

    2.5、创建PipeLine任务

    在这里插入图片描述
    创建的任务中,只需要在界面上配置Jenkinsfile文件所在的git仓库和在仓库中的具体目录就行了,其它信息不需要配置。Jenkinsfile代码版本化,更适合项目管理的需要,有能力公司也可以对Jenkinsfile代码进行评审。
    在这里插入图片描述

    2.6、PipeLineJenkinsfile脚本代码

    完成2.1-2.5配置,就可以在具体的流水线代码上使用这些配置了,为了获取pom.xml文件中的版本,也使用groovy代码写了个version(file)获取版本号的方法供脚本调用。完整的流水线代码如下:

    /**
    * 获取maven项目版本号
    */
    def version(file){
        def version = ''
        if(fileExists(file)){
            def data = readFile(file: file)
            def lines = data.readLines()
            def isProject = false;
            def isParent = false;
            for (line in lines) {
                if(line.indexOf(") > -1){
                    isProject = true
                }
                if(isProject == true && line.indexOf("") > -1){
                    def start = line.indexOf("");
                    def end = line.indexOf("");
                    version = line.substring(start + 9,end);
                    break;
                }
            }
        }
        return version
    }
    
    def build(){
        checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [],userRemoteConfigs:[[
                                                                                                            credentialsId: 'gitee', url: 'https://gitee.com/penngo/XXJApiTest.git']]])
        app_version =version(source_dir + '/pom.xml')
        env.app_name_version ="${app_name}-${app_version}"
        sh("""
            java -version
            mvn clean package
        """.replace("    ", ""))
        sh([
                "cd ${source_dir}/target",
                "cp -f ../server.sh server.sh",
                "zip -q -r ${app_name_version}_${BUILD_ID}.zip ${app_name_version}.war server.sh"
        ].join("\n"))
    }
    
    def deploy(servers, build_id_temp){
        def build_id = build_id_temp == '' ? readFile(file: "build_id.txt") : build_id_temp;
        sh("""
            cp -f ${WORKSPACE}/../../jobs/${app_name}/builds/${build_id}/archive/target/*_${build_id}.zip ${app_name}.zip
        """.replace("\t",""));
    
        for(ip in servers.tokenize(",")){
            echo "=====ssh_ip:" + ip
            sshPublisher(
                    publishers: [
                            sshPublisherDesc(
                                    configName: "${ip}",
                                    transfers: [
                                            sshTransfer(cleanRemote: false,
                                                    excludes: '',
                                                    execCommand: """
                                                            unzip -oq $upload_dir/${app_name}.zip -d ${deploy_dir}/${app_name}
                                                            cd ${deploy_dir}/${app_name}
                                                            chmod 0755 ./server.sh
                                                            ./server.sh restart
                                                    """.replace('    ',''),
                                                    execTimeout: 120000,
                                                    flatten: false,
                                                    makeEmptyDirs: false,
                                                    noDefaultExcludes: false,
                                                    patternSeparator: '[, ]+',
                                                    remoteDirectory: "${upload_dir}",
                                                    remoteDirectorySDF: false,
                                                    removePrefix: "",
                                                    sourceFiles: "${app_name}.zip")
                                    ],
                                    usePromotionTimestamp: false,
                                    useWorkspaceInPromotion: false,
                                    verbose: true)
                    ])
        }
    }
    
    def rollback(servers, build_number){
        deploy(servers, build_number)
    }
    
    pipeline {
        agent any
        tools {
            maven 'MAVEN_HOME'
            jdk 'JAVA_HOME8'
        }
        options {
            buildDiscarder(logRotator(numToKeepStr: "10"))
        }
        environment{
            source_dir="${WORKSPACE}/JApiTest"
            jenkinsfile_dir="${WORKSPACE}"
            jenkinsfile_job_dir="${WORKSPACE}/../../JApiTest"
            app_version=version(source_dir + '/pom.xml')
            app_name="JApiTest"
            app_name_version="${app_name}-${app_version}"
            deploy_dir="/data/app"
            upload_dir="/data/upload"
    
        }
        parameters {
            choice(name: 'app', choices:['JApiTest'],description: '应用(请选择需要操作的应用:JApiTest)')
            choice(name: 'select', choices:[
                    '请选择',
                    '编译',
                    '部署',
                    '回滚',
            ],description: '操作(请选择操作:编译、部署、回滚)')
    
            extendedChoice(description: '服务器', value: '192.168.28.129,192.168.28.132',
                descriptionPropertyValue: '192.168.28.129(测试),192.168.28.132(正式)',
                multiSelectDelimiter: ',', name: 'servers', quoteValue: false, saveJSONParameterToFile: false, type: 'PT_CHECKBOX', visibleItemCount: 5)
            string(
                    description: """回滚版本号""",
                    name: 'roll_number',
                    trim: true
            )
        }
    
        stages {
            stage('编译') {
                when {
                    expression { params.select.trim() == "编译" }
                }
                steps {
                    dir("${source_dir}"){
                        script {
                            build()
                        }
                    }
                    writeFile(file: "build_id.txt", text: "${BUILD_ID}")
                }
            }
    
            stage('部署') {
                when {
                    expression { "${params.select}".trim() == "部署" }
                }
                steps {
                    script {
                        deploy(params.servers, '')
                    }
                }
            }
    
            stage('回滚') {
                when {
                    expression { "${params.select}".trim() == '回滚' }
                }
                steps {
                    script {
                        rollback(params.servers, params.roll_number)
                    }
                }
            }
        }
        post {
            success{
                script {
                    if(params.select.trim() == "编译"){
                        dir("${source_dir}"){
                            archiveArtifacts artifacts: "target/${app_name_version}_${BUILD_ID}.zip", fingerprint: false
                        }
                    }
                }
    
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172

    配合运行的Linux脚本server.sh

    #!/bin/bash -il
    export JAVA_HOME=/usr/lib/jvm/bellsoft-java8-full.x86_64
    export PATH=$JAVA_HOME/bin:$PATH
    java -version
    
    APP_NAME="JApiTest"
    JAR_NAME="JApiTest-1.0.0.jar"
    JAVA_OPTS="-Xmx2G -XX:+UseG1GC"
    
    start() {
    	pid=`ps -ef | grep "$JAR_NAME" | grep -v grep | awk '{print $2}'`
    	if [ -z $pid ]; then
    	  export JENKINS_NODE_COOKIE=dontKillMe
        nohup java $JAVA_OPTS -jar $JAR_NAME 2>&1 &
        sleep 3
        pid=`ps -ef | grep "$JAR_NAME" | grep -v grep | awk '{print $2}'`
        if [ -z $pid ]; then
          echo ".....service ${JAR_NAME} is run error....."
        else
          echo ""
          echo ".....Service ${JAR_NAME} is starting!pid=${pid}....."
          echo "........................Start successfully!........................."
        fi
    	else
    		echo ""
    		echo "Service ${JAR_NAME} is already running,it's pid = ${pid}"
    		echo ""
    	fi
    }
    
    stop() {
    	pid=`ps -ef | grep "$JAR_NAME" | grep -v grep | awk '{print $2}'`
    	if [ -z $pid ]; then
        echo "Service ${JAR_NAME} is not running!"
    	else
    		kill -9 $pid
    		echo "Service stop successfully!pid:${pid} been killed"
    	fi
    }
    
    restart() {
    	echo ""
    	echo ".............................Restarting.............................."
    	stop
    	start
    	echo "....................Restart successfully!..........................."
    }
    
    if [ ! -n "$1" ] ;then
     echo "Usage: $0 {start|stop|restart}"
    elif [ $1 = "start" ];then
     start
    elif [ $1 = "stop" ];then
     stop
    elif [ $1 = "restart" ];then
     restart
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    2.7、构建效果

    使用Pipeline脚本方式部署,必须先执行一次后,才能出现2.7.1参数可选界面,如果未执行过,默认是下边在界面:
    在这里插入图片描述

    2.71、编译

    在这里插入图片描述

    2.72、部署

    在这里插入图片描述

    2.73、回滚

    构建前需要填入有归档文件的构建任务号。
    在这里插入图片描述

    2.8、全部构建结果

    构建任务中有下载图标的都表示这个构建任务有归档文件。
    在这里插入图片描述

  • 相关阅读:
    三菱FX3U——ST编程定时器和计数器
    StoneDB for MySQL 5.7 版本发布
    【实操日记】使用 PyQt5 设计下载远程服务器日志文件程序
    你真的了解HTML中的JavaScript吗?
    lazarus创建自定义组件
    继copilot之后,又一款免费帮你写代码的插件
    设计模式(结构型设计模式——桥接模式)
    使用iPXE自动化安装ubuntu22.04
    【数据结构】线段树
    Springboot 实践(21)服务熔断机制
  • 原文地址:https://blog.csdn.net/penngo/article/details/126687946