回滚方案:

一般回滚方案:

• 重新走一步流程发布上一次版本代码

• 部署之前备份程序文件

回滚流程设计:

回滚实现步骤:

安装Extended Choice Parameter插件并配置生成对应的语法

生成连接Jenkins服务器用户名和密码语法

此处是使用下面参数生成的凭据语法(避免脚本中用户名密码的敏感信息泄露)

1、在原有Pipeline脚本中增加生成“备份版本记录文件”脚本

备份的文件名格式最好是统一规范的,例如:项目名-文件名-日期-构建编号
项目名:当前创建的项目
文件名:部署war包的名称(ROOT.war)
日期:date +%F
构建编号:jenkins自带的变量(build_number)

#备份版本记录文件的生成:
ls /data/backup |awk '{print $0}'
ls /data/backup |awk '{print $0","}'
ls /data/backup |awk '{printf $0","}'
ls /data/backup |awk 'BEGIN{printf "abc="}{printf $0","}'
#\转义,不让jenkins解析变量,而是shell本身的变量
ls /data/backup |awk 'BEGIN{printf "abc="}{printf \$0","}' > /tmp/backup_version.txt
ls /data/backup |sort -t - -k 6 -nr |head -n 3|awk 'BEGIN{printf "abc="}{printf \$0","}' > /tmp/backup_version.txt

#ssh实现免交互推送backup_version.txt文件到jenkins服务器
sshpass -p"123.com" scp /tmp/backup_version.tx t root@x.x.x.x:/opt/jenkins_home/

新建项目–>配置–>最下面配置流水线脚本进行发布项目

