并行执行

1.3 新版功能.

默认情况下,Fabric 会默认 顺序 执行所有任务(详细信息参见 Execution strategy ),这篇文档将介绍 Fabric 如何在多个主机上 并行 执行任务,包括 Fabric 参数设置、任务独立的装饰器,以及命令行全局控制。

它是如何运转的

由于 Fabric 1.x 并不是完全线程安全(以及为了更加通用,任务函数之间并不会产生交互),该功能的实现是基于 Python multiprocessing 模块,它会为每一个主机和任务组合创建一个线程,同时提供了一个(可选的)弹窗用于阻止创建过多的进程。

举个例子,假设你正打算更新数台服务器上的 Web 应用代码,所有服务的代码都更新后开始重启服务器(这样代码更新失败的时候比较容易回滚)。你可能会写出下面这样的代码:

from fabric.api import *

def update():
    with cd("/srv/django/myapp"):
        run("git pull")

def reload():
    sudo("service apache2 reload")

在三台服务器上并行执行,就像这样:

$ fab -H web1,web2,web3 update reload

刚常见的情况是没有启动任何并行执行参数,Fabric 将会按顺序在服务器上执行:

  1. web1更新

  2. web2更新

  3. web3更新

  4. web1重新加载配置

  5. web2重新加载配置

  6. web3重新加载配置

如果激活并行执行(通过 -P ——下面会详细介绍)它将变成这样:

  1. web1web3web3更新

  2. web1web2web3重新加载配置

这样做的好处非常明显——如果 update 花费 5 秒 reload 花费 2 秒顺序执行总共会花费 (5+2)*3 = 21 秒,而并行执行只需要它的 1/3,也就是 (5+2) = 7 秒。

如何使用

装饰器

由于并行执行影响的最小单位是任务,所以功能的启用或禁用也是以任务为单位使用 parallelserial 装饰器。以下面这个 fabfile 为例:

from fabric.api import *

@parallel
def runs_in_parallel():
    pass

def runs_serially():
    pass

如果这样执行:

$ fab -H host1,host2,host3 runs_in_parallel runs_serially

将会按照这样的流程执行:

  1. runs_in_parallel 运行在 host1host2host3

  2. runs_serially 运行在 host1

  3. runs_serially 运行在 host2

  4. runs_serially 运行在 host3

命令行参数

你也可以使用命令行选项 -P 或者环境变量 env.parallel <env-parallel>强制所有任务并行执行。不过被装饰器 `~fabric.decorators.serial 封装的任务会忽略该设置,仍旧保持顺序执行。

例如,下面的 fabfile 会产生和上面同样的执行顺序:

from fabric.api import *

def runs_in_parallel():
    pass

@serial
def runs_serially():
    pass

在这样调用时:

$ fab -H host1,host2,host3 -P runs_in_parallel runs_serially

和上面一样,runs_in_parallel 将会并行执行,runs_serially 顺序执行。

bubble 大小

主机列表很大时,用户的机器可能会因为并发运行了太多的 Fabric 进程而被压垮,因此,你可能会选择 moving bubble 方法来限制 Fabric 并发执行的活跃进程数。

默认情况下没有使用 bubble 限制,所有主机都运行在并发池中。你可以在任务级别指定 parallel 的关键字参数 pool_size 来覆盖该设置,或者使用选项 -z 全局设置。

例如同时在 5 个主机上运行:

from fabric.api import *

@parallel(pool_size=5)
def heavy_task():
    # lots of heavy local lifting or lots of IO here

或者不使用关键字参数 pool_size

$ fab -P -z 5 heavy_task

行级输出 vs 比特级输出

为了支持 与远程程序集成 特性,Fabric 默认会一字节一字节地讲数据输出到终端。并行情况下,这样的输出结果会非常糟糕,因为多个进程的同时输出结果可能会混在终端的标准输出流中。

为了消除该问题,在并行执行时 Fabric 会自动启用行级输出,这会导致上面链接中提到的远程交互特性大部分失效,不过这是一个合理的折中。

行级输出混淆的情况在多进程的情况下是无法避免的,但是你可以设置主机地址作为前缀来区分。

注解

未来版本会增加增强的日志支持来简化并行运行情况下的问题追踪。