JENKINS流水线,实现多台服务器批量发布、回滚(随记)

/*
1、脚本中尽量使用双引号,单引号中使用变量会出问题
2、远程执行shell脚本中的内部的“$”符号需要斜杠转义,包括变量和正则表达式的使用,流水线中的全局变量则不需要转义直接使用
3、远程执行shell脚本说明
发布:
1)使用“Publish over SSH”插件上传jar包到服务器
2)修改jar文件名称,在名称后面加上构建时间,最终jar文件名称类似gateway-1.0.7.22020914.jar,jar文件名添加时间这步也可以由mav`在这里插入代码片`en实现
3)生成软连接
4)nohup启动java程序
5)输出20s日志
回滚:
1)停止进程
2)软连接指向回滚版本的jar文件
3)启动java程序
4)输出20s日志
*/
pipeline {
agent any
environment {
/*
BUILD_TIMESTAMP 构建时间,由 Build Timestamp插件提供,这里在全局配置格式改成了yyMMddHHmm,也可以使用groovy的TimeZone.getTimeZone("Asia/Shanghai")函数实现

*/
PROJECT_NAME = "golife" // 项目名称
SERVICE_NAME= "member" // 服务名称
BRANCH_NAME = "master" // gitlab分支
GIT_PATH = "GITURL" // 项目URL
GIT_AUTH_ID = "git凭证id" // git凭证id
GIT_BRANCH = "master" // git分支

}

// maven工具,在系统管理-全局工具配置-Maven,如果没有新增一个即可,这里的“maven3.6.3”为新增mavne的“name”
tools {
maven "maven"
}

options {
timestamps() // 构建日志中带上时间
disableConcurrentBuilds() // 不允许同时执行流水线
timeout(time: 5, unit: "MINUTES") // 设置流水线运行超过5分钟,Jenkins将中止流水线
buildDiscarder(logRotator(numToKeepStr: "10")) // 表示保留10次构建历史
}

//参数化构建过程,可在流水线语法http://{JENKINS URL}/job/piptt/pipeline-syntax/中选择“properties: Set job properties”生成流水线
parameters{
// 发布选项--选项参数
choice(
choices: ['deploy', 'rollback','start','restart','stop'],
description: '''发布选项:
发布:deploy
回滚:rollback
启动:start
重启:restart
停止:stop
''',
name: 'ACTION'
)
// 要执行的服务器--多选框参数(需安装插件Extended Choice Parameter)
extendedChoice(
description: '选择需要执行的服务器',
multiSelectDelimiter: ',',
name: 'Release_servers',
quoteValue: false,
saveJSONParameterToFile: false,
type: 'PT_CHECKBOX',
value: '192.168.1.100,192.168.1.101,192.168.1.102,192.168.1.103', // Publish over SSH插件添加的服务器名,安装好插件后再系统管理-系统配置-找到“Publish over SSH”添加服务器
visibleItemCount: 10
)
// 发布版本号--字符串参数
string(
defaultValue: '1.0.7',
description: '''发布版本号,仅发布操作需要填写。
该版本号为maven打包后jar包名称的版本号,如gateway-1.0.7,填写 1.0.7''',
name: 'RELEASE_VERSION',
trim: true
)
// 回滚版本号--字符串参数
string(
description: '''回滚版本号,仅回滚操作需要填写
可在构建历史日志中,搜索“RELEASE VERION”找到,格式为:1.0.7.22010456''',
name: 'BACK_VERSION',
trim: true
)
}

stages {
stage("打印信息") { //打印信息
steps {
echo "打印信息"
echo "jenkins任务名: ${JOB_NAME}"
echo "项目名: ${PROJECT_NAME}"
echo "服务名:${SERVICE_NAME}"
echo "执行动作: ${ACTION}"
echo "gitlab分支名: ${BRANCH_NAME}"
echo "build_id: ${BUILD_ID}"
echo "工作空间: ${WORKSPACE}"
echo "发布服务器: ${Release_servers}"
}
}

stage("拉取代码") {
// 仅当${ACTION}=="deploy"时,再执行此stage,详情查看官网when指令https://www.jenkins.io/zh/doc/book/pipeline/syntax/#when
when {
environment name:"ACTION", value: "deploy"
}
// 流水线语法中选择“checkout: Check out from version control”生成
steps {
script {
checkout([
$class: "GitSCM",
branches: [[name: "${GIT_BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "${GIT_AUTH_ID}", url: "${GIT_PATH}"]]
])
}
}
}

stage("maven构建") {
when {
environment name:"ACTION", value: "deploy"
}

steps {
sh "mvn clean install -f ./pom.xml"
}
}

stage("发布") {
/*
when {
environment name:"ACTION", value: "rollback"
not {
environment name:"Release_servers", value:""
}
}
这里的when指令可以多用一个not来判断构建的时候是否有选择发布的服务器,为了直观的看到我这里在scritp里面判断了下,如果没有选择服务器则输出“没有可用于执行的服务器”
*/
when {
environment name:"ACTION", value: "deploy"
}
steps {
script {
if (Release_servers.length() == 0) {
println("没有可用于执行的服务器")
} else {
for ( ip in Release_servers.tokenize(",")) {
sshPublisher (
publishers: [
sshPublisherDesc (
configName: "${ip}",
transfers: [
sshTransfer (
cleanRemote: false,
excludes: "",
execCommand:
"""
echo "###########################################################################################################################################################################################################################"
mkdir /var/log/${PROJECT_NAME}/${SERVICE_NAME} -p
mkdir /opt/${PROJECT_NAME}/link-jar/ -p
SERVICE_PID=`pgrep -f "java.*${SERVICE_NAME}{0,20}\$"` # 服务运行pid

mv /opt/${PROJECT_NAME}/jar/${SERVICE_NAME}/${SERVICE_NAME}-${RELEASE_VERSION}.jar /opt/${PROJECT_NAME}/jar/${SERVICE_NAME}/${SERVICE_NAME}-${RELEASE_VERSION}.${BUILD_TIMESTAMP}.jar
if [ \$? != "0" ]
then
echo "${ip}上找不到版本号为${RELEASE_VERSION}的${SERVICE_NAME},请检查后重新构建"
exit 145
else
echo "${ip}:开始部署${SERVICE_NAME}"
ln -snf /opt/${PROJECT_NAME}/jar/${SERVICE_NAME}/${SERVICE_NAME}-${RELEASE_VERSION}.${BUILD_TIMESTAMP}.jar /opt/${PROJECT_NAME}/link-jar/${PROJECT_NAME}-${SERVICE_NAME}
kill -9 "\${SERVICE_PID}"
nohup java -jar /opt/${PROJECT_NAME}/link-jar/${PROJECT_NAME}-${SERVICE_NAME} > /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.log 2>&1 &
echo -e "\n启动日志将持续输出20s时间,如果没有输出日志,则表示发布失败\n"
timeout 20 tail -f /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.log
sleep 1
echo "< RELEASE VERION: ${RELEASE_VERSION}.${BUILD_TIMESTAMP} >"
fi
""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: "[, ]+",
remoteDirectory: "/${PROJECT_NAME}/jar/${SERVICE_NAME}",
remoteDirectorySDF: false,
removePrefix: "${SERVICE_NAME}/target",
sourceFiles: "**/${SERVICE_NAME}*.jar"
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
}
}
}
}
}
stage('回滚') {
when {
environment name:"ACTION", value: "rollback"
}
steps {
script {
if (Release_servers.length() == 0) {
println("没有可用于执行的服务器")
} else {
for ( ip in Release_servers.tokenize(",")) {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: "${ip}",
transfers: [
sshTransfer(cleanRemote: false,
excludes: '',
execCommand: """
echo "###########################################################################################################################################################################################################################"
mkdir /var/log/${PROJECT_NAME}/${SERVICE_NAME} -p
mkdir /opt/${PROJECT_NAME}/link-jar/ -p
SERVICE_PID=`pgrep -f "java.*${SERVICE_NAME}{0,20}\$"` # 服务运行pid
if [ -f "/opt/${PROJECT_NAME}/jar/${SERVICE_NAME}/${SERVICE_NAME}-${BACK_VERSION}.jar" ]
then
echo "${ip}:开始部署${SERVICE_NAME}"
ln -snf /opt/${PROJECT_NAME}/jar/${SERVICE_NAME}/${SERVICE_NAME}-${BACK_VERSION}.jar /opt/${PROJECT_NAME}/link-jar/${PROJECT_NAME}-${SERVICE_NAME}
kill -9 "\${SERVICE_PID}"
nohup java -jar /opt/${PROJECT_NAME}/link-jar/${PROJECT_NAME}-${SERVICE_NAME} > /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.${ACTION}.log 2>&1 &
echo -e "\n启动日志将持续输出20s时间,如果没有输出日志,则表示回滚失败\n"
timeout 20 tail -f /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.${ACTION}.log
sleep 1
else
echo "${ip}上找不到版本号为${BACK_VERSION}的${SERVICE_NAME},请检查后重新构建"
exit 145
fi
""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF:
false, removePrefix: '',
sourceFiles: ''
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
}
}
}
}
}
stage('启动') {
when {
environment name:"ACTION", value: "start"
}
steps {
script {
if (Release_servers.length() == 0) {
println("没有可用于执行的服务器")
} else {
for ( ip in Release_servers.tokenize(",")) {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: "${ip}",
transfers: [
sshTransfer(cleanRemote: false,
excludes: '',
execCommand: """
echo "###########################################################################################################################################################################################################################"
SERVICE_PID=`pgrep -f "java.*${SERVICE_NAME}{0,20}\$"` # 服务运行pid
if [ \$? != "0" ]
then
echo "${ip}:正在启动${SERVICE_NAME}"
nohup java -jar /opt/${PROJECT_NAME}/link-jar/${PROJECT_NAME}-${SERVICE_NAME} > /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.${ACTION}.log 2>&1 &
echo -e "\n启动日志将持续输出20s时间,如果没有输出日志,则表示回滚失败\n"
timeout 20 tail -f /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.${ACTION}.log
sleep 1
else
echo "${ip}上${SERVICE_NAME}已启动,无需执行"
exit 145
fi
""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF:
false, removePrefix: '',
sourceFiles: ''
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
}
}
}
}
}
stage('重启') {
when {
environment name:"ACTION", value: "restart"
}
steps {
script {
if (Release_servers.length() == 0) {
println("没有可用于执行的服务器")
} else {
for ( ip in Release_servers.tokenize(",")) {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: "${ip}",
transfers: [
sshTransfer(cleanRemote: false,
excludes: '',
execCommand: """
echo "###########################################################################################################################################################################################################################"
SERVICE_PID=`pgrep -f "java.*${SERVICE_NAME}{0,20}\$"` # 服务运行pid
echo "${ip}:正在重启${SERVICE_NAME}"
kill -9 "\${SERVICE_PID}"
nohup java -jar /opt/${PROJECT_NAME}/link-jar/${PROJECT_NAME}-${SERVICE_NAME} > /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.${ACTION}.log 2>&1 &
echo -e "\n启动日志将持续输出20s时间,如果没有输出日志,则表示回滚失败\n"
timeout 20 tail -f /var/log/${PROJECT_NAME}/${SERVICE_NAME}/${SERVICE_NAME}-${BUILD_TIMESTAMP}.${ACTION}.log
sleep 1

""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF:
false, removePrefix: '',
sourceFiles: ''
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
}
}
}
}
}
stage('停止') {
when {
environment name:"ACTION", value: "stop"
}
steps {
script {
if (Release_servers.length() == 0) {
println("没有可用于执行的服务器")
} else {
for ( ip in Release_servers.tokenize(",")) {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: "${ip}",
transfers: [
sshTransfer(cleanRemote: false,
excludes: '',
execCommand: """
echo "###########################################################################################################################################################################################################################"
SERVICE_PID=`pgrep -f "java.*${SERVICE_NAME}{0,20}\$"` # 服务运行pid
if [ \$? == "0" ]
then
echo "${ip}:正在停止服务${SERVICE_NAME}"
kill -9 "\${SERVICE_PID}"
echo "服务${SERVICE_NAME}"停止成功
else
echo "${ip}上${SERVICE_NAME}未启动,无需执行"
exit 145
fi
""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF:
false, removePrefix: '',
sourceFiles: ''
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
}
}
}
}
}
}
}

posted @ 2024-07-12 10:08  技术颜良  阅读(2)  评论(0编辑  收藏  举报