部署

    用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() 来执行。