pipeline {
agent {
label "task1"
}
environment {
git_address = "http://gitlab.ctnrs.com/my-group/ansible.git"
git_password = "4cfc8d8f-489b-4c29-9f10-4a1b265e0cb3"
ansible_ssh_auth = "a14ad8a7-5ad6-43de-bac6-221000c8015c"
}
parameters {
gitParameter branch: '', branchFilter: '.*', defaultValue: 'master', description: '请选择你要发布的分支', name: 'branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
choice choices: ['webservers1', 'webservers2', 'webservers3'], description: '''灰度发布策略,分批次部署
webservers1
192.168.0.2
192.168.0.5
webservers2
192.168.0.2
webservers3
192.168.0.5''', name: 'ENV'
}

triggers {
pollSCM '*/1 * * * * '
}
stages {
stage('拉取代码') {
steps {
checkout([$class: 'GitSCM', branches: [[name: "${params.branch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "$git_password", url: "$git_address"]]])
}
}
stage('编译构建') {
steps {
sh "/usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true"
}
}
stage('拷贝构建文件到远程主机并部署') {
steps {
// 读取连接Jenkins服务器用户名和密码
withCredentials([usernamePassword(credentialsId: 'ad3b7fb3-897e-4dc3-a7cf-0975ffc7b4ec', passwordVariable: 'jenkinspass', usernameVariable: 'jenkinsroot')]) {
// ===========================================
sh """
###################### 主机清单文件 ############################
cat > /opt/jenkins/.hosts << EOF
[webservers1]
192.168.0.2
192.168.0.5
[webservers2]
192.168.0.2
[webservers3]
192.168.0.5
EOF
###################### Playbook文件 ############################
cat > /opt/jenkins/.playbook.yaml << "EOF"
- hosts: $ENV # Jenkins参数化变量
gather_facts: no
vars: # 定义playbook变量,下面{{}}引用这里的变量
workspace: $WORKSPACE # WORKSPACE和BUILD_NUMBER引用Jenkins变量
build_number: $BUILD_NUMBER
tomcat_dir: "/usr/local/tomcat" # 自定义变量
backup_dir: "/data/backup"
backup_filename: "demo-ROOT-\$(date +%F)-{{ build_number }}.war" # 格式:项目名-文件名-日期-构建编号
jenkins_ip: "192.168.0.4"
username: $jenkinsroot
password: $jenkinspass
tasks:
- name: 推送部署包到远程服务器
copy: src="{{ item }}" dest={{ tomcat_dir }}/webapps
with_fileglob:
- "{{ workspace }}/target/*.war"
- name: 部署新程序并重启Tomcat
shell: |
cd {{ tomcat_dir }}/webapps
mv ROOT.war {{ backup_dir }}/{{ backup_filename }}
mv *.war ROOT.war
pid=\$(ps -ef |grep {{ tomcat_dir }} |egrep -v 'grep' |awk '{print \$2}')
[ -n "\$pid" ] && kill -9 \$pid
export JAVA_HOME=/usr/local/jdk
nohup {{ tomcat_dir }}/bin/startup.sh
ls /data/backup |sort -t - -k 6 -nr |head -n 3|awk 'BEGIN{printf "abc="}{printf \$0","}' > /tmp/backup_version.txt
sshpass -p{{ password }} scp /tmp/backup_version.txt {{ username }}@{{ jenkins_ip }}:/opt/jenkins_home/
EOF
"""
// ===========================================
}
ansiblePlaybook(
playbook: '/opt/jenkins/.playbook.yaml',
inventory: '/opt/jenkins/.hosts',
credentialsId: "${ansible_ssh_auth}"
)
}
}
}
}

2、在Jenkins创建一个独立的回滚项目

新建项目–>配置–>最下面配置流水线脚本(回滚项目中部署脚本只做根据选择的备份版本名称重新部署)

pipeline {
agent {
label "task1"
}
environment {
ansible_ssh_auth = "a14ad8a7-5ad6-43de-bac6-221000c8015c"
}
parameters {
extendedChoice description: '请选择你要回滚的版本', multiSelectDelimiter: ',', name: 'RollbackVersion', propertyFile: '/var/jenkins_home/backup_version.txt', propertyKey: 'abc', quoteValue: false, saveJSONParameterToFile: false, type: 'PT_SINGLE_SELECT', visibleItemCount: 5
choice choices: ['webservers1', 'webservers2', 'webservers3'], description: '''灰度发布策略,分批次部署
webservers1
192.168.0.2
192.168.0.5
webservers2
192.168.0.2
webservers3
192.168.0.5''', name: 'ENV'
}

stages {
stage('拷贝构建文件到远程主机并部署') {
steps {

// ===========================================
sh """
###################### 主机清单文件 ############################
cat > /opt/jenkins/.hosts << EOF
[webservers1]
192.168.0.2
192.168.0.5
[webservers2]
192.168.0.2
[webservers3]
192.168.0.5
EOF
###################### Playbook文件 ############################
cat > /opt/jenkins/.playbook.yaml << "EOF"
- hosts: $ENV # Jenkins参数化变量
gather_facts: no
vars: # 定义playbook变量,下面{{}}引用这里的变量
workspace: $WORKSPACE # WORKSPACE和BUILD_NUMBER引用Jenkins变量
tomcat_dir: "/usr/local/tomcat" # 自定义变量
backup_dir: "/data/backup"
backup_filename: $RollbackVersion
tasks:
- name: 恢复备份程序并重启tomcat
# 脚本中\$必须转义,否则会认为是Jenkins变量
shell: |
cd {{ tomcat_dir }}/webapps
[ ! -d {{ backup_dir }} ] && mkdir {{ backup_dir }} -p
[ ! -d /tmp/new_root ] && mkdir /tmp/new_root
mv ROOT.war /tmp/new_root
mv {{ backup_dir }}/{{ backup_filename }} ROOT.war
pid=\$(ps -ef |grep {{ tomcat_dir }} |egrep -v 'grep' |awk '{print \$2}')
[ -n "\$pid" ] && kill -9 \$pid
export JAVA_HOME=/usr/local/jdk
nohup {{ tomcat_dir }}/bin/startup.sh
EOF
"""
// ===========================================
ansiblePlaybook(
playbook: '/opt/jenkins/.playbook.yaml',
inventory: '/opt/jenkins/.hosts',
credentialsId: "${ansible_ssh_auth}"
)
}
}
}
}

最终实现如下:

3.验证结果:

模拟场景:公司一套软件要发布上线,代码测试后发布开始构建部署,此次代码版本稳定运行。后期更新了新的功能,要开始第二次的构建部署(代码更新),发布上线后出现了严重的bug,需要回退到上一个版本,那么此时进入回滚项目,选择要回滚的代码版本即可回滚到上一个版本,保障业务能够正常运行。

①发布一次构建代码(查看页面功能)—>②修改代码—>再次构建代码(查看页面功能出现了严重bug)—>③开始回滚—>选择要回滚的上一次的代码版本—>开始构建