LoginSignup
1
1

More than 3 years have passed since last update.

運用/保守の手間を減らした話

Posted at

前提

作業端末はWindows。運用/保守対象はUnix系サーバが数十台。
このUnix系サーバで必要なデータをCSV形式で生成し、Windows上でゴニョゴニョするのが目的。
以下言語はksh93とPython

当時の状況

各サーバにログインしてコマンド叩いてテキストでデータ取ってた。
こんな非効率的なことはありえない。
改良してやる!

改良

第一手

各Unix系サーバ上に、必要なデータをCSV化するスクリプト(以下moge.sh)をばらまく。
これで1台あたり数分まで短縮。
数十台でスクリプト実行してからFilezilla等で取ってくる。それからWindowsでゴニョゴニョする。
まだ面倒。

第2手

どこか1台のサーバで全サーバのスクリプト起動できるようにして、ついでにコピーもしよう。
こんな感じ

kicker.ksh
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が遅いんだからこいつをバックグラウンドにしたらいいじゃん。
つーわけでこんな感じ

kicker2.ksh
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が都合がいいってことで言語決定。
※自分がお勉強したかったからという理由も当然ある。
こんな感じ

kicker3.py

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で非同期実行させたほうが
いいかもしれない。ということで研究開始。
紆余曲折を経てこんな感じ。

kicker4.py
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ってコマンドの与え方が複数あるんだろう?統一したらいいのに。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1