二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Nexus系列文章

  • Jenkins系列文章

    • Jenkins入门系列笔记汇总整理
    • 前言与介绍
    • Jenkins初始部署与简单配置
    • Jenkins各配置选项介绍
    • Jenkins中一个项目的构建
    • Jenkins配置项目构建的钉钉通知
    • Jenkins忘记管理员密码怎么办
    • Jenkins与gitlab的交互探微
    • Jenkins根目录详解
    • Jenkins插件之显示构建时间
    • Jenkins插件之批量修改配置
    • Jenkins配置简单构建邮件推送
    • Jenkins复杂邮件推送配置详解
    • Jenkins配置复杂构建邮件推送
    • Jenkins构建安卓项目之前的一些唠叨
    • Jenkins构建安卓项目配置
    • Jenkins与Gitlab分支的交互
    • Jenkins构建nodejs项目
    • 使用docker部署Jenkins及初始配置
    • 配置gitlab提交代码Jenkins自动构建
    • Jenkins回滚方案探微
    • Jenkins角色控制(小黄锁)探微
    • Jenkins构建的应用配置问题解决探微
    • Jenkins构建中tag的应用
    • Jenkins插件之Ansicolor(神器)
    • 最基础核心的Jenkins功能部署一个java应用
    • Jenkins+sonar构建代码扫描
    • Jenkins+docker+gitlab将应用部署到docker
    • Jenkins参数化构建犀利插件Active-Choices-Plugin
    • 记一次将代码中参数外显到构建历史中的操作
    • Jenkins升级与迁移的经验分享
    • pipeline笔记之从一个简单的项目构建开始
    • Jenkinsfile声明式语法详解
    • 自动构建的原始配置以及pipeline中的用法
    • 多分支构建的实践与思考
    • 使用Jenkinsfile类前端项目的部署与回滚
    • 如何在Jenkinsfile中定义一个全局的时间戳变量
    • Jenkins中自由风格回滚方案的最佳实践
    • Jenkins中pipeline风格回滚方案的最佳实践
      • 1,基于 pipeline 的定制化单机版本发布回滚配置管理
      • 2,基于 pipeline 结合 ansible 的多主机批量发布回滚配置管理
      • 3,优化方案
    • pipeline结合ansible剧本进行批量的部署与回滚配置
    • 最近配置安卓iOS打包本地化流程中一些值得记录的内容
    • pipeline中如何在environment环节声明一个含有通配符的变量
    • git-Parameter插件在pipeline共享库中的实践详解
    • jenkins作为ci检测代码是否合并的实践
    • 将Jenkins共享库的Jenkinsfile放到ci静态检测的实践
    • Jenkins的pipeline实践之GitSCM参数配置项详解
    • Jenkins中pipeline对接CMDB接口获取主机列表的发布实践
    • Jenkins有任务无法kill提示即将关闭
    • Jenkins基于Share Library共享库的最佳实践探索
    • Jenkins结合MySql Database插件的平台化实践思路
    • Jenkins-Groovy中三元表达式的用法
    • Jenkins-Groovy中Switch的高阶用法
    • Jenkins-pipeline之利用activity choice插件对接查询MySQL数据实现动态参数化的功能
    • CentOS通过yum快速安装Jenkins
    • Jenkins-pipeline语法之错误处理详解(文末有干货)
    • Jenkins常用插件汇总以及简单介绍
    • Jenkins所遇报错汇总及解决
    • Jenkins管理维护运维规范
  • ELK笔记

  • Kubernetes笔记

  • LLM专题

  • 系列专题
  • Jenkins系列文章
二丫讲梵
2019-12-14
目录

Jenkins中pipeline风格回滚方案的最佳实践

文章发布较早,内容可能过时,阅读注意甄别。

传统的自由风格完成之后,来到流水线的配置事实上已经非常简单了,主题核心代码变化不多,只需要遵照流水线的语法合理配置使用即可,所以废话不多说,直接分享代码出来。

# 1,基于 pipeline 的定制化单机版本发布回滚配置管理

仍旧在原来代码的基础之上,将部署方式更改为流水线风格,目前实验做下来,与上边自由风格对比,大概有如下几点不同:

  • 1,时间戳不太容易定义,上边时间戳也只是在项目版本目录定义的时候使用,能够便于日常排查确认版本,不过现在流水线里边时间戳不太容易定义了,就干掉了时间戳为前缀,更改为项目名作为版本的前缀。
  • 2,目前来看流水线中通过 cat 写入脚本不是十分理想,于是这个脚本固定放在了项目工作目录。
  • 3,其他基本一样,部署也是一样,一般情况下,都采用手动部署的方式,部署的时候,直接填入将要构建的分支,直接填入即可触发构建。
  • 4,关于自动构建,可以在测试环境引用,不建议在线上环境中引用。

