查看原文
其他

Jenkins多分支流水线

木讷大叔爱运维 木讷大叔爱运维 2022-07-13


点击上方蓝色字体,关注我们

docker-compose编排实现java多环境交付一文中我们使用docker-compose对项目的环境校验发版/回滚/重启、操作校验等步骤进行了优化。但是由于job使用的是“自由风格项目”或“mvn项目”,因此都是一个分支对应一个job,虽然也可以通过参数化构建方式实现一个job对应多个分支,常此以往一个项目需要建立很多job,管理非常不便。


解决方案:

Jenkin多分支流水线,允许Jenkinsfile与需要 Jenkins 构建的应用程序代码放在一起,然后 Jenkins 从源代码管理系统中检出 Jenkinsfile 文件作为流水线项目构建过程的一部分并接着执行你的流水线。


下面我们就来体验下Jenkins多分支的构建过程吧。


Jenkinsfile

Jenkins流水线的定义通常需要写入到一个文本文件(称为 Jenkinsfile )中,该文件可以被放入项目的源代码版本库中。 


注意:Jenkinsfile放到项目版本库的根路径下。

在此我们还是延续使用helloworld的java项目。


Jenkinsfile的代码如下:

vim Jenkinsfilepipeline { options { ansiColor('xterm') timestamps() } agent { label 'docker-slave-java' } triggers { GenericTrigger (causeString: 'Generic Cause', genericVariables: [[defaultValue: 'deploy', key: 'deploy_env', regexpFilter: '', value: '']], regexpFilterExpression: '', regexpFilterText: '', token: '123456') }
environment { APP_NAME = "helloworld" IMAGE_NAME = "helloworld/helloworld" MONITOR_URL = "http://127.0.0.1:8080" JAVA_OPTS = "-Xmx128m -Xms128m -Dspring.profiles.active=branch" PORT = "9080:8080" } parameters { choice choices: ['deploy', 'rollback', 'restart'], description: '''deploy:发布 rollback:回滚 restart:重启 注意:restart 参数只适用与docker环境''', name: 'deploy_env' string defaultValue: '0', description: '''回滚版本号,发版时忽略 注意: 版本号为git commitid,如7e2c56522188c98f6294d91c8568dfcedf994e42。''', name: 'version', trim: false }
stages { stage('操作校验') { steps { sh label: '', script: ''' echo ${APP_NAME} #操作校验 if [ "${deploy_env}" = "deploy" ];then echo -e "\\033[34mstart ${deploy_env}\\033[0m" echo ${GIT_PREVIOUS_SUCCESSFUL_COMMIT} echo ${GIT_COMMIT} [ "${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" != "${GIT_COMMIT}" ] && echo -e "\\033[34mstart maven package\\033[0m" || { #版本未更新,停止发版 echo -e "\\033[31mRepositories not update, stop ${deploy_env}\\033[0m" exit 1 }
/usr/local/maven/bin/mvn clean package docker:build -DdockerImageTags=${GIT_COMMIT} -Dmaven.test.skip=true -DpushImageTag
[ $? -eq 0 ] && echo -e "\\033[32mmaven package success\\033[0m" || { echo -e "\\033[31mmaven package fail\\033[0m" exit 1 } elif [ "${deploy_env}" = "rollback" ];then echo -e "\\033[34mstart ${deploy_env}\\033[0m" echo ${GIT_PREVIOUS_SUCCESSFUL_COMMIT} echo ${GIT_COMMIT} #查看远程分支是否有此版本 git branch -r --contains $version [ $? -eq 0 ] && echo -e "\\033[34mstart docker steps\\033[0m" || { echo -e "\\033[31mverison is wrong,please check version\\033[0m" exit 1 } fi''' } }        stage('开发部署') { when { branch 'develop' } steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'test-3.63', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: """ #!/bin/bash IN_FACE=`/sbin/route -n |awk \'{if(\$4~/UG/){print \$8}}\'|head -n 1` LOCAL_IP=`/sbin/ip addr show \"\${IN_FACE}\" | grep -w \'inet\' | awk \'{print \$2}\'`
CONTAINER_NAME=`echo ${IMAGE_NAME} | awk -F/ \'{print \$2}\'` #删除老镜像 DEL_IMAGE() { echo -e "\\033[34mrm image ${IMAGE_NAME}:\$1\\033[0m" sudo docker image rm harbor.cityre.cn/${IMAGE_NAME}:\$1 --no-prune [ \$? -eq 0 ] && echo -e "\\033[32mrm ${IMAGE_NAME}:\$1 succss \\033[0m" || { echo -e "\\033[31mrm ${IMAGE_NAME}:\$1 fail \\033[0m" exit 1 } }
#健康检查 HEALTHCHECK() { timeout=180 echo -e "\\033[34mhealth check\\033[0m" for (( i=1;i<=\$timeout;i++ )) do status=\$(sudo docker inspect --format=\'{{json .State.Health}}\' \${CONTAINER_NAME}|grep -Po \'"Status[":]+\\K[^"]+\') echo \$status if [ \$status = \'healthy\' ];then echo -e "\\033[32m\${LOCAL_IP} \${CONTAINER_NAME} status is \${status}\\033[0m" [ ${deploy_env} != "restart" ] && DEL_IMAGE \${OLD_VERSION} exit 0 elif [ \$status = \'starting\' ];then sleep 23 else echo -e "\\033[31m\${LOCAL_IP} \${CONTAINER_NAME} status is \${status}\\033[0m" exit 1 fi done }

#定义docker-compose变量,注意第一步清空env,后续追加env INIT_VAR() { echo -e "\\033[34minit docker-compose variable\\033[0m" echo "IMAGE_NAME=${IMAGE_NAME}" > .env echo "CONTAINER_NAME=\${CONTAINER_NAME}" >> .env echo "APP_NAME=${APP_NAME}" >> .env echo "MONITOR_URL=${MONITOR_URL}" >> .env echo "PORT=${PORT}" >> .env echo "JAVA_OPTS=\$(echo ${JAVA_OPTS}|sed 's/branch/test/')" >> .env }
#进入项目目录 cd /App/java_app_tmp/${APP_NAME} #提前读取env文件中的老版本号,用于删除老镜像 [ -f .env ] && OLD_VERSION=\$(grep VERSION .env|awk -F= \'{print \$2}\')
case ${deploy_env} in deploy) echo -e "\\033[34mstart ${deploy_env} steps\\033[0m" INIT_VAR echo "VERSION=${GIT_COMMIT}" >> .env sudo docker-compose up -d --build HEALTHCHECK ;; rollback) echo -e "\\033[34mstart ${deploy_env} steps\\033[0m" INIT_VAR echo "VERSION=${version}" >> .env sudo docker-compose up -d --build HEALTHCHECK ;; restart) sudo docker-compose restart HEALTHCHECK ;; *) exit 1 ;; esac""", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) } }        stage('测试部署') { when {                branch 'develop' } steps {                sh 'uname -a' } } } post { unstable { emailext ( body: """项目名称:${JOB_NAME}\n构建编号:${BUILD_NUMBER}\n构建日志:${BUILD_URL}console""", subject: '【Jenkins构建通知】:$JOB_NAME - Build # $BUILD_NUMBER - Unstable!', to: 'test@test.cn', from: 'jenkins@test.cn' ) } success { emailext ( body: """项目名称:${JOB_NAME}\n构建编号:${BUILD_NUMBER}\n构建日志:${BUILD_URL}console""", subject: '【Jenkins构建通知】:$JOB_NAME - Build # $BUILD_NUMBER - Successful!', to: 'test@test.cn', from: 'jenkins@test.cn' ) } failure { emailext ( body: """项目名称:${JOB_NAME}\n构建编号:${BUILD_NUMBER}\n构建日志:${BUILD_URL}console""", subject: '【Jenkins构建通知】:$JOB_NAME - Build # $BUILD_NUMBER - Failure!', to: 'test@test.cn', from: 'jenkins@test.cn' ) } }}

我们在git版本中分了dev、test、master三个分支,分别对应开发、测试、生产三个环境,Jenkinsfile中通过when+branch来分别匹配不同的分支执行的操作。如下:

when { branch 'develop'}

本次实验我们设置:

  1. 开发部署

    实现开发分支的docker构建。

  2. 测试部署

    实现测试分支的输出内核信息。

  3. 邮件通知

    实现失败/成功/不稳定等3个维度的邮件通知。


Jenkins构建

Jenkins流水线使用Blue Ocean效果更佳哦。


1.新建多分支流水线项目

  我们的项目为docker-test-java3

2.配置多分支流水线

我们只需配置git代码库即可,此时会自动检测分支。如果代码库中没有Jenkinsfile会提示报错。

我们只需在对应分支直接构建即可。

3.构建



总结


从以上过程我们可以看出,多分支流水线的重点在于Jenkinsfile文件,但是由于多分支流水线需要Jenkins放在版本库中,因此运维也要像开发一样维护Jenkinsfile,容易导致版本冲突。


因此对于此种情况,我认为还是需要扩展共享库的,这样可以进一步优化为多个项目使用同一套流水线,运维只需维护扩展共享库即可。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存