概述和教程¶
欢迎来到面料!
这份文件是一个旋风浏览布的功能和快速指南的使用。在 使用文档 中可以找到其他文档(可以在全文中找到) - 请务必查看。
什么是面料?¶
正如 README
说:
Fabric是一个Python(2.5-2.7)库和命令行工具,用于简化SSH的应用程序部署或系统管理任务的使用。
更具体地,织物是:
一个允许您通过 命令行 执行 任意Python函数 的工具;
一个子程序库(建立在较低级别的库之上),通过SSH 简单 和 Pythonic 执行shell命令。
自然地,大多数用户组合这两件事,使用Fabric编写和执行Python函数或 任务,以自动化与远程服务器的交互。让我们来看看。
你好,fab
¶
这不会是一个正确的教程没有“通常”:
def hello():
print("Hello world!")
放置在您当前工作目录中名为 fabfile.py
的Python模块文件中,该 hello
函数可以使用 fab
工具(作为Fabric的一部分安装)执行,并且只是您期望的:
$ fab hello
Hello world!
Done.
这就是它的全部。此功能允许Fabric作为(非常)基本构建工具使用,即使不导入其任何API。
注解
fab
工具只是导入您的fabfile并执行你指定的一个或多个功能。没有什么神奇的东西 - 你可以在一个正常的Python脚本做任何事情都可以在fabfile中完成!
任务参数¶
将运行时参数传递到任务中常常很有用,就像在常规Python编程中一样。 Fabric对此有基本的支持,使用shell兼容的符号:<task name>:<arg>,<kwarg>=<value>,...
。它是设计的,但让我们扩展上面的例子来向你打个招呼:
def hello(name="world"):
print("Hello %s!" % name)
默认情况下,调用 fab hello
仍然会像以前一样运行;但现在我们可以个性化它:
$ fab hello:name=Jeff
Hello Jeff!
Done.
那些已经用于Python编程的人可能猜到这个调用的行为是完全一样的:
$ fab hello:Jeff
Hello Jeff!
Done.
暂时,您的参数值将始终作为字符串显示在Python中,并且可能需要对复杂类型(如列表)进行一些字符串操作。未来的版本可能会添加一个类型转换系统,使这更容易。
参见
本地命令¶
如上所述,fab
只真的节省了几行 if __name__ == "__main__"
样板文件。它主要设计用于Fabric的API,其中包含用于执行shell命令,传输文件等的函数(或 操作)。
让我们构建一个假设的Web应用程序fabfile。此示例方案如下:Web应用程序通过远程主机 vcshost
上的Git进行管理。在 localhost
上,我们有一个本地克隆的所述Web应用程序。当我们将更改推回到 vcshost
时,我们希望能够以自动方式在远程主机 my_server
上立即安装这些更改。我们将通过自动化本地和远程Git命令来实现。
Fabfiles通常最好在项目的根目录:
.
|-- __init__.py
|-- app.wsgi
|-- fabfile.py <-- our fabfile!
|-- manage.py
`-- my_app
|-- __init__.py
|-- models.py
|-- templates
| `-- index.html
|-- tests.py
|-- urls.py
`-- views.py
注解
我们在这里使用一个Django应用程序,但只是作为一个例子 - Fabric不绑定到任何外部代码库,保存为其SSH库。
对于初学者,也许我们想要运行我们的测试并承诺我们的VCS,所以我们准备好进行部署:
from fabric.api import local
def prepare_deploy():
local("./manage.py test my_app")
local("git add -p && git commit")
local("git push")
其输出可能看起来有点像这样:
$ fab prepare_deploy
[localhost] run: ./manage.py test my_app
Creating test database...
Creating tables
Creating indexes
..........................................
----------------------------------------------------------------------
Ran 42 tests in 9.138s
OK
Destroying test database...
[localhost] run: git add -p && git commit
<interactive Git add / git commit edit message session>
[localhost] run: git push
<git push session, possibly merging conflicts interactively>
Done.
代码本身很简单:import一个Fabric API函数 local
,并使用它来运行和与本地shell命令交互。 Fabric的其他API类似 - 它只是Python。
按照你的方式组织¶
因为Fabric是“只是Python”,你可以自由地组织你的fabfile任何你想要的方式。例如,将事情拆分为子任务通常很有用:
from fabric.api import local
def test():
local("./manage.py test my_app")
def commit():
local("git add -p && git commit")
def push():
local("git push")
def prepare_deploy():
test()
commit()
push()
prepare_deploy
任务可以像以前一样被调用,但现在如果需要,您可以对其中一个子任务进行更细化的调用。
失败¶
我们的基础案例现在工作正常,但如果我们的测试失败会发生什么?我们想要在部署之前穿上刹车并修复它们。
Fabric检查通过操作调用的程序的返回值,如果它们没有完全退出,它们将中止。让我们看看如果我们的一个测试遇到错误会发生什么:
$ fab prepare_deploy
[localhost] run: ./manage.py test my_app
Creating test database...
Creating tables
Creating indexes
.............E............................
======================================================================
ERROR: testSomething (my_project.my_app.tests.MainTests)
----------------------------------------------------------------------
Traceback (most recent call last):
[...]
----------------------------------------------------------------------
Ran 42 tests in 9.138s
FAILED (errors=1)
Destroying test database...
Fatal error: local() encountered an error (return code 2) while executing './manage.py test my_app'
Aborting.
大!我们不需要自己做任何事情:Fabric检测到故障并中止,从不运行 commit
任务。
参见
故障处理¶
但是如果我们想要灵活并给用户一个选择呢?称为 warn_only 的设置(或 环境变量,通常缩写为 env var)允许您将中止转换为警告,允许进行灵活的错误处理。
让我们翻开这个设置为我们的 test
功能,然后检查 local
调用的结果我们自己:
from __future__ import with_statement
from fabric.api import local, settings, abort
from fabric.contrib.console import confirm
def test():
with settings(warn_only=True):
result = local('./manage.py test my_app', capture=True)
if result.failed and not confirm("Tests failed. Continue anyway?"):
abort("Aborting at user request.")
[...]
在添加此新功能时,我们介绍了一些新功能:
__future__
导入需要在Python 2.5中使用with:
;Fabric的
contrib.console
子模块,包含confirm
功能,用于简单的是/否提示;settings
上下文管理器,用于将设置应用于特定的代码块;像
local
这样的命令运行操作可以返回包含关于它们的结果的信息的对象(例如.failed
或.return_code
);和
abort
函数,用于手动中止执行。
然而,尽管额外的复杂性,它仍然很容易跟随,现在更灵活。
连接¶
让我们开始封装我们的fabfile通过放入keystone:一个 deploy
任务注定要运行在一个或多个远程服务器,并确保代码是最新的:
def deploy():
code_dir = '/srv/django/myproject'
with cd(code_dir):
run("git pull")
run("touch app.wsgi")
在这里,我们再次介绍一些新的概念:
Fabric只是Python - 所以我们可以自由使用常规的Python代码结构,如变量和字符串插值;
cd
,一种用cd /to/some/directory
调用为命令加前缀的简单方法。这类似于在本地执行相同的lcd
。
我们还需要确保我们在文件的顶部导入新的函数:
from __future__ import with_statement
from fabric.api import local, settings, abort, run, cd
from fabric.contrib.console import confirm
随着这些变化,让我们来部署:
$ fab deploy
No hosts found. Please specify (single) host string for connection: my_server
[my_server] run: git pull
[my_server] out: Already up-to-date.
[my_server] out:
[my_server] run: touch app.wsgi
Done.
我们从未在fabfile中指定任何连接信息,因此Fabric不知道应该在哪个主机上执行远程命令。当这种情况发生时,Fabric在运行时提示我们。连接定义使用类似SSH的“主机字符串”(例如 user@host:port
),并将使用您的本地用户名作为默认值 - 因此在本例中,我们只需要指定主机名 my_server
。
远程交互¶
git pull
工作正常,如果你已经有你的源代码检出 - 但如果这是第一次部署如果怎么办?这也很好处理这种情况,做初始的 git clone
:
def deploy():
code_dir = '/srv/django/myproject'
with settings(warn_only=True):
if run("test -d %s" % code_dir).failed:
run("git clone user@vcshost:/path/to/repo/.git %s" % code_dir)
with cd(code_dir):
run("git pull")
run("touch app.wsgi")
和我们上面对 local
的调用一样,run
也允许我们基于执行的shell命令构建干净的Python级逻辑。然而,这里有趣的部分是 git clone
调用:因为我们使用Git的SSH方法访问我们的Git服务器上的存储库,这意味着我们的远程 run
调用将需要验证自身。
旧版本的Fabric(和类似的高级SSH库)运行远程程序,无法从本地端触摸。当您严重需要输入密码或以其他方式与远程程序交互时,这是有问题的。
Fabric 1.0和更高版本打破了这个墙,确保你总是可以与对方说话。让我们看看当我们在没有Git checkout的新服务器上运行更新的 deploy
任务时会发生什么:
$ fab deploy
No hosts found. Please specify (single) host string for connection: my_server
[my_server] run: test -d /srv/django/myproject
Warning: run() encountered an error (return code 1) while executing 'test -d /srv/django/myproject'
[my_server] run: git clone user@vcshost:/path/to/repo/.git /srv/django/myproject
[my_server] out: Cloning into /srv/django/myproject...
[my_server] out: Password: <enter password>
[my_server] out: remote: Counting objects: 6698, done.
[my_server] out: remote: Compressing objects: 100% (2237/2237), done.
[my_server] out: remote: Total 6698 (delta 4633), reused 6414 (delta 4412)
[my_server] out: Receiving objects: 100% (6698/6698), 1.28 MiB, done.
[my_server] out: Resolving deltas: 100% (4633/4633), done.
[my_server] out:
[my_server] run: git pull
[my_server] out: Already up-to-date.
[my_server] out:
[my_server] run: touch app.wsgi
Done.
注意 Password:
提示 - 这是我们在Web服务器上的远程 git
调用,要求输入Git服务器的密码。我们能够输入它,并且克隆继续正常。
参见
事先定义连接¶
在运行时指定连接信息获取旧的真正快,所以Fabric提供了一些方法来在您的fabfile或在命令行。我们不会在这里覆盖他们,但我们将向您展示最常见的一个:设置全局主机列表,env.hosts。
环境 是一个类似于全局字典的对象,驱动了许多Fabric的设置,并且可以使用属性写入(实际上,settings
,如上所述,仅仅是一个包装器)。因此,我们可以在模块级修改它我们的fabfile的顶部就这样:
from __future__ import with_statement
from fabric.api import *
from fabric.contrib.console import confirm
env.hosts = ['my_server']
def test():
do_test_stuff()
当 fab
加载我们的fabfile时,我们对 env
的修改将执行,存储我们的设置更改。最终结果与上述完全一致:我们的 deploy
任务将针对 my_server
服务器运行。
这也是你可以告诉Fabric一次在多个远程系统上运行:因为 env.hosts
是一个列表,fab
在其上进行迭代,为每个连接调用给定任务一次。
结论¶
我们完成的fabfile还很短,因为这样的事情去了。这里是它的整体:
from __future__ import with_statement
from fabric.api import *
from fabric.contrib.console import confirm
env.hosts = ['my_server']
def test():
with settings(warn_only=True):
result = local('./manage.py test my_app', capture=True)
if result.failed and not confirm("Tests failed. Continue anyway?"):
abort("Aborting at user request.")
def commit():
local("git add -p && git commit")
def push():
local("git push")
def prepare_deploy():
test()
commit()
push()
def deploy():
code_dir = '/srv/django/myproject'
with settings(warn_only=True):
if run("test -d %s" % code_dir).failed:
run("git clone user@vcshost:/path/to/repo/.git %s" % code_dir)
with cd(code_dir):
run("git pull")
run("touch app.wsgi")
此fabfile使用Fabric的大部分功能集:
定义fabfile任务并使用 fab 运行它们;
使用
local
调用本地shell命令;用
settings
修改env变量;处理命令失败,提示用户和手动中止;
并定义主机列表和
run
远程命令。
但是,还有很多我们没有在这里覆盖!请确保您遵循各种“另见”链接,并查看 主索引页 上的文档目录。
谢谢阅读!