21

Jenkins-pipeline学习笔记–pipeline中回滚方案的最佳实践以及与ansible集成的配置 |坐...

 3 years ago
source link: http://www.eryajf.net/3510.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
本文预计阅读时间 36 分钟

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

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

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

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

Jenkinsfile内容:

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

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

71cfeb93ly1g9wm76b4naj20jg0ykq4j.jpg

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

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

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

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

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

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

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

3,优化方案

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




weinxin


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK