gittools====================================
package com.intre.tools
def gitPullByBranchIfRepoNotExistsThenClone(sshUrl, branchOrTagName, gitPath) {
println "branch or tag's name: ${branchOrTagName}"
println "store git folder name: ${gitPath}"
sh """
# 如果指定的目录不存在, 则拉取代码到指定的路径下
if [ -d $gitPath ];then
rm -rf $gitPath
fi
git clone -b $branchOrTagName $sshUrl $gitPath --depth=1
"""
}
def gitCreateTagOrPushRemoteTag(tagName, gitPath, isPush) {
println "tag name: ${tagName}"
println "is push: ${isPush}"
sh """
cd ${gitPath}
git tag ${tagName}
if [ ${isPush} = 'true' ]; then
echo "需要推送到远程tag"
git push origin ${tagName}
else
echo "不需要推送远程tag"
fi
"""
}
def commitAndIsPushRemote(gitPath, description, isPush) {
sh """
cd ${gitPath}
git add .
git commit -m '${description}'
if [ ${isPush} = 'true' ]; then
echo "需要推送到远程"
git push
else
echo "不需要推送远程"
fi
"""
}
def simpleTagName(tagName) {
def tag_eles = tagName.split("/",3)
return tag_eles[tag_eles.size() - 1]
}
def resetStagesData(gitPath = ".") {
sh """
cd ${gitPath}
git reset --hard
"""
}
// 新增网关仓库拉取命令
def newGitPullByBranchIfRepoNotExistsThenClone(new_sshUrl, new_branchOrTagName, new_gitPath) {
println "branch or tag's name: ${new_branchOrTagName}"
println "store git folder name: ${new_gitPath}"
sh """
if [ ! -d $new_gitPath ];then
mkdir $new_gitPath
fi
cd $new_gitPath
repo init -u $new_sshUrl -b $new_branchOrTagName
repo sync -d -c
repo start $new_branchOrTagName --all
"""
}
//打tag并推送
def gitCreateTagAndCommitIsPushRemote(tagName, gitPath, isPush){
println "tag name: ${tagName}"
println "is push: ${isPush}"
sh """
cd ${gitPath}
git add .
git commit -m '${tagName}'
git tag ${tagName}
if [ ${isPush} = 'true' ]; then
echo "需要推送到远程tag"
git push origin master
git push origin ${tagName}
else
echo "不需要推送远程tag"
fi
"""
}
================admin资源
package com.intre.domain
class SmsSmartHomeAdmin{
Map projectMap = [
"tslSmartHomeAdmin": [
"git_dir_url": "ssh://git@git.intretech.com:7999/yqsms/yq-smswebadmin.git",
"git_dir_name": "smswebadmin",
"start_server" : "start.sh",
"dingding_title": "admin server"
]
]
Map admin_config_server_map = [
"SmartHomeServer1-223" : [
"remote_ip" : "139.219.143.223",
"remote_path" : "/usr/local/server/intre-admin/",
"port_list" : ['8888','8889'],
"namespaceId" : "88e7d906-3190-4914-aa90-2b87968abfed",
"ssh_pass" : "{AQAAABAAAAAQS49ooWTgio0jZoYyHmwByyu6VqSOC2ENuwWkoWQj75U=}"
],
"SmartHomeServer2-132" :[
"remote_ip" : "139.219.139.132",
"remote_path" : "/usr/local/server/intre-admin/",
"port_list" : ['8888','8889'],
"namespaceId" : "71039c33-9f5a-4246-94da-194815591173",
"ssh_pass" : "{AQAAABAAAAAQnprGZaL179+nTOjAmO4KG54acFonurqEqoKAZlY5KDA=}",
]
]
}
============================流水线脚本
#! groovy
@Library('intreShareLib@main') _
import groovy.json.JsonSlurperClassic
import com.intre.tools.GitTools
import com.intre.domain.SmsSmartHomeAdmin
import com.intre.tools.SystemTools
import java.util.UUID
GitTools gitTools = new GitTools()
SmsSmartHomeAdmin SmsSmartHomeAdmin = new SmsSmartHomeAdmin()
//获取参数配置
def projects = SmsSmartHomeAdmin.projectMap['tslSmartHomeAdmin']
//nacos登陆token
def nacos_token = ""
//已被使用的端口号
def old_version_port = ""
//新端口号
def new_port = ""
//旧版本在线状态
def old_version_enabled = ""
//旧版本ip
def old_version_ip = ""
//服务器目标地址
def remote_path = ""
//运行任务名
def process_name = "intre-admin"
// 启动脚本名称
def config_name = "start.sh"
//新版本在线状态
def new_vsersion_enabled = ""
//新版本ip
def new_version_ip = ""
//端口列表
List port_list = []
//脚本列表
List config_list = []
// job名称
def job_name = "${JOB_NAME} 发布"
//复制文件时,需要溢出的目录前缀
def removePrefix = "smswebadmin/intre-admin/target/"
//nacos调用http接口需要使用的参数
def namespaceId = ""
//nacos地址
def nacos_url = "42.159.201.211:8848"
def cluster_name = "DEFAULT"
def group_name = "DEFAULT_GROUP"
def result_path = ""
def remote_ip = ""
pipeline {
agent {
label "master"
}
environment {
// 编译版本
compile_version = "${env.compile_version}".trim()
//发布服务器
ssh_server_name = "${env.server_name}".trim()
//business临时目录名称
business_git_dir_name = "${projects['git_dir_name']}".trim()
//business编译代码分支名称
business_branch_name = "${env.business_branch_name}".trim()
//包名
def jarName = "${process_name}.jar"
def sourceFiles = "${removePrefix}${jarName}"
exception_cause = "启动失败"
//上传ota包用uuid
def uuid = UUID.randomUUID().toString()
//钉钉发布消息服务器名
def msg_server = ssh_server_name.replace("MicrosoftCloud", "")
}
stages {
stage("服务器参数配置") {
steps {
script {
echo "config"
Map config_map = SmsSmartHomeAdmin.admin_config_server_map["${ssh_server_name}"]
remote_ip = config_map['remote_ip']
remote_path = config_map['remote_path']
port_list = config_map['port_list']
namespaceId = config_map['namespaceId']
ssh_pass = config_map['ssh_pass']
println remote_ip
println remote_path
println port_list
println namespaceId
println ssh_pass
println uuid
}
}
}
stage("拉取admin代码") {
steps {
script {
try {
//获取busine仓库地址
def business_git_ssh_url = projects['git_dir_url']
// def business_git_ssh_url = "ssh://git@git.intretech.com:7999/yqsms/yq-smswebadmin.git"
//拉取business仓库代码
gitTools.gitPullByBranchIfRepoNotExistsThenClone(business_git_ssh_url, business_branch_name, business_git_dir_name)
} catch (e) {
exception_cause = "拉取代码出错"
error exception_cause
}
}
}
}
stage("编译admin代码") {
steps {
script {
echo "开始编译"
try {
//执行shell命令
sh """
#进入business代码保存目录
cd ${business_git_dir_name}
#编译business代码
mvn install
"""
} catch (e) {
exception_cause = "business 编译失败"
error exception_cause
}
}
}
}
stage("检查旧版本服务在线情况,将包上传至服务器,部署新版本") {
steps {
script {
echo "获取旧版本在线情况"
//获取nacos登陆token
try {
http_login = httpRequest(
customHeaders: [
[
maskValue: false,
name : 'Content-Type',
value : 'application/x-www-form-urlencoded']
],
httpMode: 'POST',
ignoreSslErrors: true,
requestBody: "username=jenkins&password=Intretech123cool",
responseHandle: 'LEAVE_OPEN',
url: "http://${nacos_url}/nacos/v1/auth/users/login",
wrapAsMultipart: false
)
//从请求返回中提取accesstoken
def httpJson_login = new JsonSlurperClassic().parseText(http_login.content)
http_login.close()
nacos_token = httpJson_login.accessToken
//调用查询接口,确认是否有实例存在
http_get_search = httpRequest(
httpMode: 'GET',
ignoreSslErrors: true,
responseHandle: 'LEAVE_OPEN',
url: "http://${nacos_url}/nacos/v1/ns/catalog/services?hasIpCount=true&withInstances=false&pageNo=1&pageSize=10&serviceNameParam=${process_name}&groupNameParam=DEFAULT_GROUP&accessToken=${nacos_token}&namespaceId=${namespaceId}",
wrapAsMultipart: false
)
def http_search = new JsonSlurperClassic().parseText(http_get_search.content)
echo "${http_search}"
http_get_search.close()
//若返回的列表为空,则异常退出
if (http_search.count == 0) {
exception_cause = "未查询到服务"
error exception_cause
}
//调用调用查询实例接口,获取旧版本运行端口,ip,状态等信息
http_getPort = httpRequest(
customHeaders: [
[
maskValue: false,
name : 'Authorization',
value : "${http_login.content}"
]
],
httpMode: 'GET',
ignoreSslErrors: true,
responseHandle: 'LEAVE_OPEN',
url: "http://${nacos_url}/nacos/v1/ns/catalog/instances?accessToken=${nacos_token}&serviceName=${process_name}&clusterName=${cluster_name}&groupName=${group_name}&pageSize=10&pageNo=1&namespaceId=${namespaceId}",
wrapAsMultipart: false
)
//从请求返回中提取端口,ip,在线状态信息
def httpJson_getPort = new JsonSlurperClassic().parseText(http_getPort.content)
echo "从请求返回中提取端口,ip,在线状态信息"
echo "${httpJson_getPort}"
http_getPort.close()
//获取上线数量
def online_count = httpJson_getPort.count
//旧版本在线状态
old_version_enabled = "${httpJson_getPort.list[0].enabled}"
//旧版本ip
old_version_ip = "${httpJson_getPort.list[0].ip}"
//已被使用的端口号
old_version_port = "${httpJson_getPort.list[0].port}"
echo old_version_port
//判断在线数量
//若是数量为2时,报错人工处理;
if (online_count == 2) {
exception_cause = "已有两个服务同时在线,转人工处理"
error exception_cause
} else if (online_count == 0) {
exception_cause = "无服务在线,转人工处理"
error exception_cause
}
//需要部署的新端口号,根据已部署的端口号调整
if (old_version_port == port_list[0]) {
new_port = port_list[1]
config_name = "${config_name}"
echo new_port
echo config_name
} else if (old_version_port == port_list[1]) {
new_port = port_list[0]
config_name = "${config_name}"
echo new_port
echo config_name
}
if (new_port == null) {
exception_cause = "部署新端口号异常"
error exception_cause
}
echo "准备上传"
//服务器目标地址
remote_path = remote_path.concat("${new_port}/")
result_path = "${remote_path}temp/${uuid}.txt"
echo "开始上传"
//调用publish over ssh插件,上传编译好的包,并执行部署命令
sshPublisher(
failOnError: true,
publishers: [
sshPublisherDesc(
configName: "${ssh_server_name}",
sshCredentials: [
encryptedPassphrase: "${ssh_pass}",
key : '',
keyPath : '/home/jenkins/.ssh/id_rsa',
username : 'jenkins'
],
transfers: [
sshTransfer(
cleanRemote: false,
excludes: '',
remoteDirectory: "${remote_path}",
remoteDirectorySDF: false,
removePrefix: "${removePrefix}",
sourceFiles: "${sourceFiles}",
execCommand: """
source /etc/profile
echo "start running script"
cd ${remote_path}
if [ -f "${jarName}" ];then
echo "文件存在"
else
echo "failure" > temp/${uuid}.txt
echo "文件不存在" >> temp/${uuid}.txt
exit 1
fi
old_process_id=`ps -ef|grep "${process_name}" |grep ${old_version_port} |grep -v grep |awk '{print\$2}'`
echo \$old_process_id
if [[ -n \$old_process_id ]]
then
echo "process exists,continue."
else
echo "failure" > temp/${uuid}.txt
echo "process no exist" >> temp/${uuid}.txt
exit 1
fi
new_process_id=`ps -ef|grep "${process_name}" |grep ${new_port} |grep -v grep |awk '{print\$2}'`
echo "新进程ID为:"
echo \$new_process_id
if [[ -n \$new_process_id ]]
then
echo "process is running,need kill"
kill -9 \$new_process_id
else
echo "no service running"
fi
nohup sh ${config_name} ${jarName} > /dev/null 2>&1 &
echo "success" > temp/${uuid}.txt
""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+'
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
echo "新版本已部署上线"
sh """
scp jenkins@${remote_ip}:${result_path} ${WORKSPACE}
"""
def read_result = readFile(file: "${WORKSPACE}/${uuid}.txt").trim()
def lines = read_result.split('\n')
if (lines.size() <= 1) {
echo read_result
} else {
def firstLine = lines[0]
def secondLine = lines[1]
if (firstLine == "failure") {
exception_cause = "${secondLine}"
error exception_cause
} else {
echo firstLine
}
}
} catch (e) {
print e
if (exception_cause == "启动失败") {
exception_cause = "新版本上线失败"
}
error exception_cause
}
}
}
}
stage("检查新版本是否正常运行") {
steps {
script {
echo "开始确认新版本上线是否成功"
//调用publish over ssh插件,执行命令确认新服务是否运行,进行健康检查
sshPublisher(
failOnError: true,
publishers: [
sshPublisherDesc(
configName: "${ssh_server_name}",
sshCredentials: [
encryptedPassphrase: "${ssh_pass}",
key : '',
keyPath : '/home/jenkins/.ssh/id_rsa',
username : 'jenkins'
],
transfers: [
sshTransfer(
cleanRemote: false,
excludes: '',
remoteDirectory: "",
remoteDirectorySDF: false,
removePrefix: '',
sourceFiles: "",
execCommand: """
#移动至工作目录
cd ${remote_path}
#健康检查,失败20次则直接杀掉进程
for i in \$(seq 1 20)
do
#确认新服务是否运行
new_process_id=`ps -ef|grep "${process_name}" |grep ${new_port} |grep -v grep |awk '{print\$2}'`
echo \$new_process_id
#进程号存在则继续执行,不存在则退出
if [[ -n \$new_process_id ]]
then
echo "process is running,continue health check"
else
echo "failure" > temp/${uuid}.txt
echo "no service running" >> temp/${uuid}.txt
exit 1
fi
health_check=`curl http://localhost:${new_port}/admin/service/health`
if [[ "\$health_check" == "ok" ]]
then
echo "success" > temp/${uuid}.txt
echo "Service deployment successful" > temp/${uuid}.txt
exit 0
else
echo "Service deployment not completed, wait for 30 seconds and try again"
sleep 30s
fi
done
echo "failure" > temp/${uuid}.txt
echo "Health check failed" >> temp/${uuid}.txt
kill -9 \$new_process_id
exit 1
""",
execTimeout: 600000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+'
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
sh """
scp jenkins@${remote_ip}:${result_path} ${WORKSPACE}
"""
def read_result = readFile(file: "${WORKSPACE}/${uuid}.txt").trim()
def lines = read_result.split('\n')
if (lines.size() <= 1) {
echo read_result
} else {
def firstLine = lines[0]
def secondLine = lines[1]
if (firstLine == "failure") {
exception_cause = "${secondLine}"
error exception_cause
} else {
echo firstLine
}
}
echo "新版本已部署上线,nacos确认上线是否成功"
//调用调用查询实例接口,获取新版本运行是否正常
try {
http_get_service_status = httpRequest(
customHeaders: [
[
maskValue: false,
name : 'Authorization',
value : "${http_login.content}"]
],
httpMode: 'GET',
ignoreSslErrors: true,
responseHandle: 'LEAVE_OPEN',
url: "http://${nacos_url}/nacos/v1/ns/catalog/instances?accessToken=${nacos_token}&serviceName=${process_name}&clusterName=${cluster_name}&groupName=${group_name}&pageSize=10&pageNo=1&namespaceId=${namespaceId}",
wrapAsMultipart: false
)
//从请求返回中提取端口,ip,在线状态信息
def httpJson_get_service_status = new JsonSlurperClassic().parseText(http_get_service_status.content)
http_get_service_status.close()
if (httpJson_get_service_status.count == 2) {
def version_list = httpJson_get_service_status.list
def enabled_1 = "${version_list[0].enabled}"
def enabled_2 = "${version_list[1].enabled}"
if (!enabled_1 == "true" || !enabled_2 == "true") {
exception_cause = "新版本上线异常"
error exception_cause
} else if (enabled_1 == "true" && enabled_2 == "true") {
echo "新版本上线成功,开始通知下线旧版本"
} else {
exception_cause = "新版本上线失败"
error exception_cause
}
} else {
exception_cause = "新版本上线失败"
error exception_cause
}
} catch (e) {
print e
if (exception_cause == "启动失败") {
exception_cause = "调用nacos获取实例失败"
}
error exception_cause
}
}
}
}
stage("下线旧版本business服务") {
steps {
script {
sleep(time: '15', unit: 'SECONDS')
echo "旧版本开始下线"
//调用nacos借口,通知旧版本下线
try {
http_notify_offline = httpRequest(
customHeaders: [
[
maskValue: false,
name : 'Content-Type',
value : 'application/x-www-form-urlencoded'],
[
maskValue: false,
name : 'Authorization',
value : "${http_login.content}"]
],
httpMode: 'PUT',
ignoreSslErrors: true,
responseHandle: 'LEAVE_OPEN',
url: "http://${nacos_url}/nacos/v1/ns/instance?accessToken=${nacos_token}&serviceName=${process_name}&clusterName=${cluster_name}&groupName=${group_name}&ip=${old_version_ip}&port=${old_version_port}&ephemeral=true&weight=1&enabled=false&metadata=%7B%22preserved.register.source%22%3A%22SPRING_CLOUD%22%7D&namespaceId=${namespaceId}",
wrapAsMultipart: false
)
//根据接口回复确认是否继续执行
if (http_notify_offline.content == "ok") {
sleep(time: '15', unit: 'SECONDS')
//调用publish over ssh插件,查询旧版本进程是否存在,杀死旧版本进程
sshPublisher(
failOnError: true,
publishers: [
sshPublisherDesc(
configName: "${ssh_server_name}",
sshCredentials: [
encryptedPassphrase: "${ssh_pass}",
key : '',
keyPath : '/home/jenkins/.ssh/id_rsa',
username : 'jenkins'
],
transfers: [
sshTransfer(
cleanRemote: false,
excludes: '',
remoteDirectory: "",
remoteDirectorySDF: false,
removePrefix: '',
sourceFiles: "",
execCommand: """
cd ${remote_path}
old_process_id=`ps -ef|grep "${process_name}" |grep ${old_version_port} |grep -v grep |awk '{print\$1}'`
if [[ \$old_process_id != "jenkins" ]];then
echo "failure" > temp/${uuid}.txt
echo "旧版本启动用户非jenkins,权限不足" >> temp/${uuid}.txt
exit 1
else
process_id=`ps -ef|grep "${process_name}" |grep ${old_version_port} |grep -v grep |awk '{print\$2}'`
kill -9 \$process_id
echo "success" > temp/${uuid}.txt
fi
""",
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+'
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
http_notify_offline.close()
sh """
scp jenkins@${remote_ip}:${result_path} ${WORKSPACE}
"""
def read_result = readFile(file: "${WORKSPACE}/${uuid}.txt").trim()
def lines = read_result.split('\n')
if (lines.size() <= 1) {
echo read_result
} else {
def firstLine = lines[0]
def secondLine = lines[1]
if (firstLine == "failure") {
exception_cause = "${secondLine}"
error exception_cause
} else {
echo firstLine
}
}
} else {
exception_cause = "旧版本下线失败"
error exception_cause
}
} catch (e) {
print e
if (exception_cause == "启动失败") {
exception_cause = "旧版本下线失败"
}
error exception_cause
}
}
}
}
}
post {
// always {
// script {
// if (getContext(hudson.FilePath)) {
// deleteDir()
// }
// }
// }
success {
dingtalk(
robot: 'c0dea75c-736d-40b7-8962-4eab8ad31f4c',
type: 'MARKDOWN',
title: "${job_name} - Successs",
text: [
"#### **<font size=5>[${job_name}](${JOB_URL}${BUILD_NUMBER}/console)</font>**",
'---',
"- 发布服务: intre_admin",
"- 操作用户: ${BUILD_USER}",
"- 构建时长: ${common.currentDurationTime()}",
"- 构建结果: **${log.info currentBuild.result}**"
],
atAll: true
)
script {
currentBuild.description = 'Success'
}
}
failure {
dingtalk(
robot: 'c0dea75c-736d-40b7-8962-4eab8ad31f4c',
type: 'MARKDOWN',
title: "${job_name} - ${currentBuild.result}",
text: [
"#### **<font size=5>[${job_name}](${JOB_URL}${BUILD_NUMBER}/console)</font>**",
'---',
"- 发布服务: intre_admin",
"- 操作用户: ${BUILD_USER}",
"- 构建时长: ${common.currentDurationTime()}",
"- 构建结果: **${log.error currentBuild.result}**",
"- 异常原因: ${exception_cause}"
],
atAll: true
)
script {
currentBuild.description = exception_cause
}
}
}
}