部署
用FTP还是SCP还是rsync复制文件?如果你需要手动复制,用一次两次还行,一天如果部署50次不但慢、效率低,而且容易出错。
正确的部署方式是使用工具配合脚本完成自动化部署。 Fabric 就是一个自动化部署工具。由于Fabric是用Python 2.x开发的,所以,部署脚本要用Python 2.7来编写,本机还必须安装Python 2.7版本。
要用Fabric部署,需要在本机(是开发机器,不是Linux服务器)安装Fabric:
$ easy_install fabric
Linux服务器上不需要安装Fabric,Fabric使用SSH直接登录服务器并执行部署命令。
下一步是编写部署脚本。Fabric的部署脚本叫 fabfile.py ,我们把它放到 awesome-python-webapp 的目录下,与 www 目录平级:
awesome-python-webapp/
+- fabfile.py
+- www/
+- …
Fabric的脚本编写很简单,首先导入Fabric的API,设置部署时的变量:
# fabfile.py
import os, re
from datetime import datetime
# 导入Fabric API:
from fabric.api import *
# 服务器登录用户名:
env.user = 'michael'
# sudo用户为root:
env.sudo_user = 'root'
# 服务器地址,可以有多个,依次部署:
env.hosts = ['192.168.0.3']
# 服务器MySQL用户名和口令:
db_user = 'www-data'
db_password = 'www-data'
然后,每个Python函数都是一个任务。我们先编写一个打包的任务:
TARFILE = 'dist-awesome.tar.gz'
def build():
includes = ['static', 'templates', 'transwarp', 'favicon.ico', '.py']
excludes = ['test', '.', '.pyc', '.pyo']
local('rm -f dist/%s' % TARFILE)
with lcd(os.path.join(os.path.abspath('.'), 'www')):
cmd = ['tar', '—dereference', '-czvf', '../dist/%s' % TARFILE]
cmd.extend(['—exclude=\'%s\'' % ex for ex in excludes])
cmd.extend(includes)
local(' '.join(cmd))
Fabric提供 local('…') 来运行本地命令, with lcd(path) 可以把当前命令的目录设定为 lcd() 指定的目录,注意Fabric只能运行命令行命令,Windows下可能需要 Cgywin 环境。
在 awesome-python-webapp 目录下运行:
$ fab build
看看是否在 dist 目录下创建了 dist-awesome.tar.gz 的文件。
打包后,我们就可以继续编写 deploy 任务,把打包文件上传至服务器,解压,重置 www 软链接,重启相关服务:
REMOTETMPTAR = 'tmp%s' % TARFILE
REMOTEBASE_DIR = 'srvawesome'
def deploy():
newdir = 'www-%s' % datetime.now().strftime('%y-%m-%d%H.%M.%S')
# 删除已有的tar文件:
run('rm -f %s' % REMOTETMP_TAR)
# 上传新的tar文件:
put('dist/%s' % TARFILE, REMOTETMP_TAR)
# 创建新目录:
with cd(REMOTEBASE_DIR):
sudo('mkdir %s' % newdir)
# 解压到新目录:
with cd('%s/%s' % (REMOTEBASE_DIR, newdir)):
sudo('tar -xzvf %s' % REMOTETMP_TAR)
# 重置软链接:
with cd(REMOTEBASE_DIR):
sudo('rm -f www')
sudo('ln -s %s www' % newdir)
sudo('chown www-data:www-data www')
sudo('chown -R www-data:www-data %s' % newdir)
# 重启Python服务和nginx服务器:
with settings(warn_only=True):
sudo('supervisorctl stop awesome')
sudo('supervisorctl start awesome')
sudo('etcinit.d/nginx reload')
注意 run() 函数执行的命令是在服务器上运行, with cd(path) 和 with lcd(path) 类似,把当前目录在服务器端设置为 cd() 指定的目录。如果一个命令需要sudo权限,就不能用 run() ,而是用 sudo() 来执行。