关于jenkins迁移及自动发布
2021-12-18 本文已影响0人
liurongming
一、jenkins迁移
# 执行如下三个命令即可完成迁移
scp -r /etc/sysconfig/jenkins/ 迁移机器IP:/etc/sysconfig/jenkins
scp -r /usr/lib/jenkins/ 迁移机器IP:/usr/lib/jenkins
scp -r /var/lib/jenkins/ 迁移机器IP:/var/lib/jenkins
# 改变目录权限
chown -R jenkins:jenkins /var/lib/jenkins/
# 制作自启动
scp -r /etc/rc.d/init.d/jenkins 迁移机器IP:/etc/rc.d/init.d/
# 调试启动
java -DJENKINS_HOME=/var/lib/jenkins -jar jenkins.war
# 建立非登录用户
useradd -d /var/lib/jenkins -c 'Jenkins Automation Server' jenkins -s /bin/false
二、常见自动化Pipeline
A、后端
#!/usr/bin/env groovy
pipeline {
agent any
options {
timestamps() // 日志显示时间
skipDefaultCheckout() // 禁止默认检出
disableConcurrentBuilds() // 不允许并行执行Pipeline
timeout(time: 1, unit: 'HOURS') // 设置超时时间
}
environment {
GIT_CREDENTIAL_KEY = "jenkinskprivatekey"
GIT_URL = "ssh://git@xxxx/group/project.git"
}
parameters {
string defaultValue: "172.18.5.xxx", description: '请输入将要发布的服务器', name: 'deploy_servers', trim: true
choice(name: 'build_target', choices: ['Shell', 'Docker'], description: '请选择制品方式')
booleanParam(name: 'skip_test', defaultValue: true, description: '你需要在部署之前执行自动化测试么 ?')
extendedChoice defaultValue: "xxx-gateway,xxx-system,xxx-exam,xxx-live", description: '请选择要发布的项目', multiSelectDelimiter: ',', name: 'choose_project', quoteValue: false, saveJSONParameterToFile: false, type: 'PT_CHECKBOX', value: 'xxx-gateway,xxx-system,xxx-exam,xxx-live', visibleItemCount: 10
listGitBranches branchFilter: '.*', credentialsId: "jenkinskprivatekey", defaultValue: 'refs/heads/release', name: 'choose_branch', quickFilterEnabled: false, remoteURL: "ssh://git@xxxx/group/project.git", selectedValue: 'DEFAULT', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH_TAG'
}
stages {
stage('选择环境') {
steps {
timeout(time: 10, unit: 'MINUTES') {
script {
// 编译环境检测
checkBuildEnviroment(build_target)
}
}
}
}
stage('拉取源码') {
steps {
// 根据选择分支拉取代码
checkout([$class : 'GitSCM',
branches : [[name: "${env.choose_branch}"]],
extensions : [],
userRemoteConfigs: [[credentialsId: "${GIT_CREDENTIAL_KEY}",
url : "${GIT_URL}"]]
])
script {
sh '''
# 查看修改配置
cd ${WORKSPACE}
# find ./ -type f -name '*.yml' | grep -vE 'xxx-common|xxx-api|target'|xargs cat |grep namespace
find ./ -type f -name '*.yml' | grep -vE 'xxx-common|xxx-api|target'|xargs cat |grep namespace
'''
}
}
}
stage('构建制品') {
steps {
script {
// 构建制品
doBuild(build_target)
}
}
}
stage('部署发布') {
steps {
script {
// 部署发布
doDeploy(build_target)
}
}
}
stage('结果通知') {
steps {
script {
// 结果通知
doNotify(build_target)
}
}
}
}
post {
always {
script {
sh '''
# 环境还原
cd ${WORKSPACE}
git reset --hard
'''
}
}
success {
script {
// 结果通知
echo "success======"
}
}
failure {
script {
// 结果通知
echo "failure======"
}
}
aborted {
script {
// 结果通知
echo "aborted======"
}
}
}
}
/** 执行环境检测 */
def checkBuildEnviroment(String option) {
println("选择项目:${env.choose_project}")
println("选择分支:${env.choose_branch}")
switch (option) {
case "Shell":
println("选择 Shell 原生制品")
sh '''
echo "构建环境检测"
pwd
java -version
mvn -v
'''
break
case "Docker":
println("选择 Docker 镜像制品")
sh '''
echo "构建环境检测"
pwd
java -version
mvn -v
docker -v
'''
break
}
}
/** 执行编译 */
def doBuild(String option) {
if (option == 'Docker') {
sh '''
# 镜像构建
mvn -f pom.xml clean package -Dmaven.test.skip=true -U docker:build
'''
} else {
sh '''
# 原生构建
mvn -f pom.xml clean package -Dmaven.test.skip=true -U
'''
}
}
/** 执行发布 */
def doDeploy(String option) {
if (option == 'Shell') {
echo "选择Shell部署发布中..."
def tasks = [:]
for (toServer in deploy_servers.tokenize(',')) {
def sendServer = toServer
tasks["deploying-${sendServer}"] = {
for (curProject in choose_project.tokenize(',')) {
def sendProject = curProject
println("正在 ${sendServer} 上 - 执行部署:${sendProject} ...")
dir("${sendProject}") {
def cmdDeploy = "find /home/xxx/ -maxdepth 1 -type f -name '${sendProject}*'|awk -F '/' '{print \$NF}'|xargs -i srvctl {} deploy"
sshPublisher(publishers: [sshPublisherDesc(configName: "${sendServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${cmdDeploy}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/xxx', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/*.jar')], usePrxxxtionTimestamp: false, useWorkspaceInPrxxxtion: false, verbose: true)])
}
}
}
}
parallel tasks
} else {
echo "Docker部署发布..."
}
}
/** 结果通知 */
def doNotify(String option) {
if (option == 'Shell') {
echo "结果通知..."
} else {
echo "结果通知..."
}
}
B、前端
#!/usr/bin/env groovy
pipeline {
agent any
options {
timestamps() // 日志显示时间
skipDefaultCheckout() // 禁止默认检出
disableConcurrentBuilds() // 不允许并行执行Pipeline
timeout(time: 1, unit: 'HOURS') // 设置超时时间
}
environment {
GIT_CREDENTIAL_KEY = "jenkinskprivatekey"
GIT_URL = "ssh://git@xxx.git"
}
parameters {
string defaultValue: "172.18.5.xxx", description: '请输入将要发布的服务器', name: 'deploy_servers', trim: true
choice(name: 'build_target', choices: ['Shell', 'Docker'], description: '请选择制品方式')
listGitBranches branchFilter: '.*', credentialsId: "jenkinskprivatekey", defaultValue: 'refs/heads/release', name: 'choose_branch', quickFilterEnabled: false, remoteURL: "ssh://git@xxx.git", selectedValue: 'DEFAULT', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH_TAG'
}
stages {
stage('选择环境') {
steps {
timeout(time: 10, unit: 'MINUTES') {
script {
// 编译环境检测
checkBuildEnviroment(build_target)
}
}
}
}
stage('拉取源码') {
steps {
// 根据选择分支拉取代码
checkout([$class : 'GitSCM',
branches : [[name: "${env.choose_branch}"]],
extensions : [],
userRemoteConfigs: [[credentialsId: "${GIT_CREDENTIAL_KEY}",
url : "${GIT_URL}"]]
])
}
}
stage('构建制品') {
steps {
script {
// 构建制品
doBuild(build_target)
}
}
}
stage('部署发布') {
steps {
script {
// 部署发布
doDeploy(build_target)
}
}
}
stage('结果通知') {
steps {
script {
// 结果通知
doNotify(build_target)
}
}
}
}
post {
always {
script {
sh '''
# 环境还原
cd ${WORKSPACE}
git reset --hard
'''
}
}
success {
script {
// 结果通知
echo "success======"
}
}
failure {
script {
// 结果通知
echo "failure======"
}
}
aborted {
script {
// 结果通知
echo "aborted======"
}
}
}
}
/** 执行环境检测 */
def checkBuildEnviroment(String option) {
println("选择项目:${env.choose_project}")
println("选择分支:${env.choose_branch}")
switch (option) {
case "Shell":
println("选择 Shell 原生制品")
sh '''
echo "构建环境检测"
pwd
node -v
'''
break
case "Docker":
println("选择 Docker 镜像制品")
sh '''
echo "构建环境检测"
pwd
node -v
docker -v
'''
break
}
}
/** 执行编译 */
def doBuild(String option) {
if (option == 'Docker') {
sh '''
# 镜像构建
echo "docker build"
'''
} else {
sh '''
# 原生构建
cd $WORKSPACE && rm -rf dist && yarn && yarn build
'''
}
}
/** 执行发布 */
def doDeploy(String option) {
if (option == 'Shell') {
echo "选择Shell部署发布中..."
def tasks = [:]
for (toServer in deploy_servers.tokenize(',')) {
def sendServer = toServer
tasks["deploying-${sendServer}"] = {
println("正在 ${sendServer} 上 - 执行部署 ...")
dir("${WORKSPACE}") {
sh '''
tar czf dist.tar.gz dist/
'''
def cmdDeploy = "webctl deploy"
sshPublisher(publishers: [sshPublisherDesc(configName: "${sendServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${cmdDeploy}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/omo', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'dist.tar.gz')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
}
}
}
parallel tasks
} else {
echo "Docker部署发布..."
}
}
/** 结果通知 */
def doNotify(String option) {
if (option == 'Shell') {
echo "结果通知..."
} else {
echo "结果通知..."
}
}
三、对应Shell
A、后端
#!/bin/sh
# 当前时间
CUR_YEAR=$(date "+%Y")
CUR_MONTH=$(date "+%Y%m")
CUR_DATE=$(date "+%Y%m%d")
CUR_TIME=$(date "+%H%M%S")
CUR_DATE_TIME="${CUR_DATE}_${CUR_TIME}"
# 项目名称
# 取命令行第3个参数
# 默认取值:'pj_xxx'
IN_PJ_NMAE=$3
PJ_NAME=${IN_PJ_NAME:-'pj_xxx'}
# 应用文件
APP_NAME=`echo $1 |awk -F '/' '{print $NF}'`
# 运行环境
# 取命令行第4个参数
# 默认取值:'dev'
IN_CE=$4
CE=${IN_CE:-'sit'}
# 部署目录
APP_DIR="/online/${PJ_NAME}"
# 上传目录
UPLOAD_DIR="/home/${PJ_NAME}"
# 备份路径
BACKUP_DIR="/backup/${PJ_NAME}"
# 指定日志路径
LOG_DIR="/home/srv.log"
# 内存参数
# 取命令行第5个参数
# 默认取MEM_OPTS值
IN_MEM_OPTS=$5
MEM_OPTS=${IN_MEM_OPTS:-"-Xms512m -Xmx512m -Xss1024K -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m"}
# Encoding
ENCODING_PARAM="-Dfile.encoding=UTF-8"
# 使用说明,用来提示输入参数
usage() {
echo "Usage: srvctl app_name.jar [start|stop|restart|status|backup|deploy|setup|syn] [pj_name(default sywn) ce(default dev) mem_opts(default -Xms512m)]"
exit 1
}
# 检查程序是否在运行
is_exist() {
pid=`ps -ef |grep -w ${APP_NAME} |grep -vE 'grep|sh' |awk '{print \$2}' `
# 如果不存在返回1,存在返回0
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}
# 部署
deploy(){
# 加载环境变量
source /etc/profile && env
echo ${CUR_DATE_TIME} >>${LOG_DIR}
echo "${APP_NAME}" >>${LOG_DIR}
echo "$PATH" >> ${LOG_DIR}
echo "${NACOS_NAMESPACE}"-"${CE}">>${LOG_DIR}
whoami >>${LOG_DIR}
echo "--------------------------------">> ${LOG_DIR}
backup
syn && restart
}
# 初始化安装目录
function setup(){
# 应用文件放在
# /online/应用名/
# 临时备份放在
# 备份按日期时间版本归档压缩tar.gz
# /backup/应用名/
# 日志文件放在
# on - 在线日志
# off - 归档日志
# /data/logs/应用名/{on,off}/
# core文件放在
# hot - 可对外
# ice - 非对外
# /data/corefile/应用名/{hot,ice}/{日期}/{分类}/
# 持久化备份放在
# 归档压缩tar.gz文件
# keep - 在运行
# fixed - 已归档
# 备份按日期时间归档压缩tar.gz
# /data/backup/应用名/{keep,fixed}/{日期}/{分类}/
mkdir -p /{online,backup}/${PJ_NAME}
mkdir -p /data/{logs/${PJ_NAME}/{on,off},corefile/${PJ_NAME}/{hot,ice},backup/${PJ_NAME}/{keep,fixed}}
}
# 同步文件
syn() {
# 判断上传文件目录是否存在
if [ ! -d ${UPLOAD_DIR} ];then
echo "${UPLOAD_DIR} is not exist"
exit 1
fi
# 判断文件
cd ${UPLOAD_DIR}
if [ ! -f ${APP_NAME} ];then
echo "${APP_NAME} is not exist"
exit 2
fi
# 拷贝文件
rsync -a --delete ${UPLOAD_DIR}/${APP_NAME} ${APP_DIR}/
if [ $? -eq "0" ]; then
echo "sync ${APP_NAME} success"
return 0
else
echo "sync ${APP_NAME} failed!"
return 1
fi
}
# 临时备份
backup() {
# 判断文件
cd ${APP_DIR}
if [ ! -f ${APP_NAME} ];then
echo "${APP_NAME} is not exist"
return 1
fi
if [ ! -d ${BACKUP_DIR} ];then
echo "${APP_NAME} is not exist"
mkdir -p ${BACKUP_DIR}
fi
# 备份
tar -czf $BACKUP_DIR/${APP_NAME}.${CUR_DATE_TIME}.tar.gz ${APP_NAME}
if [ $? -ne "0" ]; then
echo "${APP_NAME} tar is not ok"
return 1
fi
# 每日仅保留10个版本
cd ${BACKUP_DIR}
ls -lt |grep ${APP_NAME}|grep ${CUR_DATE} |grep -vE '*.jar$'|awk 'NR>10 {print $NF}' |xargs -i mv {} /tmp
# 每月仅保留30个版本
ls -lt |grep ${APP_NAME}|grep ${CUR_MONTH} |grep -vE '*.jar$'|awk 'NR>30 {print $NF}' |xargs -i mv {} /tmp
# 提示
echo "$APP_NAME backup is done!"
}
# 启动方法
start(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is already running. pid=${pid} ."
else
# 判断文件
cd ${APP_DIR}
if [ ! -f ${APP_NAME} ];then
echo "${APP_NAME} is not exist"
echo "${APP_NAME} start failed"
exit 1
fi
nohup java -server $MEM_OPTS $ENCODING_PARAM -jar ${APP_DIR}/${APP_NAME} --spring.profiles.active=${CE} >/dev/null 2>&1 &
if [ $? -eq "0" ];then
echo "Belong to project ${PJ_NAME}"
echo "Starting in ${APP_DIR}"
echo "Starting CE is ${CE}"
echo "Starting MEM_OPTS is ${MEM_OPTS}"
echo "${APP_NAME} start success"
else
echo "Belong to project ${PJ_NAME}"
echo "Starting in ${APP_DIR}"
echo "Starting CE is ${CE}"
echo "Starting MEM_OPTS is ${MEM_OPTS}"
echo "${APP_NAME} start failed"
fi
fi
}
# 停止方法
stop(){
is_exist
if [ $? -eq "0" ]; then
kill -9 $pid
else
echo "${APP_NAME} is stop."
fi
}
# 输出运行状态
status(){
is_exist
if [ $? -eq "0" ]; then
echo "The ${APP_NAME} is running and pid is ${pid}"
else
echo "${APP_NAME} is stop."
fi
}
# 重启
restart(){
stop
start
}
# 根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$2" in
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
"backup")
backup
;;
"deploy")
deploy
;;
"setup")
setup
;;
"syn")
syn
;;
*)
usage
;;
esac
B、前端
#!/bin/sh
# 当前时间
CUR_YEAR=$(date "+%Y")
CUR_MONTH=$(date "+%Y%m")
CUR_DATE=$(date "+%Y%m%d")
CUR_TIME=$(date "+%H%M%S")
CUR_DATE_TIME="${CUR_DATE}_${CUR_TIME}"
# 项目名称
# 取命令行第3个参数
# 默认取值:'pj_xxx'
IN_PJ_NMAE=$2
PJ_NAME=${IN_PJ_NAME:-'pj_xxx'}
# 应用文件
APP_NAME='dist'
# 部署目录
APP_DIR="/online/${PJ_NAME}"
# 上传目录
UPLOAD_DIR="/home/${PJ_NAME}"
# 备份路径
BACKUP_DIR="/backup/${PJ_NAME}"
# 使用说明,用来提示输入参数
usage() {
echo "Usage: webctl [deploy|setup] [pj_name(default pj_xxx)]"
exit 1
}
# 初始化安装目录
function setup(){
# 应用文件放在
# /online/应用名/
# 临时备份放在
# 备份按日期时间版本归档压缩tar.gz
# /backup/应用名/
# 日志文件放在
# on - 在线日志
# off - 归档日志
# /data/logs/应用名/{on,off}/
# core文件放在
# hot - 可对外
# ice - 非对外
# /data/corefile/应用名/{hot,ice}/{日期}/{分类}/
# 持久化备份放在
# 归档压缩tar.gz文件
# keep - 在运行
# fixed - 已归档
# 备份按日期时间归档压缩tar.gz
# /data/backup/应用名/{keep,fixed}/{日期}/{分类}/
mkdir -p /{online,backup}/${PJ_NAME}
mkdir -p /data/{logs/${PJ_NAME}/{on,off},corefile/${PJ_NAME}/{hot,ice},backup/${PJ_NAME}/{keep,fixed}}
}
# 同步文件
deploy() {
# 判断上传文件目录是否存在
if [ ! -d ${UPLOAD_DIR} ];then
echo "${UPLOAD_DIR} is not exist"
exit 1
fi
# 解压文件
cd ${UPLOAD_DIR} && tar zxvf dist.tar.gz
# 判断文件
cd ${UPLOAD_DIR}
if [ ! -d ${APP_NAME} ];then
echo "${APP_NAME} is not exist"
exit 2
fi
# 拷贝文件
rsync -a --delete ${UPLOAD_DIR}/${APP_NAME} ${APP_DIR}/
if [ $? -eq "0" ]; then
echo "sync ${APP_NAME} success"
cd ${UPLOAD_DIR}
[ -d ${APP_NAME} ] && rm dist -rf
return 0
else
echo "sync ${APP_NAME} failed!"
cd ${UPLOAD_DIR}
[ -d ${APP_NAME} ] && rm dist -rf
return 1
fi
}
# 根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"deploy")
deploy
;;
"setup")
setup
;;
*)
usage
;;
esac

M2_HOME
/usr/local/maven
NODE_HOME
/usr/local/node
PATH+EXTRA
$PATH:$M2_HOME/bin:$NODE_HOME/bin