基于fabric和hg的自动化部署

自动化部署

fabric是个很好用的自动化部署工具,虽然功能比起puppet,saltstack之类要弱一些,但胜在用python,而且免安装服务端。

当然你要说docker更好我也同意,然而我是经常使用FreeBSD的,而且还有一些32位的低配系统,并不适合用docker。更不用说虚拟机了。

自动化部署的目的主要是简化手工部署的麻烦,包括初次安装部署和代码修改后的更新部署。初始部署主要是安装基础环境,初始化数据库等。更新部署则更麻烦一些,需要修改基础环境配置,变更数据库结构等。相比之下代码发布和更新反而是最简单的,用一个版本控制工具即可。

我是比较习惯用hg做代码版本管理的,当然这里要把hg换成git也可以,但我就是喜欢hg,你咬我啊。

fabric

fabric的官网是fabfile.org,基本用法都可以看官方文档,这里只对几种常用的用法作简单说明。

安装很简单,只要在本地用pip安装fabric即可,远程只要有SSH服务即可,不需要安装额外的东西,这点比puppet和saltstack省事。

使用上也很简单,最关键的是部署脚本是用python,比起puppet用的ruby来说,更合我口味。

默认的脚本文件名为: fabfile.py,当然你也可以用别的名字,但用起来就不方便了,类似make要用默认的Makefile文件名才方便一样。

执行默认脚本的命令为:fab <函数名>[<[参数名:]值>]

其中的函数名为fabfile.py中定义的任意函数(当使用@task装饰器时就只能使用已经装饰过的函数),也可以带上参数。对于特定主机或用户,还可以给函数加上角色装饰器。

运行本地命令的例子如下:

from fabric.api import local
def deploy_1():
    local(“hg commit”)
    local(“hg push”)
fab deploy_1

运行远端命令的例子如下:

from fabric.api import run
def deploy_2():
    run(“hg pull”)
    run(“hg update”)
fab deploy_2 -H hostname_or_ip

切换当前目录:

with lcd(“path”) # 本地目录
with cd(“path”) # 远端

错误处理:任何返回值不为0的操作都将导致异常,除非…

with settings(warn_only=True)

并且用命令的.failed属性来判断执行结果

env用于保存相关配置环境,比如SSH的KEY文件:key_filename,还有主机名列表:hosts,角色定义:roledefs等

角色的使用:

env.roledefs={‘role1’:[“user1@server1:port1”, “user2@server2:port2”],
    ‘role2’:[“user3@server3:port3”]}
@roles(“role1”)
def deploy_3():
    pass

结合fabric和hg的部署

以一个简单的python web应用为例来说明。

一次完整的手工初次部署大致包括以下内容(假设服务端系统已安装必要软件,比如hg, python, virtualenv, database, webserver,其中database和webserver已经配置好,单独的virtualenv已创建)等:

  • 在virtualenv环境中安装必要的依赖包 pip install -r requirements.txt
  • 通过hg发布要部署的代码 [local] hg push ssh://user@host/path; [remote] hg update
  • 初始化数据库
  • 启动(通过gunicorn, supervisord等)

代码修改过以后再次部署则涉及以下一些内容:

  • 更新依赖包
  • 更新代码
  • 更新数据库结构
  • 重启服务

再考虑到可能需要分别部署到测试环境和正式环境,又需要考虑以下问题:

  • 测试环境和正式环境涉及不一样的配置(比如连接不一样的数据库,配置不同的端口,甚至静态文件指向不同的路径)
  • 必须是测试环境中测试通过的版本才可以更新到正式环境中
  • 正式环境有更严格的权限管理(开发部门不可以直接部署到正式环境,甚至不能接触正式环境的配置信息)

由此,我们至少需要两个代码仓库:一个是开发代码库(repo_dev),包括测试配置,另一个是正式配置仓库(repo_prod)。

那么,基本的fabfile.py的deploy_dev函数大致有以下内容:

local("hg push repo_dev")
with cd("/target"):
    run("hg pull repo_dev")
    run("hg update")
    run("workon venv") # 切换到指定的virtualenv
    run("pip install -r requirements.txt")
    # 初始化数据库或更新数据库结构
    sudo("supervisorctl restart xxx") # gunicorn不能用supervisor重启,因为停止需要等待一段时间,建议用kill信号进行软重启

可以通过角色配置使repo_dev指定的远程服务器为测试主机 testhost ,在测试主机上测试通过以后,测试部分可以部署到正式机 prodhost 上。

with cd("/prodconf"):
    run("hg pull repo_prod")
with cd("/prod"):
    run("hg pull testhost") # 从测试主机上更新代码
    run("hg update rev") # 注意,这里要更新测试过的指定版本
    run("cp /prodconf/config .") # 使用正式配置替换测试配置
    run("workon venv")
    run("pip install -r requirements.txt")
    # 更新数据库结构
    sudo("supervisorctl restart xxx")

这个部署函数使用另一个用户角色,只要控制这个角色只有测试部门有权限即可,开发部门即使不慎运行了这个部署函数,也会因为没有权限而失败。

推送到[go4pro.org]