Jenkinsfile内容:

pipeline {
    agent any
    environment {
        project="admin-pipeline"
        git_url = "git@10.3.0.42:jenkins-learn/breeze-college.git"
        remote_port="22"
        remote_user="root"
        remote_ip="10.3.0.42"
        project_dir="/data/www/${project}"
        version_dir="/release/$project/${project}_${BUILD_ID}"
    }
    options {
        timestamps()
        disableConcurrentBuilds()
        timeout(time: 10, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '10'))
    }
    triggers{
        gitlab( triggerOnPush: true,
                triggerOnMergeRequest: true,
                branchFilterType: 'All',
                secretToken: "${env.git_token}")
    }
    parameters {
        string(name: 'branch', defaultValue: 'master', description: '请输入将要构建的代码分支')
        choice(name: 'mode', choices: ['deploy','rollback'], description: '请选择发布或者回滚?')
        string(name: 'version_id', defaultValue: '0', description: '回滚时用,默认回滚到上一次构建,如需要回滚到更早构建,请输入对应构建ID,只支持最近五次构建的回滚,部署请忽略此参数')
    }
    stages {
        stage('拉取代码') {
            steps {
                echo 'Checkout'
                script {
                    try {
                        git branch: "${branch}",url: "${git_url}"
                    }catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('定义版本管理脚本'){
            steps{
                script{
                    try {
sh '''
cat > keepfive.sh << 'EOF'
file_path="/release/admin-pipeline"
while true;do
A=`ls ${file_path} | wc -l`
B=`ls -lrX ${file_path} | tail -n 1 | awk '{print $9}'`
if [ $A -gt 5 ];then rm -rf ${file_path}/$B;else break;fi;done
EOF
'''
                    }catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('部署') {
            when {
                environment name: 'mode',value: 'deploy'
            }
            steps {
                script {
                    ansiColor('xterm') {
                    try {
                        sh '''
                        echo "创建远程主机上的版本目录 :${version_dir}"
                        ssh -p $remote_port $remote_user@$remote_ip "mkdir -p ${version_dir}"
                        [ $? != 0 ] && echo "请注意,在创建远程主机上的版本目录时出错,故而退出构建,可联系运维同学处理!" && exit 1
                        echo "将代码同步到远程主机版本目录"
                        rsync -az -e "ssh -p $remote_port" --exclude='Jenkinsfile' --exclude='keepfive.sh' --delete ${WORKSPACE}/  $remote_user@$remote_ip:$version_dir/
                        [ $? != 0 ] && echo "请注意,在执行同步代码到版本目录时出错,故而退出构建,可联系运维同学处理!" && exit 1
                        echo "将代码同步到远程主机版本目录成功!"
                        echo "将项目部署到生产目录"
                        ssh -p $remote_port $remote_user@$remote_ip "ln -snf $version_dir $project_dir"
                        [ $? != 0 ] && echo "请注意,在将项目部署到生产目录时出错,故而退出构建,可联系运维同学处理!" && exit 1
                        echo "将项目部署到生产目录成功!"
                        echo "使版本目录保持五个版本历史"
                        ssh -p $remote_port $remote_user@$remote_ip sh < keepfive.sh
                        [ $? != 0 ] && echo "请注意,在执行版本清理时出错,将会影响回滚,故而退出构建,可联系运维同学处理!" && exit 1
                        echo "执行版本清理成功!"
                        echo "同步版本号到本地"
                        [ ! -d /root/.jenkins/version/$project ] && mkdir -p /root/.jenkins/version/$project
                        ssh -p $remote_port $remote_user@$remote_ip "ls /release/$project" > /root/.jenkins/version/$project/version.log
                        echo "============"
                        echo "上线部署完成!"
                        echo "============"
                        '''
                    } catch(err) {
                        echo "${err}"
                    }
                }
                }
            }
        }
        stage('回滚') {
            when {
                environment name: 'mode',value: 'rollback'
            }
            steps {
                script {
                    try {
                        sh '''
                            if [ ${version_id} == "0" ];then
                                echo "选择回滚的版本是默认,将回滚到上次制品,回滚即将进行..."
                                Version="/release/$project/`tail -n2 /root/.jenkins/version/$project/version.log | head -n1`"
                                ssh -p $remote_port $remote_user@$remote_ip "ln -snf $Version $project_dir"
                                [ $? != 0 ] && echo "请注意,在执行回滚时出错,故而退出构建,可立即联系运维同学处理!" && exit 1
                                echo "=============="
                                echo "项目已回滚完成!"
                                echo "=============="
                            else
                                echo "选择回滚的版本是:${version_id},将回滚到 ${version_id} 的制品,回滚即将进行..."
                                Version="/release/$project/`grep "_$version_id" /root/.jenkins/version/$project/version.log`"
                                ssh -p $remote_port $remote_user@$remote_ip "ln -snf $Version $project_dir"
                                [ $? != 0 ] && echo "请注意,在执行回滚时出错,故而退出构建,可立即联系运维同学处理!" && exit 1
                                echo "项目回滚到 ${version_id} 完成!"
                            fi
                        '''
                    } catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
    }
    post {
        success {
            dingTalk accessToken:'https://oapi.dingtalk.com/robot/send?access_token=改成自己的',
            imageUrl:'https://ae01.alicdn.com/kf/Hdfe28d2621434a1eb821ac6327b768e79.png',
            jenkinsUrl: "${env.JENKINS_URL}",
            message:'构建成功 ✅',
            notifyPeople:'李启龙'
        }
        failure {
            dingTalk accessToken:'https://oapi.dingtalk.com/robot/send?access_token=改成自己的',
            imageUrl:'https://ae01.alicdn.com/kf/Hdfe28d2621434a1eb821ac6327b768e79.png',
            jenkinsUrl: "${env.JENKINS_URL}",
            message:'构建失败 ❌',
            notifyPeople:'李启龙'
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

目前采用推送代码自动构建的方式,回滚的方式与上边自由风格一致。基本上各个步骤也都还比较清晰,这里只是把主体部署与回滚的步骤给定义了,如果自己生产当中还有其他的场景,可以自行配置添加。

img

后来又在一个地方学到了时间戳的定义方式,真正使用起来,似乎也并不复杂:

def createVersion() {
    return new Date().format('yyyyMMddHHmmss') + "_${env.BUILD_ID}"
}
pipeline {
    agent any
    environment {
        _version = createVersion()
    }
    stages {
        stage ("test") {
            steps {
                echo ${_version}
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

如果引用这一变量,只需调整一下上边脚本引用的变量即可。

# 2,基于 pipeline 结合 ansible 的多主机批量发布回滚配置管理

流水线会涉及到更多内容,脚本如下:

def createVersion() {
    return new Date().format('yyyyMMddHHmmss') + "_${env.BUILD_ID}"
}
pipeline {
    agent any
    environment {
        _version = createVersion()
        // 需要修改此处,定义项目名称
        project="admin-ansible"
        // 定义项目git地址
        git_url = "git@10.3.0.42:jenkins-learn/breeze-college.git"
        remote_port="22"
        remote_user="root"
        // 定义项目的webroot目录
        project_dir="/data/www/${project}"
        //定义项目的版本目录,一般不用更改
        version_dir="/release/$project/${_version}"
    }
    options {
        timestamps()
        disableConcurrentBuilds()
        timeout(time: 10, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '10'))
    }
    triggers{
        gitlab( triggerOnPush: true,
                triggerOnMergeRequest: true,
                branchFilterType: 'All',
                secretToken: "${env.git_token}")
    }
    parameters {
        string(name: 'branch', defaultValue: 'master', description: '请输入将要构建的代码分支')
        choice(name: 'mode', choices: ['deploy','rollback'], description: '请选择发布或者回滚?')
        string(name: 'version_id', defaultValue: '0', description: '回滚时用,默认回滚到上一次构建,如需要回滚到更早构建,请输入对应构建ID,只支持最近五次构建的回滚,部署请忽略此参数')
        choice(name: 'remote_ip', choices: ['all','10.3.9.32','10.3.20.4'], description: '选择要发布的主机')
    }
    stages {
        stage('拉取代码') {
            steps {
                echo 'Checkout'
                script {
                    try {
                        git branch: "${branch}",url: "${git_url}"
                    }catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('定义版本管理脚本'){
            steps{
                script{
                    try {
sh '''
cat > keepfive.sh << 'EOF'
file_path="/release/admin-pipeline"
while true;do
A=`ls ${file_path} | wc -l`
B=`ls -lrX ${file_path} | tail -n 1 | awk '{print $9}'`
if [ $A -gt 5 ];then rm -rf ${file_path}/$B;else break;fi;done
EOF
'''
                    }catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('定义部署主机列表'){
            steps{
                script{
                    try{
                        sh '''
                        if [ $remote_ip == "all" ];then
cat > hosts.ini << EOF
[remote]
10.3.9.32 ansible_port=34222
10.3.20.4 ansible_port=34222
EOF
else
cat > hosts.ini << EOF
[remote]
$remote_ip ansible_port=34222
EOF
fi
'''
                    }catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('定义部署剧本'){
            steps{
                script{
                    try {
sh '''
cat > deploy.yml << EOF
---
- hosts: "remote"
  remote_user: root
  tasks:
    - name: "创建远程主机上的版本目录"
      file: path=/release/${project}/${_version} state=directory
    - name: "将代码同步到远程主机版本目录"
      synchronize:
        src: ${WORKSPACE}/
        dest: /release/${project}/${_version}/
        rsync_opts: --exclude-from=excludefile
    - name: "将项目部署到生产目录"
      file: path=/data/www/${project} state=link src=/release/${project}/${_version}
    - name: "使版本目录保持五个版本历史"
      script: keepfive.sh
    - name: "生成远程版本号"
      shell: "ls /release/${project} > /release/version.log"
    - name: "同步版本号到本地"
      synchronize: "src=/release/version.log dest=/root/.jenkins/version/${project}/version.log mode=pull"
EOF
'''
                    }catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('定义忽略文件'){
            steps{
                script{
                    try {
sh '''
cat > excludefile << EOF
hosts.ini
deploy.yml
Jenkinsfile
keepfive.sh
excludefile
rollback.yml
EOF
'''
                    }catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('部署') {
            when {
                environment name: 'mode',value: 'deploy'
            }
            steps {
                script {
                    try {
                        sh '''
                        ansible-playbook -i hosts.ini deploy.yml
                        '''
                    } catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
        stage('回滚') {
            when {
                environment name: 'mode',value: 'rollback'
            }
            steps {
                script {
                    try{
                        if (params.version_id == '0'){
                            sh '''
                                echo "选择回滚的版本是默认,将回滚到上次制品,回滚即将进行..."
                                Version="/release/$project/`tail -n2 /root/.jenkins/version/$project/version.log | head -n1`"
cat > rollback.yml << EOF
---
- hosts: "remote"
  remote_user: root
  tasks:
    - name: "将项目回滚到对应期望的构建"
      file: path=/data/www/${project} state=link src=${Version}
EOF
                                ansible-playbook -i hosts.ini rollback.yml
                                echo "=============="
                                echo "项目已回滚完成!"
                                echo "=============="
                                '''
                        } else{
                            sh '''
                                echo "选择回滚的版本是:${version_id},将回滚到 ${version_id} 的制品,回滚即将进行..."
                                Version="/release/$project/`grep "_$version_id" /root/.jenkins/version/$project/version.log`"
cat > rollback.yml << EOF
---
- hosts: "remote"
  remote_user: root
  tasks:
    - name: "将项目回滚到对应期望的构建"
      file: path=/data/www/${project} state=link src=${Version}
EOF
                                ansible-playbook -i hosts.ini rollback.yml
                                echo "项目回滚到 ${version_id} 完成!"
                                '''
                        }
                    } catch(err) {
                        echo "${err}"
                    }
                }
            }
        }
    }
    post {
        success {
            dingTalk accessToken:'https://oapi.dingtalk.com/robot/send?access_token=改成自己的',
            imageUrl:'https://ae01.alicdn.com/kf/Hdfe28d2621434a1eb821ac6327b768e79.png',
            jenkinsUrl: "${env.JENKINS_URL}",
            message:'构建成功 ✅',
            notifyPeople:'李启龙'
        }
        failure {
            dingTalk accessToken:'https://oapi.dingtalk.com/robot/send?access_token=改成自己的',
            imageUrl:'https://ae01.alicdn.com/kf/Hdfe28d2621434a1eb821ac6327b768e79.png',
            jenkinsUrl: "${env.JENKINS_URL}",
            message:'构建失败 ❌',
            notifyPeople:'李启龙'
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

依旧是把所有内容都集成在一个脚本当中,方便整体维护管理。

# 3,优化方案

在完成这个实验之后,我又继续前进,将脚本当中用到的 ansible 剧本进行了一波优化调整,以简化脚本当中的内容,让剧本兼容更多的项目构建,从而达到多用,复用的目录,新的思路也已经整理成文章,可以直接点击下边文章跳转。

Jenkins-pipeline 学习笔记–pipeline 结合 ansible 剧本进行批量的部署与回滚配置 |坐而言不如起而行! 二丫讲梵 (opens new window)

微信 支付宝
#jenkins#回滚
上次更新: 2024/07/04, 22:40:37
Jenkins中自由风格回滚方案的最佳实践
pipeline结合ansible剧本进行批量的部署与回滚配置

← Jenkins中自由风格回滚方案的最佳实践 pipeline结合ansible剧本进行批量的部署与回滚配置→

最近更新
01
睡着的人不关灯
06-12
02
学习周刊-总第215期-2025年第24周
06-12
03
学习周刊-总第214期-2025年第23周
06-05
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式