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
...

追記終わり

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