本日も乙

ただの自己満足な備忘録。

複数サーバで同時操作したい時の方法まとめ

今日も飽きずに過去のメモを掘り起こして記事にしました


全台、もしくは一部のサーバに対して一斉にコマンド実行したい場合があります。
調べてみると色々な方法があるみたいなので、いくつか紹介します。
他にあれば随時追加するかも。

tmux

ローカルPCにtmuxをインストールし、~/.tmux.conf に以下の設定を追加します。

# $HOME/.tmux.conf

bind e setw synchronize-panes on
bind E setw synchronize-panes off

同じWindowに複数paneを開いてそれぞれ複数サーバにSSHログインし、<bind>+eすると、paneで操作を同期することができます。
<bind>+Eで同期が終了します。

メリット

  • SSHパスフレーズやsudoのパスワードなどを考えなくて済む(自分で入力するため)
  • いつも自分がやっている作業をそのままできる、余計なコマンドを覚えなくても済む

デメリット

  • 複数panelを立ち上げてそれぞれサーバにログインするのが面倒
  • panel同期後、コマンド補完ができなかったりするため、予め実行するコマンドをまとめておいて、コピペして実行した方が良い

参考URL

pdsh(Parallel Distributed Shell)

https://code.google.com/p/pdsh/

インストール

$ sudo yum --enablerepo=epel install pdsh

$ pdsh -V
pdsh-2.26 (+readline+debug)
rcmd modules: ssh,exec (default: ssh)
misc modules: (none)

基本的な使い方

$ pdsh -w foo_server[1,2] "hostname"
foo_server1: foo_server1
foo_server2: foo_server2

sudo を使う場合そのままでは渡せないので、次のように強制的にttyを飛ばすようにします。

# $HOME/.ssh/PASS
<sudoのパスワード>
$ chmod 600 ~/.ssh/PASS
$ export PDSH_SSH_ARGS="-tt"

$ pass=$(cat ~/.ssh/PASS) && pdsh -w foo_server[1,2] "echo $pass | sudo -S ls /root 2> /dev/null" 2> /dev/null | dshbak
----------------
foo_server1
----------------
hoge_dir
----------------
foo_server2
----------------
hoge_dir

sudo権限で/rootをlsコマンドで見るようにしてみました。$HOME/.ssh/PASSからsudoパスワードを読み込むことでヒストリに残さないようにしています。dshbakコマンドを使うことで出力を見やすくできるのでよく使っています。

メリット

デメリット

pssh(pararell-ssh)

https://code.google.com/p/parallel-ssh/

インストール

$ sudo yum --enablerepo=epel install pssh

$ pssh --version
2.3.1

使い方

$ pssh -i --host=foo_server1 -l foo_user -i "cat /etc/redhat-release"
[1] 16:29:28 [SUCCESS] foo_server1
CentOS release 6.5 (Final)

複数ホストを実行したい場合は、--host(-H)オプションを複数付けてもできるが、ホスト一覧のファイルを用意して-hオプションで指定した方が楽です。

# hostlist.txt

foo_server1
foo_server2
$ pssh -i -h ./hostlist.txt -l foo_user -i "cat /etc/redhat-release"
[1] 17:20:19 [SUCCESS] foo_server1
CentOS release 6.5 (Final)
[2] 17:20:19 [SUCCESS] foo_server2
CentOS release 6.5 (Final)

しかし、パスフレーズ付きの秘密鍵の場合、パスワードの入力が求められます。
解決するには、苦し紛れですがソースを改変します。

# /usr/lib/python2.6/site-packages/psshlib/askpass_client.py L.69
if not prompt.strip().lower().endswith('password:'):
    sys.stderr.write(prompt)
    sys.stderr.write('\n')
    #sys.exit(1)   # コメントアウト

--askオプションを付けるとパスワードを聞かれるのでそれに返すとコマンドを実行できます。
Stderrが出ていて気持ち悪いが気にしないことにします。

$ pssh --ask -i --host=foo_server1 -l foo_user -i "cat /etc/redhat-release"

Warning: do not enter your password if anyone else has superuser
privileges or access to your account.
Password:
[1] 17:07:34 [SUCCESS] foo_server1
CentOS release 6.5 (Final)
Stderr: Enter passphrase for key '/home/foo_user/.ssh/id_rsa':

sudoを使う場合は -x 'tt' で強制的にttyを飛ばすようにすれば良いです。

# $HOME/.ssh/PASS
<sudoのパスワード>
$ chmod 600 ~/.ssh/PASS

$ pass=$(cat ~/.ssh/PASS) && pssh --ask -i -x '-tt' --host=foo_server1 -l foo_user -i "echo $pass | sudo -S  ls /root" 2> /dev/null

Warning: do not enter your password if anyone else has superuser
privileges or access to your account.
Password:
[1] 17:10:59 [SUCCESS] foo_server1
[sudo] password for foo_user:
scripts
Stderr: Enter passphrase for key '/home/foo_user/.ssh/id_rsa':
tcgetattr: Invalid argument
Connection to foo_server1 closed.

メリット

デメリット

  • Stderrが出てくるので気持ち悪い

最後に

ここまで書いてふと思ったのがAnsibleでもできるかなと思いました。
良い方法があれば教えてください。