前提
作業端末はWindows。運用/保守対象はUnix系サーバが数十台。
このUnix系サーバで必要なデータをCSV形式で生成し、Windows上でゴニョゴニョするのが目的。
以下言語はksh93とPython
当時の状況
各サーバにログインしてコマンド叩いてテキストでデータ取ってた。
こんな非効率的なことはありえない。
改良してやる!
改良
第一手
各Unix系サーバ上に、必要なデータをCSV化するスクリプト(以下moge.sh)をばらまく。
これで1台あたり数分まで短縮。
数十台でスクリプト実行してからFilezilla等で取ってくる。それからWindowsでゴニョゴニョする。
まだ面倒。
第2手
どこか1台のサーバで全サーバのスクリプト起動できるようにして、ついでにコピーもしよう。
こんな感じ
cat servers.list | while read HOST ; do
ssh ${HOST} -n /hoge/moge.sh
scp ${HOST}:/hoge/moge.csv ./data/${HOST}.csv
done
終わったら1台から全CSVをFilezilla等で取ってくる。
でも直列だからスクリプトが終わるまで1時間位かかる。
第3手
sshが遅いんだからこいつをバックグラウンドにしたらいいじゃん。
つーわけでこんな感じ
cat servers.list | while read HOST ; do
ssh ${HOST} /hoge/moge.sh &
PID[${HOST}]=$!
done
while [[ ${#PID[*]} -ne 0 ]] ; do
for HOST in ${!PID[*]} ; do
if ! ps -ef | grep -w ${PID[${HOST}]} ; then
scp ${HOST}:/hoge/moge.csv ./data/${HOST}.csv
unset PID[${LPAR}]
fi
done
sleep 1
done
数分で終わるようになったけど、結局WindowsからFilezilla的なものが面倒。
第4手
CSVをゴニョゴニョするのにexcel作れるPythonが都合がいいってことで言語決定。
※自分がお勉強したかったからという理由も当然ある。
こんな感じ
with open("server.lst", "r", encoding='shift-jis') as fd:
for host in fd:
proc = subprocess.Popen(["ssh", host, "/hoge/moge.sh"], stdout=DEVNULL, stderr=DEVNULL)
proclist[proc.pid] = proc
while proclist:
for pid in list(proclist):
if proclist[pid].poll() is not None:
del proc[pid]
subprocess.Popen(["scp", host + ":/hoge/moge.csv", "./" + host + ".csv"], stdout=DEVNULL, stderr=DEVNULL)
sleep(1)
WSLだと動く、でもコマンドプロンプト向けにすると途中で止まる。server.listの中身を少なくする(5~6個まで)なら動く。
なんで?
第4手延長戦
全くわからなかったので、取り敢えずPythonのasyncioで非同期実行させたほうが
いいかもしれない。ということで研究開始。
紆余曲折を経てこんな感じ。
async def _run(host):
ssh = "ssh %s /hoge/moge.sh" % host
p = await asyncio.create_subprocess_exec(ssh, stdout=DEVNULL, stderr=DEVNULL)
await p.wait()
scp = ["scp", host + ":/hoge/moge.csv", "./" + host + "%s.csv"]
p = subprocess.run(scp, stdoutDEVNULL, stderr=DEVNULL)
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
with open("server.lst", "r", encoding='shift-jis') as fd:
funcs = asyncio.gather(*[_run(host) for host in fd])
loop.run_until_complete(funcs)
これまた動かない。全く事象は変わらない。
ここで気づいたこと。止まったときにEnterを叩くと動く。
なんで?
標準入力がなにか問題?
解決
最初のkshのスクリプトに正解が書いてあります。
sshの**"-n"オプション**が原因です。
kshではreadとsshが標準入力を食い合うから"-n"が必要だと思っていたので、
最初からつけていました。ところがPythonはssh以外に標準入力を食うやつが
いないので、何もしないでもいいだろうという先入観が問題だったようです。
バックグラウンドで複数のsshを起動するにあたり、標準入力は制御が必要なようです。
(stdin=subprocess.DEVNULL
は駄目でした)
WSLで動いたのはまだ謎のままですが、もう使わないので調査しません。
なぜWSLを使わなくしたのか
Pythonスクリプトの中で、Windowsでゴニョゴニョのためにopenpyxlも使っています。
これがWSL上だと時々BSODを食らわしてくれるので、コマンドプロンプトから動く
ように改修しました。
素朴な疑問
なんでsubprocessってコマンドの与え方が複数あるんだろう?統一したらいいのに。