基于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")
这个部署函数使用另一个用户角色,只要控制这个角色只有测试部门有权限即可,开发部门即使不慎运行了这个部署函数,也会因为没有权限而失败。