Skip to main content

概述和教程

欢迎来到面料!

这份文件是一个旋风浏览布的功能和快速指南的使用。在 使用文档 中可以找到其他文档(可以在全文中找到) - 请务必查看。

什么是面料?

正如 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

  • run,它类似于 local,但是运行 远程 而不是局部。

我们还需要确保我们在文件的顶部导入新的函数:

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 远程命令。

但是,还有很多我们没有在这里覆盖!请确保您遵循各种“另见”链接,并查看 主索引页 上的文档目录。

谢谢阅读!