Comments

用 Fabric 实现自动化部署

写完代码测试通过之后,终于松一口气,然后可以愉快的部署上线了。但是问题随之而来:如何部署?或者如何能更自动化的部署?

部署应用是一系列的操作,就环境而言,分为本地和远程服务器,就操作而言,大概包括提交代码、备份代码、更新代码、安装依赖、迁移数据库、重启服务等流程。其中除了提交代码这一步是在本地完成,其余操作都需要在服务器环境执行。

上面的流程当中,有一个很重要的,就是如何同步代码(提交、备份、更新)。就我的经验,了解或用过这些方式:

  • rsync: rsync 是一个文件同步的工具,如果配置好使用起来体验也不错。但是有很多缺点:
    • 配置复杂,命令行参数多
    • 需要在服务器上运行 rsyncd,默认监听 873 端口(可能会有防火墙)
  • scp: scp 底层用的是 SSH 协议,所以只要服务器上运行了 sshd 就可以双向 copy 文件。对于文件传输来说,scp 比 rsync 体验差的地方有:
    • 不能增量更新,每次都是全部传输
    • 不能配置忽略文件(.git 怎么办?)
  • git: 就个人而言,git 是最方便的部署方式了,有版本控制,可以增量更新,可以配置忽略文件,使用简单。实际上只要有可能,都推荐用 git 来发布代码。但问题在于,很多公司的 git 服务器都是在内网的,所以在服务器上无法访问。

很幸运的是,我们有一个公网可以访问的 git 服务器,所以可以用 git 来发布代码。发布完代码后就是后续的一系列操作了,最原始的方式,是登录到服务器,然后一步一步敲命令执行下来。但是如果要频繁部署的话(快速迭代时肯定要经常更新代码),这就变成了繁复的体力劳动,而且容易出错(漏了流程,看花眼了)。于是就想到了脚本,把这些操作写成 shell 脚本,然后执行脚本就好了。这是一个很大的进步,然而仍然存在一个问题:从本地环境到远程环境,需要登录,导致了流程上的阻断。

Fabric 是 Python 编写的一个可以实现自动化部署和系统维护的命令行工具,只需要写一些简单的 Python 代码就能轻松解决上面提到的所有问题。Fabric 底层用的是 SSH 协议,提供了一系列语义清晰的 API 来组合实现部署任务。

安装

Fabric 是 Python 编写的工具,所以可以用 pip 来安装:

sudo pip install fabric

如果是 Ubuntu 系统,还可以用 apt-get 安装:

sudo apt-get install fabric

安装完成后,会生成一个 fab 命令,这个命令会读取当前路径在的 fabfile.py 并执行相应的任务。

Hello, world!

先来看一个简单的例子,用 fab 命令执行一个输出 Hello, world! 的任务。

新建一个文件,fabfile.py:

def hello():
	print 'Hello, world!'

在 fabfile.py 所在的路径执行:

fab hello

可以看到有这样的输出:

Hello, world!

Done.

可以给任务传递参数,修改 fabfile.py:

def hello(name="world"):
    print "Hello, %s!" % name

fab 命令执行:

$ fab hello

Hello, world!

Done.

$ fab hello:name=leon

Hello, leon!

Done.

这个例子除了展示 fab 运行任务和传递参数之外,没有什么实际意义,接下来用一个接近真实的场景来展示如何用 Fabric 部署。

部署应用

假设这样一个场景,有个 Python 项目取名 usercenter,用 git 做版本控制,用 supervisor 做进程管理。一次完整的部署过程可能包括这些流程:

# 本地
$ cd /path/to/userenter
$ git pull
$ git add -A
$ git commit -m "commit message"
$ git push

# 远程
$ cd /path/to/usercenter
$ workon usercenter		# virtualenv
$ git pull				# 更新代码
$ pip install -r requirements.txt		# 安装依赖
$ python manage.py db migrate			# 数据库迁移
$ supervisorctl restart usercenter	# 重启服务

我们现在用 Fabric 来一次性完成上面所有操作(假设第一次部署是手工执行的,现在只处理更新/升级的任务)。在 usercenter 项目的根目录下新建 fabfile.py 文件:

# -*- coding: utf-8 -*-

from fabric.api import env, local, cd, run
from fabric.context_managers import prefix


def production():
	""" 设置 production 环境 """
	env.hosts = ["production@123.123.123.123:22"]
	env.key_filename = "/path/to/key_file"
	# env.password = "123456"	# password 和 keyfile 两者只需要一个就可以


def staging():
	""" 设置 staging 环境 """
	env.hosts = ["staging@111.111.111.111:22"]
	env.password = "123456"		# 如果不写密码,会在 fab 执行时有交互提示输入密码


def prepare():
    """ 本地提交代码,准备部署 """
	local("git pull")	# local 用于执行本地命令
	local("pip freeze > requirements.txt")
	local("git add -p && git commit")	# 会有交互输入 commit message
	local("git push")


def update():
	""" 服务器上更新代码、依赖和迁移 """
	# cd 用于在服务器上执行 cd 命令,本地环境对应的 api 是 lcd (local cd)
	with cd("/path/to/usercenter"), prefix("workon usercenter"):
		run("git pull")			# run 用于服务器上执行命令
		run("pip install -r requirements.txt")
		run("python manage.py db migrate")
		run("supervisorctl restart usercenter")

def deploy():
	prepare()
	update()

OK, 完成。具体的意义代码里面都有注释,不赘述。需要注意的是 productionstaging 分别设置了两种不同的环境。

# 部署到 production 环境
$ fab production deploy

# 部署到 staging 环境
$ fab staging deploy

执行过程中可能会有些交互,按提示输入相应信息,然后等着执行完成就好了。如果一切顺利(应该是这样),就完成了 usercenter 的部署了,整个过程只需要敲一行命令,是不是非常方便?

More…

上面的例子基本上是可以在实际环境中使用的,不过还是有很多内容没有覆盖到,比如错误处理,多服务器部署,并行等。Fabric 默认是串行执行的,如果有多个远程服务器,是一个一个顺序执行。执行过程中如果发生异常,任务会直接中断,所以可能需要有错误处理。

上面这些(还有很多)内容都可以在 Fabric 的文档上(非常详细)找到相应的内容,下面给出一些参考链接,结合文档和自己的实际情况,多用几次就能定制出能满足自己需求的 Fabric 任务:

Comments

getElementsByTagName('BODY')[0]).appendChild(s); }()); getElementsByTagName('BODY')[0]).appendChild(s); }()); getElementsByTagName('BODY')[0]).appendChild(s); }()); comments powered by Disqus ript">comments powered by Disqus.