fabric并行任务
默认情况下,Fabric 会默认 顺序 执行所有任务.这篇文档将介绍 Fabric 如何在多个主机上 并行 执行任务,包括 Fabric 参数设置,以及命令行全局控制。
Fabric 并行是如何运转的
由于 Fabric 1.x 并不是完全线程安全(以及为了更加通用,任务函数之间并不会产生交互),该功能的实现是基于 Python multiprocessing 模块,它会为每一个主机和任务组合创建一个线程,同时提供了一个(可选的)弹窗用于阻止创建过多的进程。
举个例子,假设你正打算更新数台服务器上的 Web 应用代码,所有服务的代码都更新后开始重启服务器。你可能会写出下面这样的代码:
from fabric.api import * def update(): with cd("/root"): run("git pull") def reload(): sudo("service apache2 reload")
在三台服务器上并行执行,就像这样:
$ fab -H web1,web2,web3 update reload
常见的情况是没有启动任何并行执行参数,Fabric 将会按顺序在服务器上执行:
-
在 web1 上 更新
-
在 web2 上 更新
-
在 web3 上 更新
-
在 web1 上 重新加载配置
-
在 web2 上 重新加载配置
-
在 web3 上 重新加载配置
如果激活并行执行(通过 -P )它将变成这样,-P后面会介绍:
-
在 web1、web3 和 web3 上 更新
-
在 web1、web2 和 web3 上 重新加载配置。
这样做的好处非常明显:如果 update 花费 5 秒 reload 花费 2 秒,顺序执行总共会花费 (5+2)*3 = 21 秒,而并行执行只需要它的 1/3,也就是 (5+2) = 7 秒。
如何使用
由于并行执行影响的最小单位是任务,也就是函数,所以功能的启用或禁用也是以任务为单位使用 parallel 或 serial 注解来实现的。
parallel表示并行,serial表示串行。
serial 注解可以省略,省略的情况下,默认就是串行执行。
以下面这个 fabfile 为例:
from fabric.api import * @parallel def runs_in_parallel(): print("执行runs_in_parallel") pass def runs_serially(): print("执行runs_serially") pass
如果运行下面的命令:
$ fab -H host1,host2,host3 runs_in_parallel runs_serially
将会按照下面的流程执行:
-
runs_in_parallel 并行运行在 host1、host2 和 host3 上
-
runs_serially 运行在 host1 上
-
runs_serially 运行在 host2 上
-
runs_serially 运行在 host3 上
代码中的@parallel,表示这个任务可以在多台机器上并行执行。也就是fab同时向各台机器发送执行命令。这里的host1,host2,host3,可以是ip或者域名。-H表示后面跟的主机参数。
命令行参数
你也可以使用命令行选项 -P 或者环境变量 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 将会并行执行,因为有一个 -P
命令选项。 runs_serially 顺序执行。
bubble 大小
主机列表很大时,用户的机器可能会因为并发运行了太多的 Fabric 进程而被压垮,因此,你可能会选择 moving bubble 方法来限制 Fabric 并发执行的活跃进程数。
bubble 是气泡的意思,这里表示每次运行多少个主机并发执行。
默认情况下没有使用 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 会自动启用行级输出,这会导致上面链接中提到的远程交互特性大部分失效,不过这是一个合理的折中。
行级输出混淆的情况在多进程的情况下是无法避免的,但是你可以设置主机地址作为前缀来区分。
未来版本会增加增强的日志支持来简化并行运行情况下的问题追踪。