SCOOPとは
- ssh とPython の設定を適切にするだけで、簡単にネットワーク間での分散処理が実行できる。
- ネットワーク分散に対応していないライブラリに便利。
- 関数の処理を分散して行い、返り値をまとめて返してくれる。
- Core 数に応じた Worker 数の設定が可能
- Docker との相性が抜群
基本動作
python 標準の map 関数の使い方で ssh で繋いだ先(ノード)でも分散処理してくれる。multiprocessing.Pool.map のネットワーク処理に対応した版。
map 関数に配列を渡すと配列の各要素を引数に関数を実行する。その際の各処理を各ノードでマルチプロセスで実行して、ホストPCに変数を返す。
通信には pickle 化された情報を送信している。
ssh で接続先の通信用ポートを開けて、ポートの情報をホストに送って通信。--tunnel
オプションを使えば、localhost 上に port fowarding する。この際のホストIPの解決には、ssh 上の host name が使われる。
サンプルコード
import random
import time
import scoop
import os
from uuid import getnode as get_mac
data = [random.randint(-1000, 1000) for r in range(1000)]
class MyClass(object):
def __init__(self):
pass
def my_method(self, data):
time.sleep(0.01)
return((os.getpid()))
if __name__ == '__main__':
# SCOOP's parallel function
t = MyClass()
process_list = list(scoop.futures.map(t.my_method, data))
unique_process = set(process_list)
p_nums = {}
for pid in unique_process:
p_nums[pid] = len([tmp_pid for tmp_pid in process_list if tmp_pid == pid])
scoop.logger.info("last_value : %s"%(p_nums))
scoop.futures.map
という関数で並列したい関数と、並列したい引数を配列で渡す。
実行スクリプト
python -m scoop --host docker-host1 docker-host2 docker-host3 -n 16 --tunnel scoop_test.py
実行結果
[2017-02-12 14:27:32,585] launcher INFO SCOOP 0.7 1.1 on linux2 using Python 2.7.12 (default, Feb 7 2017, 14:01:56) [GCC 5.4.0 20160609], API: 1013
[2017-02-12 14:27:32,585] launcher INFO Deploying 16 worker(s) over 3 host(s).
[2017-02-12 14:27:32,585] launcher INFO Worker distribution:
[2017-02-12 14:27:32,585] launcher INFO docker-host1: 5 + origin
[2017-02-12 14:27:32,586] launcher INFO docker-host2: 5
[2017-02-12 14:27:32,586] launcher INFO docker-host3: 5
[2017-02-12 14:27:34,415] __main__ INFO Worker(s) launched using /bin/bash
[2017-02-12 14:27:35,551] scoop_test (127.0.0.1:58127) INFO last_value : {18305: 77, 3842: 45, 18307: 77, 3835: 47, 18309: 77, 18311: 78, 18313: 78, 18314: 51, 3837: 47, 10199: 65, 10201: 66, 10203: 66, 3841: 48, 10205: 65, 10206: 67, 3839: 46}
[2017-02-12 14:27:35,879] launcher (127.0.0.1:41977) INFO Root process is done.
[2017-02-12 14:27:33,542] __main__ INFO Worker(s) launched using /bin/bash
[2017-02-12 14:27:34,092] __main__ INFO Worker(s) launched using /bin/bash
[2017-02-12 14:27:36,533] launcher (127.0.0.1:41977) INFO Finished cleaning spawned subprocesses.
last_value: の出力で、16個の pid
上で処理されているのが確認できる。
使い方
説明はウェブサイト上にもあるが、動かないサンプルコードも多くはまるので注意。
1. 分散処理をしたいPCでPythonの実行環境を揃える。
標準ではホストPC での Python の実行環境が接続先に渡されて分散処理されることになる。そのため、pyenv 等で /home/user
ディレクトリ下に実行環境があると user 名が一致しないためちゃんと動かない。/usr/local/bin/python に統一しておく等の処置が必要になる。
処理に必要なファイル等があれば、それも同一のパスとして各ノードに配置。
Docker との相性が良いのもこのあたり。Docker であれば、容易に同一環境を構築できる。
2. ssh サーバーの設定
パスワードなしの認証キー認証にする。パスワードありには対応していない。
3. ホストPCと各ノードでの ssh config の設定
ssh config での host name が ノードの PC の名前かIPでないと、エラーが出る。このあたりの説明がまったくなくて、ソースコードを追っかけるはめになった。
Docker上で実行したければ、--tunnel
オプションをつければ大丈夫。--tunnel
オプションを付けていれば、host name の問題は気にしなくていい。--tunnel
オプションに関する説明もドキュメントにはないので注意。
4. 実行時
python -m scoop --host docker-host1 docker-host2 docker-host3 -n 16 --tunnel scoop_test.py
--host
ssh config 上での host name-n
合計のワーカー数--tunnel
ssh tunnel を使う
他にもホストごとにワーカー数を設定したりできるが、そのあたりは公式ドキュメントを参照してください。
導入の上での注意
- python の実行環境をすべて一致させておく必要がある。
- ここでの実行環境とは、フルパスでの実行パス。
- 各ライブラリ、実行ファイルともに同一パスに保存していること。
- Docker を使おう。
- 読み込むファイル等があれば、そのファイルはすべてのノード上に置く
- 使用するポートは予め開けておくか、
--tunnel
オプションを使う。 - ssh の設定名とホストPC の名前を一致、もしくはIPアドレスにしておく。
- 一致しない場合は
--external-hostname
オプションを使うが、一致させた専用のconfigファイルを作って置くほうが良い - IP の取得を PC の名前から取得しようとする。ローカルネットワーク内のみ、この方法が使える。
/etc/host
を編集して、ssh config の host name に IP を割り当ててもいけるはず。
- 一致しない場合は
雑感
分散処理したいメソッド内でshell command を叩いて、返り値を得たり等をすれば、Python 以外の言語の並列化にも使える。システムトレードのバックテストの並列化にはおすすめ。
コメント