Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
fab 部署脚本

部署脚本


这是我常用的 fab 部署脚本的改写通用版本。

可能对大型项目没起作用,但是对于小项目,作用还是很大,所以发出来写个文章分享下。


有啥功能

  • 部署开发人员的测试环境
  • 部署测试环境
  • 部署生产环境

部署生产环境

fab deploy:master

部署测试环境

fab deploy:dev

部署开发人员的测试环境

fab deploy:test,userA,funBranch,ubuntu@10.10.10.12

依赖

python, pip, fabric

sudo apt-get build-essential python-dev python-pip
sudo pip install fabric

配置

  • 前 30 行针对性修改
  • git 地址针对性修改
  • 部署前,前端编译,后端编译等等针对修改
  • 部署后,环境安装,服务重启等等针对修改

注意事项:

  • 所有机器的 ssh 端口,都统一,不支持运行时修改 ssh 端口
  • 所有仓库的仓库名都一致, 推荐 fork 方式进行 git 协助
  • 执行这脚本的机器有 ssh 方式部署各个部署 git 仓库的权限,有 ssh 方式访问各个部署机器的权限。
# coding: utf-8
import os
import datetime
# from fabric.contrib.files import exists
from fabric.api import run, env, local, task, cd, lcd, execute
# 项目的仓库地址。
HUBNAME = 'map'
# 项目上游
UPSTREAM = 'shenqihui'
# 临时部署的目录,存储部署下载的仓库
TMP_CODE_DIR = '/var/deploy/'
# 部署测试环境的位置
STAGING_DEST = '/opt/dev/'
# 部署生产环境的位置
RELEASE_DEST = '/opt/www/'
# 网站代码,未编译版本
BUILDING_CODE_DEST = '/map/src/'
# 网站代码,已编译版本
BUILDED_CODE_DEST = '/map/dist/'
# 主生产分支
MAIN_RELEASE_BRANCH = 'master'
# 主开发分支
MAIN_DEV_BRANCH = 'dev'
# 机器 ssh 端口,默认所有机器都是统一端口,不进行运行时设定。
SSH_PORT = '6666'
# fab ssh 后缀
SSH_PORT_ENDFIX = ':%s' % SSH_PORT
DEPLOY_BRANCH = ''
DEPLOY_HUB = ''
DEPLOY_DEST = ''
# 产品发布部署目标机器。
RELEASE_HOST = ['ubuntu@10.10.10.10']
# 开发分支部署的目标机器
STAGING_HOST = ['ubuntu@10.10.10.11']
# add port for fab
RELEASE_HOST = ['%s%s' % (host, SSH_PORT_ENDFIX) for host in RELEASE_HOST if host]
STAGING_HOST = ['%s%s' % (host, SSH_PORT_ENDFIX) for host in STAGING_HOST if host]
RSYNC = ('rsync -e "ssh -p %s" --rsync-path="sudo rsync" -avzP --delete {source}%s/ {server}:{dest}' % (SSH_PORT, BUILDED_CODE_DEST))
def get_repo_tmp_dir(repo_name):
# 获取源码存放的目录。
return os.path.join(TMP_CODE_DIR, repo_name)
def is_git_repo_exists(repo_name):
# 判断源码是否已经存在了。
repo_path = get_repo_tmp_dir(repo_name)
return os.path.exists(repo_path)
def get_repo_url(repo_name):
# 获取源码的仓库地址
return 'git@github.com:%s.git' % repo_name
def git_clone(repo_name, branch):
''' clone 版本库的代码到部署临时目录
'''
repo_url = get_repo_url(repo_name)
repo_path = get_repo_tmp_dir(repo_name)
local('git clone %s %s' % (repo_url, repo_path))
with lcd(repo_path):
local('git checkout %s' % branch)
def git_pull(repo_name, branch):
''' 更新本地代码至 MAIN_DEV_BRANCH 的版本
'''
repo_path = get_repo_tmp_dir(repo_name)
with lcd(repo_path):
try:
# 增加远程仓库,方便进行合并上下游代码
local('git remote add deploy_auto_upstream %s' % get_repo_url('%s/%s' % (UPSTREAM, HUBNAME)))
except:
pass
local('git checkout .')
local('git fetch origin')
local('git fetch deploy_auto_upstream')
local('git checkout -f deploy_auto_upstream/%s' % MAIN_DEV_BRANCH)
local('git reset --hard deploy_auto_upstream/%s' % MAIN_DEV_BRANCH)
if branch != MAIN_DEV_BRANCH:
# 其他分支,进行部署前需要合并到主开发分支。
try:
local('git branch -D deploy_auto_%s' % branch)
except:
pass
local('git checkout -b deploy_auto_%s' % branch)
# 合并上下游代码
local('git merge --no-commit --no-edit origin/%s -X theirs' % branch)
def upload_code(repo_name, dest):
''' 上传代码至机器
'''
tmp_code_path = get_repo_tmp_dir(repo_name)
# 源码同步
for server_host in env.all_hosts:
local(RSYNC.format(source=tmp_code_path, server=server_host.replace(SSH_PORT_ENDFIX, ''), dest=dest))
def install_requirements():
# 安装依赖,包括 PIP 等等,python 网站需要安装 pip 包。等等
with cd(DEPLOY_DEST):
# simple pip install, using env's pip
run('sudo ./env/bin/pip install -r requirements.pip -i http://pypi.douban.com/simple --timeout 60 --trusted-host=pypi.douban.com')
def front_action(repo_name):
# 前端处理
repo_path = get_repo_tmp_dir(repo_name)
with lcd(os.path.join(repo_path, BUILDING_CODE_DEST)):
local('npm install')
local('bower install')
local('gulp build')
def restart():
''' 重启 web 服务,以及相关服务
'''
try:
run('sudo /etc/init.d/nginx -s reload')
except:
try:
run('sudo /etc/init.d/nginx start')
except:
pass
def do_deploy(repo_name, dest):
''' 部署代码到web服务器,默认部署至staging host
'''
if not is_git_repo_exists(repo_name):
git_clone(repo_name, DEPLOY_BRANCH)
git_pull(repo_name, DEPLOY_BRANCH)
front_action(repo_name)
upload_code(repo_name, dest)
# install_requirements() # python 网站需要安装 pip 包。等等
restart() # 远程进行重启操作,或者其他
def execute_deploy(hosts):
# 执行部署 deploy
print '\nBegin deploy for %s at %s\n' % (hosts, datetime.datetime.now())
try:
execute(do_deploy, DEPLOY_HUB, DEPLOY_DEST, hosts=hosts)
except:
pass
print '\nFinished deploy for %s at %s\n' % (hosts, datetime.datetime.now())
@task
def deploy(deploy_type, origin=UPSTREAM, branch=MAIN_DEV_BRANCH, target_host=''):
'''
部署: dev/master/test
指令例子:
* fab deploy:dev
* fab deploy:master
* fab deploy:test,hubid,funA,ubuntu@10.10.10.10
参数:
origin,branch,target_host 这三个参数只对 deploy_type == 'test' 时候有效。而且必须填写这三个参数。
'''
global HUBNAME
global DEPLOY_HUB
global DEPLOY_BRANCH
global DEPLOY_DEST
if deploy_type == 'test':
# 部署特定仓库的测试分支。
if not origin or not branch or not target_host:
print u'部署测试分支必须填写参数'
deploy_type = ''
else:
HUBNAME = origin
DEPLOY_BRANCH = branch
DEPLOY_DEST = STAGING_DEST
DEPLOY_HUB = '%s/%s' % (HUBNAME, HUBNAME)
execute_deploy(target_host)
elif deploy_type == 'dev':
# 部署主开发分支
HUBNAME = UPSTREAM
DEPLOY_BRANCH = MAIN_DEV_BRANCH
DEPLOY_DEST = STAGING_DEST
DEPLOY_HUB = '%s/%s' % (HUBNAME, HUBNAME)
execute_deploy(STAGING_HOST)
elif deploy_type == 'master':
# 部署生产分支
HUBNAME = UPSTREAM
DEPLOY_BRANCH = MAIN_RELEASE_BRANCH
DEPLOY_DEST = RELEASE_DEST
DEPLOY_HUB = '%s/%s' % (HUBNAME, HUBNAME)
execute_deploy(RELEASE_HOST)
if not deploy_type:
print deploy.__doc__
exit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment