zoukankan      html  css  js  c++  java
  • 通过Jenins实现应用程序部署

    接着上一篇处理jenkins的问题,Jenkins调整好以后(主要包括docker和maven),准备把之前的一个.net项目通过jenkins发布。

    项目里面分5个子项目,server/receiver/push/attachment/web,之前用shell脚本做过一次编译封装,目前欠缺的主要是代码的下载、触发构建和部署。

    Jenkins构建简介

    Jenkins的执行过程叫pipeline,一个pipeline基本组成单元是stage,这个stage是一个逻辑的概念,当然可以把所有操作放在一个stage里,但是配置好不同的stage,比如下载,编译,部署等,在Jenkins构建界面上,可以看到具体项目的执行步骤,更友好一些。

    除了页面配置,还需要Jenkinsfile控制pipeline的过程。这个文件使用Groovy语言写的,分为声明式和脚本式两种语法。我这里用的是声明式的,其实脚本式的可能功能更强大。

    脚本的基本结构:

    pipeline {
          agent any
          environment {
                ......
          }
    
          stages {
                stage('1.') {
                      steps {
                            //执行动作
                            ......
                      }
                }
                stage('2.') {
                      steps {
                            //执行动作
                            ......
                      }
                }
                ......
          }
    }
    

    pipeline是固定结构,agent代表运行这个构建的agent的要求,这里可以指定标签,也可以指定跑一个docker镜像等等。

    之前尝试跑一个maven镜像,但是镜像里没有docker命令,没有办法在里面打包docker镜像(也可以二次打包maven镜像,但是实在有点麻烦),最终还是直接用了宿主的maven和docker。

    steps里执行的动作可以参考jenkins的官方教程,但是实在不怎么友好,我的大多数问题还是通过搜索引擎得到的。。。

    设计

    • 拉取代码:通过界面配置git地址和分支,我这里只用了master分支,实际是可以指定不同分支的
    • 构建:替换真正的编译脚本build.sh中的环境变量,在编译脚本中会根据Dockerfile构建并推送镜像
    • 部署:根据不同的环境,推送到相应的环境,然后通过docker-compoes更新部署镜像文件
    • 发送通知:成功通知或失败通知

    配置

    界面配置

    配置一个Jenkins项目,配置pipeline为从git拉取代码后,执行项目的Jenkinsfile文件完成后续的构建和部署。

    这里我配置了3个参数

    • BUILD_TYPE:决定构建哪个子项目,如果传0那么就构建所有子项目
    • BUILD_VER:这里我是用来控制发布到生产还是测试环境的
    • DEFAULT_RECIPIENTS:这其实是一个系统变量,但是可以被程序覆写,代表邮件接收人

    脚本配置

    整体脚本放在全文最后。

    Jenkinsfile的environment

    可以通过以下方法根据环境变量,设置另一个环境变量的值。

            DEPLOY_CONFIG = """${sh(
                    returnStdout: true,
                    script: 'if [ "${BUILD_ENV}" = "正式环境" ] ; then echo "ALIYUN_FORMAL"; else echo "ALIYUN_TEST"; fi'
                    ).trim()}"""
    

    这里碰到过两个问题

    • if里不支持正则匹配运算符~=,因为我的Jenkins跑在alpine里,默认的ash不支持。
    • 参数通过env.xxx或者params.xxx获取不到,这点比较奇怪,但是通过直接使用变量名却可以访问。
    执行脚本命令

    可以通过sh执行shell命令,比如我用到了一些sed替换环境变量。这里调用的实际就是jenkins安装环境里的shell处理的,我的shell用的是alpine的ash,所以比bash少一些功能,这也是中间碰到过的问题。

    //这是编译中一个替换编译脚本环境变量的命令
    sh "sed -i 's=JENKINS_VERSION_NO=${DOCKER_REGISTRY_IMAGE_TAG}=g' ${BUILD_SCRIPT}"
    
    部署

    这里用的是Publish Over SSH插件,需要先在Jenkins的配置文件里,创建一个SSH配置,其中包括了主机信息、用户、密码等。

                      sshPublisher(
                       continueOnError: false, failOnError: true,
                       publishers: [
                        sshPublisherDesc(
                         configName: "${DEPLOY_CONFIG}",
                         verbose: true,
                         transfers: [
                          sshTransfer(
                           sourceFiles: "${DEPLOY_SCRIPT}",
                           remoteDirectory: "${DEPLOY_REMOTE_PATH}",
                           execCommand: "cd ${DEPLOY_REMOTE_PATH} && /bin/bash ${DEPLOY_SCRIPT} && rm ${DEPLOY_SCRIPT}"
                          )
                         ])
                       ])
    

    ${DEPLOY_SCRIPT}是我的部署脚本,里面会触发docker重新下载镜像。
    exeCommand参数是脚本上传完成以后执行的命令,我这里切换到脚本目录,运行了脚本。

    发送通知邮件

    这个网上教程还是挺好找的,用Email Extension Plugin插件,同样需要在Jenkins web界面配置邮件基本信息。

    我用的qq邮箱,需要先去开启SMTP以及获得授权码。

    然后通过模板发送邮件就可以了。

    后记

    主要是在语法上碰到了一些问题,Jenkins相关的文档不是很好看,需要结合很多网上例子,才能达到自己的目的。

    完整脚本

    pipeline {
        agent any
    	
        environment {
            //modify for need
            DEBUG_JENKINS = 0
            DEPLOY_CONFIG = """${sh(
                    returnStdout: true,
                    script: 'if [ "${BUILD_ENV}" = "正式环境" ] ; then echo "ALIYUN_PRD"; else echo "ALIYUN_TEST"; fi'
                    ).trim()}"""
            DEPLOY_REMOTE_PATH = """${sh(
                    returnStdout: true,
                    script: 'if [ "${BUILD_ENV}" = "正式环境" ] ; then echo "swarm-deploy/formal"; else echo "swarm-deploy/test"; fi'
                    ).trim()}"""
    
            DOCKER_REGISTRY_HOST = 'registry.cn-beijing.aliyuncs.com'
            DOCKER_REGISTRY_HOST_VPC = 'registry-vpc.cn-beijing.aliyuncs.com'
            DOCKER_REGISTRY_REPO = "${DOCKER_REGISTRY_HOST}/trt-iot"
            DOCKER_REGISTRY_REPO_VPC = "${DOCKER_REGISTRY_HOST_VPC}/trt-iot"
            DOCKER_REGISTRY = credentials('DOCKER_REGISTRY_ALIYUN_TEST')
    
            BUILD_SCRIPT = 'build.sh'
    
            DEPLOY_FLAG = '_BUILD_FLAG'
            DEPLOY_IMAGE_PREFIX = '_IMAGE_PREFIX'
            DEPLOY_IMAGE_NAME = '_IMAGE_NAME'
    
            DEPLOY_SCRIPT = 'deploy.sh'
    
            VERSION_NO  = """${sh(
                    returnStdout: true,
                    script: 'version=`echo "${GIT_BRANCH}"|cut -d / -f 2`;echo "${version}_`date +%y%m%d`"'
                    ).trim()}"""
            DOCKER_REGISTRY_IMAGE_TAG = "${VERSION_NO}_${BUILD_ID}"
        }
    
        stages {
            stage('Prepare') {
                steps {
                    echo "Copy script files..."
                    sh "cp Tools/Docker/Build/Dockerfile.* ./"
                    sh "cp Jenkins/*.sh ./"
                    echo "Done!"
                }
            }
            stage('Build') {
                steps {
                    echo "Building TYPE ${params.BUILD_TYPE} from BRANCH ${GIT_BRANCH}.."
                    sh "printenv"
                    //replace build script variables
                    sh "sed -i 's=JENKINS_VERSION_NO=${DOCKER_REGISTRY_IMAGE_TAG}=g' ${BUILD_SCRIPT}"
                    sh "sed -i 's=JENKINS_IMAGE_HUB_PREFIX=${DOCKER_REGISTRY_REPO}=g' ${BUILD_SCRIPT}"
                    sh "if [ ${DEBUG_JENKINS} = 1 ] ; then cat ${BUILD_SCRIPT} ; fi"
                    //login in to custom docker registry because docker push will be triggered inside build script
                    sh "docker login -u ${DOCKER_REGISTRY_USR} -p ${DOCKER_REGISTRY_PSW} https://${DOCKER_REGISTRY_HOST}"
                    //execute build script
                    sh "/bin/bash ${BUILD_SCRIPT} ${params.BUILD_TYPE}"
                }
            }
            stage('Pre-Deploy') {
                stages{
                    stage('server') {
                        when {
                            anyOf {
                                equals expected: "0", actual: params.BUILD_TYPE
                                equals expected: "1", actual: params.BUILD_TYPE
                            }
                        }
                        steps {
                            sh "sed -i 's=SERVER${DEPLOY_FLAG}=1=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=SERVER${DEPLOY_IMAGE_PREFIX}=${DOCKER_REGISTRY_REPO_VPC}/server=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=SERVER${DEPLOY_IMAGE_NAME}=${DOCKER_REGISTRY_REPO_VPC}/server:${DOCKER_REGISTRY_IMAGE_TAG}=g' ${DEPLOY_SCRIPT}"
                        }
                    }
                    stage('receiver') {
                        when {
                            anyOf {
                                equals expected: "0", actual: params.BUILD_TYPE
                                equals expected: "2", actual: params.BUILD_TYPE
                            }
                        }
                        steps {
                            sh "sed -i 's=RECEIVER${DEPLOY_FLAG}=1=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=RECEIVER${DEPLOY_IMAGE_PREFIX}=${DOCKER_REGISTRY_REPO_VPC}/receiver=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=RECEIVER${DEPLOY_IMAGE_NAME}=${DOCKER_REGISTRY_REPO_VPC}/receiver:${DOCKER_REGISTRY_IMAGE_TAG}=g' ${DEPLOY_SCRIPT}"
                        }                    
                    }
                    stage('push') {
                        when {
                            anyOf {
                                equals expected: "0", actual: params.BUILD_TYPE
                                equals expected: "3", actual: params.BUILD_TYPE
                            }
                        }
                        steps {
                            sh "sed -i 's=PUSH${DEPLOY_FLAG}=1=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=PUSH${DEPLOY_IMAGE_PREFIX}=${DOCKER_REGISTRY_REPO_VPC}/push=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=PUSH${DEPLOY_IMAGE_NAME}=${DOCKER_REGISTRY_REPO_VPC}/push:${DOCKER_REGISTRY_IMAGE_TAG}=g' ${DEPLOY_SCRIPT}"
                        }                    
                    }
                    stage('attachment') {
                        when {
                            anyOf {
                                equals expected: "0", actual: params.BUILD_TYPE
                                equals expected: "4", actual: params.BUILD_TYPE
                            }
                        }
                        steps {
                            sh "sed -i 's=ATTACHMENT${DEPLOY_FLAG}=1=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=ATTACHMENT${DEPLOY_IMAGE_PREFIX}=${DOCKER_REGISTRY_REPO_VPC}/attachment=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=ATTACHMENT${DEPLOY_IMAGE_NAME}=${DOCKER_REGISTRY_REPO_VPC}/attachment:${DOCKER_REGISTRY_IMAGE_TAG}=g' ${DEPLOY_SCRIPT}"
                        }                    
                    }
                    stage('web') {
                        when {
                            anyOf {
                                equals expected: "0", actual: params.BUILD_TYPE
                                equals expected: "5", actual: params.BUILD_TYPE
                            }
                        }
                        steps {
                            sh "sed -i 's=WEB${DEPLOY_FLAG}=1=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=WEB${DEPLOY_IMAGE_PREFIX}=${DOCKER_REGISTRY_REPO_VPC}/web=g' ${DEPLOY_SCRIPT}"
                            sh "sed -i 's=WEB${DEPLOY_IMAGE_NAME}=${DOCKER_REGISTRY_REPO_VPC}/web:${DOCKER_REGISTRY_IMAGE_TAG}=g' ${DEPLOY_SCRIPT}"
                        }                    
                    }
                    stage('test') {
                        when {
                            equals expected: "1", actual: DEBUG_JENKINS
                        }
                        steps {
                            sh "cat ${DEPLOY_SCRIPT}"
                        }
                    }
                }
            }
            stage('Deploy') {
                steps {
                    echo 'DEPLOYING (${DEPLOY_CONFIG})......'
                      sshPublisher(
                       continueOnError: false, failOnError: true,
                       publishers: [
                        sshPublisherDesc(
                         configName: "${DEPLOY_CONFIG}",
                         verbose: true,
                         transfers: [
                          sshTransfer(
                           sourceFiles: "${DEPLOY_SCRIPT}",
                           remoteDirectory: "${DEPLOY_REMOTE_PATH}",
                           execCommand: "cd ${DEPLOY_REMOTE_PATH} && /bin/bash ${DEPLOY_SCRIPT} && rm ${DEPLOY_SCRIPT}"
                          )
                         ])
                       ])
                    echo 'DONE......'
                }
           }
        }
        post {
            success {
                emailext (
                    subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 构建正常",
                    body: """
                    详情:<br/><hr/>
                    &nbsp;&nbsp;<span style='color:green'>SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'</span><br/><hr/>
                    &nbsp;&nbsp;项目名称:$JOB_NAME<br/><hr/>
                    &nbsp;&nbsp;构建编号:$BUILD_NUMBER<br/><hr/>
                    &nbsp;&nbsp;构建环境:$BUILD_ENV - $BUILD_TYPE<br/><hr/>
                    &nbsp;&nbsp;构建日志地址:<a href="${BUILD_URL}console">${BUILD_URL}console</a><br/><hr/>
                    &nbsp;&nbsp;构建地址:<a href="$BUILD_URL">$BUILD_URL</a><br/><hr/>
                    &nbsp;&nbsp;<b>本邮件是程序自动下发的,请勿回复!</b><br/><hr/>
                    """,
                    to: "${DEFAULT_RECIPIENTS}"
                 )
            }
            failure {
                emailext (
                    subject: "'${env.JOB_NAME} [${env.BUILD_NUMBER}]' 构建失败",
                    body: """
                    详情:<br/><hr/>
                    &nbsp;&nbsp;<span style='color:red'>FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'</span><br/><hr/>
                    &nbsp;&nbsp;项目名称:$JOB_NAME<br/><hr/>
                    &nbsp;&nbsp;构建编号:$BUILD_NUMBER<br/><hr/>
                    &nbsp;&nbsp;构建环境:$BUILD_ENV - $BUILD_TYPE<br/><hr/>
                    &nbsp;&nbsp;构建日志地址:<a href="${BUILD_URL}console">${BUILD_URL}console</a><br/><hr/>
                    &nbsp;&nbsp;构建地址:<a href="$BUILD_URL">$BUILD_URL</a><br/><hr/>
                    &nbsp;&nbsp;<b>本邮件是程序自动下发的,请勿回复!</b><br/><hr/>
                    """,
                    to: "${DEFAULT_RECIPIENTS}"
                 )
            }
        }
    }
    
  • 相关阅读:
    golang 使用 protobuf 的教程
    golang语言中sync/atomic包的学习与使用
    《算法竞赛进阶指南》0x21有向无环图中点的可达性统计 topsort+bitset
    《算法竞赛进阶指南》0x21树和图的遍历 求dfs序以及树的重心
    《算法竞赛进阶指南》0x17二叉堆 利用优先队列求k叉哈夫曼树的最优结构
    《算法竞赛进阶指南》0x17二叉堆 链表+红黑树实现高效插入、删除、取最小值
    《算法竞赛进阶指南》0x17二叉堆 POJ2442 矩阵取数求前N大
    GIT-windows系统部署gitblit服务器
    mysql 端口修改
    VUE-开发工具VSCode
  • 原文地址:https://www.cnblogs.com/mosakashaka/p/13040470.html
Copyright © 2011-2022 走看看