GNU Parallelがすごすぎて生きるのがつらい

皆さん今日もたくさんのサーバを相手にされていることかと思いますが、いくつかのサーバにアクセスして 1 秒間の統計情報(例えばvmstat 1 2)を集めてパッと表示したい時ってどうやってますかね?shell script を学びはじめたばっかりの僕はこんな感じで書いてました。

$ for i in host1 host2 host3; do ssh $i "vmstat 1 2 | tail -1"; done
 0  0      0 329004 210836 14275360    0    0     0  2424 1410 1828  0  0 100  0  0
 0  0      0 3716112 587704 25921684    0    0     0   488 1643 2026  0  0 100  0  0
 1  0      0 555440 265560 14015548    0    0     0  4204 1534 2392  1  0 99  0  0

vmstatとかiostatは最初の行は今の瞬間を見るには適して無いのでvmstat 1 2 | tail -1としていますが、これだと 1 ホストについて 1 秒かかってしまうことになります。数が少ないうちはこれでもいいんですが、例えば集める対象が 100 台とかになったら 100 秒かかってしまい、いったいいつの時点の値が欲しかったのかもわからないですし、awkで集計とかも意味がなくなってしまいます。

こういう処理は並列処理して、一気にばばっと集めてきたいですよね。

世の中にはそういうニーズに答えるツールというのが存在します。psshなんかも有名なようですが、僕は GNU Parallel をおすすめします。

YouTube にデモ動画が上がっているのでこのエントリの最後にも貼っておきましたが、かなり高機能でイカしてます。しかも中身はただの Perl のスクリプトなので Perl Monger の端くれの端くれとしては wktk せざるを得ません。バイナリじゃないのでmake installとかしたくなければ適当にPATHの通ってるところにファイル置いとけばいいですし。

ドキュメントには豊富な実行例もありますし、その部分を日本語に翻訳されてる方もいました。すばらしい。

色々高度すぎて、使い道に困るんですが、とりあえず冒頭の話題についてはこんな感じにすると対象が 100 台でも並列しまくってくれました(試しに 200 台位にやってみましたが 1 秒で取れました)。-j 1000%は今回の様に cpu を使うわけではない場合、全力で並列化しても問題ないので 1 コア当たり 10 個ぐらい割り当てても良いと思います。gzipの様な cpu を使う処理を使うときは、どう頑張ってもコア数以上の並列化は難しいのでご注意を。

$ parallel -j 1000% -u ssh {} 'vmstat 1 2 | tail -1' ::: host1 host2 ... host200

or

$ head -2 hosts
host1
host2
$ cat hosts | parallel -j 1000% -u ssh {} 'vmstat 1 2 | tail -1'

shell script 使ってると、スペース区切りで並べた時はfor、改行区切りで並べた時はwhile readを使い分けてたんですが、何気に parallel なら上記の様にどちらでも対応できる感じです。-j 1としてあげれば普通にforwhileで回してるときとおんなじ感じです。

出力結果をawkに食わせて 200 台の平均 cpu 使用率を vmstat の様に流すこともできます。

$ while : ; do \
  parallel -j 1000% -u ssh {} 'vmstat 1 2 | tail -1' \
  ::: host1 host2 ... host200 | \
  awk '{usr+=$13;sys+=$14;io+=$16}END{print usr/NR,sys/NR,io/NR}'; \
done
1.31544 0.610738 0.0402685
1.36242 0.590604 0.0872483
1.42953 0.583893 0.147651

めっちゃ ssh 張りまくりなので、頻繁にこういうことやるなら daemon 的なソフトウェアを書いた方がいいですが、怠惰にパパっとやりたいときには GNU Parallel を使うのも手だと思います。parallelで並列に情報をあつめてきて、awkで集計処理とか胸熱ですね!(集計とかもparallelでできるかもですが)

2011/04/09 追記

ちなみに上記でparallel -uとしてますが、-uとつけると各並列ジョブの出力をバッファせずにバーっとだしてくれます(素朴に並列処理やったときの出力ですね)。そうではなくて例えばsshしたホスト名もセットで出したいとかがあれば-uは付けずにこんな感じにすると良いと思います。

$ parallel -j 1000% ssh {} 'hostname; vmstat 1 2 | tail -1' ::: host1 host2 ... host200
host1
 0  0      0 329004 210836 14275360    0    0     0  2424 1410 1828  0  0 100  0  0
host2
 0  0      0 3716112 587704 25921684    0    0     0   488 1643 2026  0  0 100  0  0
host3
 1  0      0 555440 265560 14015548    0    0     0  4204 1534 2392  1  0 99  0  0
...

追記終わり

では最後に僕も感動した(デモ動画としてこういうのもアリか!という意味でも)動画はこちらです。