並列実行

バージョン 1.3 で追加.

Fabric はデフォルトですべてのタスクを 連続 で実行します。(参照 実行ストラテジー ) このドキュメントは per-taskデコレーター、コマンドラインスイッチを使って複数のホストでタスクを 並行 で実行する Fabric のオプションを説明します。

どうなるか

Fabric 1.x は完全なスレッドセーフではありません。(一般的な使い方ではタスク関数は他に影響しません)この機能はPythonの multiprocessing モジュールで実装されています。これはそれぞれのホストとタスクコンビネーションで新しいプロセスを作り同時に多くのプロセスが走るのを避けるために任意(設定可能)のスライディングウィンドウを使います。

例えばいくつかのウェブサーバーでウェブアプリケーションのコードを更新してコードが配信されたら(コードの更新が失敗したら簡単にロールバックできる)サーバーをリロードする状況を想像してください。次のFabfileで実行できます:

from fabric.api import *

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

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

そして3つのサーバーで連続して実行するには:

$ fab -H web1,web2,web3 update reload

並列実行のオプションを有効にしなければ通常Fabricはこのように実行します:

  1. update on web1
  2. update on web2
  3. update on web3
  4. reload on web1
  5. reload on web2
  6. reload on web3

並列実行を有効(-P – 下記参照)にした場合はこのように:

  1. update on web1, web2, and web3
  2. reload on web1, web2, and web3

うまくいけば利益は明らかです – もし 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 on host1, host2, and host3
  2. runs_serially on host1
  3. runs_serially on host2
  4. runs_serially on host3

コマンドライン・フラグ

コマンドラインフラグ -P や環境変数 env.parallel を使ってすべてのタスクに並行処理を強制することもできます。しかし、どんなタスクも 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 はシーケンスで実行されます。

バブルサイズ

巨大なホストリストではローカルマシンが同時に実行されるあまりの多くのFabricプロセスのために高負荷になります。このため同時に走るFabricのアクティブプロセスの数を制限するムービング・バブルアプローチを選ぶこともできます。

デフォルトではバブルは使わずすべてのホストでひとつの poolで実行します。この per-taskレベルは 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

あるいは kwarg pool_size をスキップして:

$ fab -P -z 5 heavy_task

出力 行単位 対 バイト単位 (Linewise vs bytewise)

Fabricのデフォルトの端末への出力モードはバイト単位です。これは リモートプログラムとのやりとり のサポートのためです。これはパラレルモードでは悲惨なことになります。複数のプロセスが端末の標準出力に同時に書き込むかもしれません。

これを避けるためにパラレルが有効なときには自動的に Fabricの linewise output オプションが有効になります。これは上記リンクの Fabricのリモートインターラクティブ機能の便利さの大部分を失うことになりますが、パラレル呼び出しではうまく配置できません。一般的にはフェアな取引きです。

行単位の出力でも複数のプロセスの混乱を避けることはできません。しかしプレフィックス the host-string で少なくとも見分けることができます。

ノート

将来のバージョンでは並行処理のトラブルシューティングを簡単にするためにロギングサポートが改良されるでしょう。