赞
踩
这是我发布于 publish:July 4, 2017 -Tuesday 的文章,当时发现公司的回滚功能不好进行的改造。
昨天上午因为周末的上线中出现一些异常,产品提出要进行线上代码回滚,但之前我们使用jenkins方法进行回滚的时候,都是回滚到线上代码目录的倒数一个目录,但这样会有一个问题。
比如在发布的时候存在某些服务器发布失败,这时如果这些服务器都按这个逻辑回滚,则可能会在回滚时,不同的服务器上回滚的版本可能不一样,从而导致新的异常。
之前我也考虑过这个问题,因为有多个服务器,就需要实现jenkins在每个服务器中执行的时候有一个可以共用的东西,之前在jenkins中从来没有去获取过SVN的版本号,以为不能去取这些数据,所以我当时有考虑能否有一个方法能实现在各个服务器里生成一样的值的问题,不过昨天我尝试着去想,jenkins的fabfile.py中应该能取到提取SVN代码时的版本号。果真发现一个很有用的变量,使用:
os.getenv("SVN_REVISION");
可提取jenkins发布时拉取的svn版本号。这样实现方案就容易多了。有了这个版本号,每次发布的时候,生成的新程序目录带上发布时的代码版本号,比如发布时新的代码目录 r20170704125854_764 (764即是SVN版本号,在回滚的时候我只要ls | grep _764 可指定要回到哪个版本号的目录,这样可大确保回滚时服务器的不一致问题。
对于一个版本号可能发布过二次或多次的情况,只要在grep后提取一下,如有多个,取一个值即可。
jenkins管理后台执行的命令如下:
- #回滚至某个版本号
- fab -f job/fabfile.py online rollback --set rollToVersion=102
回滚命令中执行的 python 代码如下:
- ####代码回归到指定版本的python:接收参数:rollToVersion
- @task
- def rollback():
- if not env.has_key('rollToVersion'):
- print('no params:rollToVersion.')
- else:
- env.rollto = run("sudo ls -r %(base_dir)s/revs | grep _%(toversion)s;" % { 'base_dir':env.base_dir ,'toversion':env.rollToVersion})
- if len(env.rollto) < 10:
- print('回滚失败,未找到版本号目录:' + env.rollToVersion)
- else:
- env.rollto = env.rollto.split()
- with cd(env.base_dir):
- run("rm -f current; ln -s %(base_dir)s/revs/%(rollto)s current;ls -l | grep current;" % { 'base_dir':env.base_dir ,'rollto':env.rollto[0] })
- print('成功回滚至版本目录:' + env.rollto[0])
今天因为服务器迁移变迁,又对发布脚本进行了一些优化改进。之前在公司的发布脚本上进行了一个主要的回滚改进,即在版本文件夹中引入了SVN版本号从而实现回滚时可回滚至确定的版本号,也有过一篇这文件,今天再在这上面进行了一些改善,所以在这笔记本里保存一下。
在jenkins平台中的Comman命令配置如下:
#自动部署脚本
fab -f deploy/fabfile.py production deploy
#线上全量回滚至某个版本
fab -f deploy/fabfile.py production rollback --set rollToVersion=SVN版本号
此代码的功能特点如下:
1,代码直接解压至目标版本文件夹。使用tar 的--strip-components 1选项。减少了有些发布先解压至tmp目录再进行处理的步骤。
2,发布时可通过修改exclude_list 设置哪些文件夹或文件不用发布至代码中
3,创建current软链时使用ln -sTf直接覆盖,减少一些发布中先删除current再创建的步骤。
4,发布脚本最后添加命令ls -tlr| head -n -8 | xargs sudo rm -rf 删除8个以外的代码版本。避免过多版本冗余
5,发布时提取SVN的版本号SVN_REVISION,存入版本文件夹名称中,从而实现回滚的时候可以实现所有服务器回滚至同一版本号。
6,服务器同时存在多个同一版本的代码,回滚至此版本也不会出错。
以下是完整的fabfile.py中的完整代码:
- #! /usr/bin/python
- #encoding=utf-8
- #kermit@007.com
-
-
- #jenkins平台中的Comman命令如下:
- #自动部署脚本
- #fab -f deploy/fabfile_new2019.py production deploy
-
- #线上全量回滚至某个版本
- #fab -f deploy/fabfile_new2019.py production rollback --set rollToVersion=SVN版本号
-
- from fabric.api import *
- import time
- import os
-
- # fab production deploy
- env.app = '04007_api'
-
- #线上发布脚本
- @task
- def production():
- env.user = "04007_jenkins"
- env.hosts = [
- '192.168.162.43',
- '192.168.162.44',
- '192.168.162.45',
- '192.168.162.46',
- ]
- env.password = '111111111111111'
- env.base_dir = "/opt/04007_data"
- #线上部署时使用线上环境
- env.copyconfig = 2
-
- #线上全量代码回滚
- @task
- def rollback():
- if not env.has_key('rollToVersion'):
- print('no must params:rollToVersion.')
- else:
- env.rollto = run("sudo ls -r %(base_dir)s/jenkins_revs | grep _%(toversion)s;" % { 'base_dir':env.base_dir ,'toversion':env.rollToVersion})
- if len(env.rollto) < 10:
- print('回滚失败,未找到版本号目录:' + env.rollToVersion)
- else:
- env.rollto = env.rollto.split()
- with cd(env.base_dir):
- run("ln -sTf jenkins_revs/%(rollto)s current;" % { 'rollto':env.rollto[0]})
- print('成功回滚至版本目录:' + env.rollto[0])
-
- #全量发布
- @task
- def deploy():
- remote_floder = time.strftime('%Y%m%d%H%M%S') + '_' + os.getenv("SVN_REVISION");
- remote_dir = env.base_dir + '/jenkins_revs/';
- env.current_release = remote_dir + remote_floder;
- exclude_list = "{.svn,static}"
- upload_project(remote_dir=remote_dir, remote_floder = remote_floder, exclude=exclude_list)
-
- with cd(env.current_release):
- run("rm -rf static;ln -s /opt/wireless/static static;")
- #测试环境/线上环境部署时的配置文件
- if env.copyconfig==1:
- run("cp -f config.test.php config.php;")
- if env.copyconfig==2:
- run("cp -f config.online.php config.php;")
-
- with cd(env.base_dir):
- run("ln -sTf %(current_release)s current;sudo /etc/init.d/php-fpm reload;cd jenkins_revs;ls -tlr| head -n -8 | xargs sudo rm -rf" % {'current_release':env.current_release})
-
- #代码部署上服务器
- def upload_project(remote_dir="", remote_floder="", exclude=""):
- from tempfile import mkdtemp
- local_dir = os.getcwd()
- local_dir = local_dir.rstrip(os.sep)
- local_path, local_name = os.path.split(local_dir)
- tar_file = "%s.tar.gz" % local_name
- target_tar = os.path.join(remote_dir, tar_file)
- tmp_folder = mkdtemp()
-
- try:
- tar_path = os.path.join(tmp_folder, tar_file)
- cmd = "tar -czf %s -C %s %s --exclude=%s" % (tar_path, local_path, local_name, exclude)
- local(cmd)
- put(tar_path, target_tar)
- with cd(remote_dir):
- try:
- run("mkdir %s;tar -xzf %s -C %s --strip-components 1" % (remote_floder, tar_file, remote_floder))
- finally:
- run("rm -f %s" % tar_file)
- finally:
- local("rm -rf %s" % tmp_folder)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。