読者です 読者をやめる 読者になる 読者になる

App::RemoteCommand リリース

App::RemoteCommand というのを cpan にあげた。

インストール方法

> cpanm App::RemoteCommand
> rcommand -v
App::RemoteCommand 0.01

なにするもの?

ssh複数ホストにはいって並列にコマンドを実行するスクリプト

なんで作ったか

fabric や chef を使えば複数ホストに並列にコマンドを実行できる。 が、専用のシンタックスで書かなくちゃいけないしイマイチカジュアルじゃない。

ssh, xargs を組み合わせて

> cat host-list.txt | xargs -P5 -L1 -I{} ssh {} 'uname -a'

というのもできるが、いろいろめんどくさい。

もっと直感的に扱えるのが欲しかった。

特徴

App::RemoteCommand およびそのフロントエンド rcommand の特徴は

  • 複数ホストに並列にコマンドを実行できる。
  • 複数ホストをいい感じに指定できる。
  • sudo password を最初に覚えておいて、以後自動で補完してくれる。
  • ローカルにあるスクリプトを直接指定できる。
  • コマンド出力に実行したホスト名、時刻をつけてくれる。
  • 最後にどのホストで成功したか、失敗したかのサマリーがでる。

使い方

基本

基本的には ssh HOST COMMAND と同じで

> rcommand HOSTS COMMAND

とする。( 現状の実装では ssh は no pass で通る前提になっているので もし ssh 鍵の password が必要な場合は事前に ssh-agent をあげておく。)

例えば www001.example.comuname コマンドを実行したい時は

> rcommand www001.example.com uname
[www001.example.com] Linux
SUCCESS www001.example.com

でいい。さらに www002.example.com にも実行したいなら

> rcommand 'www001.example.com,www002.example.com' uname
[www001.example.com] Linux
[www002.example.com] Linux
SUCCESS www001.example.com
SUCCESS www002.example.com

でいい。実際は、連番のホストに実行したいなら [*-*] という記法が使えるので

> rcommand 'www[001-002].example.com' uname
[www001.example.com] Linux
[www002.example.com] Linux
SUCCESS www001.example.com
SUCCESS www002.example.com

でOK。またデータセンター別にホスト名を変えている場合などは {*,*} という記法が使えるので、

> rcommand 'www001.example.{com,jp,us}' uname
[www001.example.com] Linux
[www001.example.us] Linux
[www001.example.jp] Linux
SUCCESS www001.example.us
SUCCESS www001.example.com
SUCCESS www001.example.jp

とできる。もちろん [*-*], {*,*} は同時に使えて

> rcommand 'www00[1-2].example.{com,jp}' 'uname -a'
...

も可能。ちなみにこのホスト名展開は String::Glob::Permute を使ってる。

ローカルスクリプト実行

実行するコマンドをワンライナーで書くのは時に面倒。 その場合はローカルでスクリプトを書いてそれを指定すればいい。

> cat local-script.sh
#!/bin/bash
percent=`df -h | grep /dev/sda1 | perl -anle '$F[-2] =~ s/%//; print $F[-2]'`
if [ $percent -lt 80 ]; then
  echo "OK $percent%"
else
  echo "NG $percent%"; exit 1
fi

> rcommand --script local-script.sh 'www00[1-2].example.jp'
[www002.example.jp] OK 65%
[www001.example.jp] NG 85%
SUCCESS www002.example.jp
FAIL www001.example.jp

また、上記のように最後にSUCCESS/FAIL のサマリーが出て どのホストで成功したか、失敗したがわかるようになってる。

sudo password

sudo が入っているコマンドを実行する場合、--ask-sudo-password オプションを指定し実行すると、最初にプロンプトが出てきてそこで sudo password を記憶し、以後自動で補完してくれる。

> rcommand --ask-sudo-password 'www00[1-2].example.jp' 'sudo service cron restart'
sudo password (asking with rcommand):
[www002.example.jp] sudo password (asking with rcommand):
[www002.example.jp]
[www001.example.jp] sudo password (asking with rcommand):
[www001.example.jp]
[www002.example.jp] cron stop/waiting
[www002.example.jp] cron start/running, process 7416
...

その他

[*-*], {*,*} を使えばだいたい対象ホストをいい感じで指定できると思うが、 時にファイルに書いたホストを対象としたい時があるかもしれない。 そんな時は --host-file が使える。

> cat host.txt
# comment
www001.example.jp
www002.example.com

> rcommand --host-file host.txt 'w | head -n1'
[www002.example.com]  15:35:11 up  2:00,  1 user,  load average: 0.00, 0.01, 0.05
[www001.example.jp]  15:35:12 up  2:00,  2 users,  load average: 0.03, 0.03, 0.05
...

--append-time を指定すれば各出力に時刻がつく。

> rcommand --append-time www001.example.jp 'echo 1; sleep 1; echo 2'
[2015-01-05 00:36:58][www001.example.jp] 1
[2015-01-05 00:36:59][www001.example.jp] 2
...

デフォルトで各出力につくホスト名が邪魔なら --no-append-hostname を指定すればいい。

> rcommand --no-append-hostname www001.example.jp 'uname -a'
Linux vagrant-ubuntu-trusty-64 3.13.0-30-generic #55-Ubuntu SMP Fri Jul 4 21:40:53 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
SUCCESS www001.example.jp

デフォルトだと rcommand は 5並列でコマンドを実行しようとする。 これを変えたい場合は --concurrency で指定すればいい。

> rcommand --concurrency 1 -a 'www00[1-3].example.{jp,com}' sudo service httpd stop
...

よくわからなくなったら rcommand --help とすればなんかでてくるはず。

TODO

  • コマンドの stdout/stderr が区別できず全部 stdout になってる。
  • 1 host あたり 3 process fork するのをなんとかしたい。
  • シグナルハンドリングがよくわかってない。
  • IO::Prompter を使いこなせない。
  • 特に多段sshをしてるとき、Input/output error (errno 5?) のエラーがでるときがある。