pdsh を使って複数ホストでコマンドを同時実行する
「複数のリモートホストで同じコマンドを一斉実行したい」と思うことはよくありますが、これらのツールを使うことで実現出来ます。
この中でも Parallel Distributed Shell(以下、pdsh)は学習コストが低く、簡単に使い始めることが出来ます。今回はこの pdsh を使ってみます。前提条件として、ssh で公開鍵認証方式を利用するには などを参考に、操作元となるホストから操作対象となる複数のリモートホストへ ssh の公開鍵認証方式でログイン出来るよう、予め設定しておきます。
インストール
RPMforge で pdsh のパッケージが配布されていたので、今回はそちらを利用してインストールしました。
"-V" オプションでバージョン情報を表示させることが出来ます。
$ pdsh -V
pdsh-2.23 ( +readline)
rcmd modules: xcpu,ssh,exec ( default: ssh)
misc modules: machines,dshgroup ( *conflicting: nodeattr,netgroup,genders)
[ * To force-load a conflicting module, use the -M <name> option]
グループを指定して実行する
pdsh では予めグループを定義しておき、そのグループに対して同じコマンドを実行することが出来ます。pdsh で指定したグループ名は以下の順序で検索されるようです(ホームディレクトリ配下を優先するようです)。
~/.dsh/group/
/etc/dsh/group/
グループの定義ファイルを保存するディレクトリを作成します。ここにグループを定義したファイルを保存することになります。
今回は "TEST-HOSTS" というファイルを以下の内容で作成しました。これは "TEST-HOSTS" というグループを定義し、そのグループには "192.168.1.201, 202, 203 を含める、という意味になります。
$ cat ~/.dsh/group/TEST-HOSTS
192 .168.1.201
192 .168.1.202
192 .168.1.203
pdsh に "-g グループ名" オプションを付与し、グループを指定した上で、実行したいコマンドを記載します。実行例は以下の通りです。
$ pdsh -g TEST-HOSTS uname -a
192 .168.1.202: Linux centos-02.local 2 .6.18-194.26.1.el5.centos.plus #1 SMP Wed Nov 10 12:04:55 EST 2010 x86_64 x86_64 x86_64 GNU/Linux
192 .168.1.203: Linux centos-03.local 2 .6.18-194.26.1.el5 #1 SMP Tue Nov 9 12:54:20 EST 2010 x86_64 x86_64 x86_64 GNU/Linux
192 .168.1.201: Linux centos-01 2 .6.36.1 #2 SMP Tue Nov 30 00:12:54 JST 2010 x86_64 x86_64 x86_64 GNU/Linux
ホストを指定して実行する
"-w" オプションに続けてアドレス(もしくは、名前解決可能なホスト名)を指定することで、そのホストに対してコマンドを実行することが出来ます。正規表現を用いることが可能で、以下のように指定することで 192.168.1.201, 202, 203 の三台に対して同時にコマンドを実行することが出来ます。
$ pdsh -w 192 .168.1.20[ 1 -3] uptime
192 .168.1.201: 11 :26:36 up 14 :50, 3 users, load average: 0 .05, 0 .02, 0 .00
192 .168.1.203: 20 :26:37 up 14 :49, 0 users, load average: 0 .00, 0 .00, 0 .00
192 .168.1.202: 20 :26:49 up 14 :49, 0 users, load average: 0 .21, 0 .06, 0 .01
dshbak で出力結果を整形する
pdsh の実行結果を dshbak にパイプすると結果を整形することが出来ます。dshbak を利用しない場合、pdsh の実行結果は以下のようになります。各行の先頭に対象の IP アドレスが記載されている為、あまり見やすいとは言えません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 $ pdsh -g TEST-HOSTS cat /proc/cpuinfo
192 .168.1.203: processor : 0
192 .168.1.203: vendor_id : GenuineIntel
192 .168.1.203: cpu family : 6
192 .168.1.203: model : 23
192 .168.1.203: model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
192 .168.1.203: stepping : 10
192 .168.1.203: cpu MHz : 2654 .594
192 .168.1.203: cache size : 3072 KB
192 .168.1.203: fpu : yes
192 .168.1.203: fpu_exception : yes
192 .168.1.203: cpuid level : 13
192 .168.1.203: wp : yes
192 .168.1.203: flags : fpu ... sse4_1 lahf_lm
192 .168.1.203: bogomips : 5309 .18
192 .168.1.203: clflush size : 64
192 .168.1.203: cache_alignment : 64
192 .168.1.203: address sizes : 40 bits physical, 48 bits virtual
192 .168.1.203: power management:
192 .168.1.203:
192 .168.1.202: processor : 0
192 .168.1.202: vendor_id : GenuineIntel
192 .168.1.202: cpu family : 6
192 .168.1.202: model : 23
192 .168.1.202: model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
192 .168.1.202: stepping : 10
192 .168.1.202: cpu MHz : 2654 .594
192 .168.1.202: cache size : 3072 KB
192 .168.1.202: fpu : yes
192 .168.1.202: fpu_exception : yes
192 .168.1.202: cpuid level : 13
192 .168.1.202: wp : yes
192 .168.1.202: flags : fpu ... sse4_1 lahf_lm
192 .168.1.202: bogomips : 5309 .18
192 .168.1.202: clflush size : 64
192 .168.1.202: cache_alignment : 64
192 .168.1.202: address sizes : 40 bits physical, 48 bits virtual
192 .168.1.202: power management:
192 .168.1.202:
192 .168.1.201: processor : 0
192 .168.1.201: vendor_id : GenuineIntel
192 .168.1.201: cpu family : 6
192 .168.1.201: model : 23
192 .168.1.201: model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
192 .168.1.201: stepping : 10
192 .168.1.201: cpu MHz : 2654 .594
192 .168.1.201: cache size : 3072 KB
192 .168.1.201: fpu : yes
192 .168.1.201: fpu_exception : yes
192 .168.1.201: cpuid level : 13
192 .168.1.201: wp : yes
192 .168.1.201: flags : fpu ... hypervisor lahf_lm dts
192 .168.1.201: bogomips : 5309 .18
192 .168.1.201: clflush size : 64
192 .168.1.201: cache_alignment : 64
192 .168.1.201: address sizes : 40 bits physical, 48 bits virtual
192 .168.1.201: power management:
192 .168.1.201:
pdsh の実行結果を dshbak へパイプすると、各ノードの出力結果ごとに見やすくまとめてくれます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 $ pdsh -g TEST-HOSTS cat /proc/cpuinfo | dshbak
----------------
192 .168.1.201
----------------
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
stepping : 10
cpu MHz : 2654 .594
cache size : 3072 KB
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu ... hypervisor lahf_lm dts
bogomips : 5309 .18
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
----------------
192 .168.1.202
----------------
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
stepping : 10
cpu MHz : 2654 .594
cache size : 3072 KB
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu ... sse4_1 lahf_lm
bogomips : 5309 .18
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
----------------
192 .168.1.203
----------------
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
stepping : 10
cpu MHz : 2654 .594
cache size : 3072 KB
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu ... sse4_1 lahf_lm
bogomips : 5309 .18
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
更に、dshbak に "-c" オプションを指定すると、出力結果が重複する(=全く同じ)部分は二重に表示せず、まとめてくれるようになるので出力結果はかなりスッキリします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 $ pdsh -g TEST-HOSTS cat /proc/cpuinfo | dshbak -c
----------------
192 .168.1.201
----------------
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
stepping : 10
cpu MHz : 2654 .594
cache size : 3072 KB
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu ... hypervisor lahf_lm dts
bogomips : 5309 .18
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
----------------
192 .168.1.[ 202 -203]
----------------
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel( R) Core( TM) 2 Duo CPU P8800 @ 2 .66GHz
stepping : 10
cpu MHz : 2654 .594
cache size : 3072 KB
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu ... sse4_1 lahf_lm
bogomips : 5309 .18
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
pdsh から sudo し、管理者権限でコマンドを実行する
pdsh からリモートホストで "sudo" してからコマンドを実行しようとすると、以下のようなエラーになります。
$ pdsh -w 192 .168.1.203 'sudo uptime'
192 .168.1.203: sudo: sorry, you must have a tty to run sudo
pdsh@centos-01: 192 .168.1.203: ssh exited with exit code 1
これは pdsh ではなく、ssh に原因があります。
$ ssh admin@192.168.1.203 sudo uptime
sudo: sorry, you must have a tty to run sudo
例えば「ssh admin@192.168.1.203」のように ssh で対話的にログインすれば TTY が割り当てられますが、「ssh admin@192.168.1.203 uptime」のように、ワンライナーでコマンドを実行させる(この場合は 192.168.1.203 へ admin ユーザでログインし、"uptime" コマンドを実行させる)と ssh が TTY を割り当てません。この「TTY を割り当てない」実装の理由は推測の域を出ませんが、おそらくワンライナーの実行は一瞬で終わるにもかかわらず、都度、TTY を割り当てていてはリソースが無駄だから TTY を割当てない・・・ような気がします。ここまでを整理すると、以下のようになります。
ssh は、対話ログイン時は TTY を割り当てる
ssh は、ワンライナーでの実行時は TTY を割り当てない
話は変わって、sudo はデフォルトでは TTY を割り当てられていないスクリプトなどから実行された場合は sudo を許可しない、という動作をします。これを上記と組み合わせると、このように整理できます。
ssh は、対話ログイン時は TTY を割り当てる → sudo 出来る
ssh は、ワンライナーでの実行時は TTY を割り当てない → sudo 出来ない
ssh は "-t" オプションを指定することで強制的に TTY を割り当てることが出来ますので、"-t" オプションを使えば「リモートホストで sudo 出来ない」問題は解決出来ます。
$ ssh -t admin@192.168.1.203 sudo uptime
20 :58:53 up 15 :21, 1 user, load average: 0 .32, 0 .07, 0 .02
Connection to 192 .168.1.203 closed.
pdsh は ssh の挙動を以下の環境変数で制御することが出来ます。
PDSH_SSH_ARGS
pdsh から ssh する際の、デフォルトのオプションを指定する。何も指定しなければ "-a -2 -x -lユーザ名 ホスト名" を指定したことになる
PDSH_SSH_ARGS_APPEND
これら環境変数の処理は pdsh-2.22 の場合、"src/modules/sshcmd.c" で以下のように実装されています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 263 static int sshcmd_args_init (void )
264 {
265 char *val = NULL ;
266 char *str = NULL ;
267 List args_list = NULL ;
268
269 if (! (val = getenv ("PDSH_SSH_ARGS" )))
270 val = "-2 -a -x -l%u %h" ;
271
272 str = Strdup (val );
273 args_list = list_split (" " , str );
274 Free ((void ** ) &str );
275
276 if ((val = getenv ("PDSH_SSH_ARGS_APPEND" ))) {
277 str = Strdup (val );
278 args_list = list_split_append (args_list , " " , str );
279 Free ((void ** ) &str );
280 }
281
282 ssh_args_list = args_list ;
283
284 return (0 );
285 }
しかし、PDSH_SSH_ARGS_APPEND 環境変数を "-t" と定義しても、やはり pdsh から sudo 出来ません・・・仕方がありませんので、リモートホスト側の /etc/sudoers 中、以下のように定義されている箇所をコメントアウトし、TTY が無くても sudo 出来るよう、sudo 側を変更します。この変更を行うにあたってはセキュリティリスクを十分に理解してから設定変更を行う必要があります。
上記箇所を以下のようにコメントアウトします。
これで pdsh から sudo 出来るようになりました。
$ pdsh -w 192 .168.1.20[ 1 -3] sudo uptime
192 .168.1.201: 12 :30:07 up 15 :53, 2 users, load average: 0 .00, 0 .00, 0 .00
192 .168.1.203: 21 :30:07 up 15 :53, 0 users, load average: 0 .00, 0 .00, 0 .00
192 .168.1.202: 21 :30:20 up 15 :53, 0 users, load average: 0 .00, 0 .00, 0 .00
リモートサイトとファイルのやりとりをする
pdsh をインストールすると "pdcp" と "rpdcp" というコマンドがインストールされますが、これらを使うと複数のリモートホストへファイルをコピーしたり、逆にリモートホストからファイルをコピーしたり出来ます。リモートホストへファイルをコピーするには "pdcp" を使います。
pdcp -w 192 .168.1.20[ 1 -3] TEST ~
リモートホストからファイルをコピーするには "rpdcp" を使います。
$ rpdcp -w 192 .168.1.20[ 1 -3] TEST ./
$ ls -al TEST*
-rw-rw-r-- 1 admin admin 0 Dec 19 13 :07 TEST.192.168.1.201
-rw-rw-r-- 1 admin admin 0 Dec 19 13 :07 TEST.192.168.1.202
-rw-rw-r-- 1 admin admin 0 Dec 19 13 :07 TEST.192.168.1.203
ファイル名にはサフィックスにホスト名が付与されます。