主に自分用の備忘録です。
psshとは
psshは複数のサーバーに対してsshコマンドを同時に送ります。
複数台のサーバーがある場合、一つずつログインしていくのはとても大変なので、psshコマンドを使ってまとめて処理します。
他にもpscp, psftp, prsync など用途に応じてコマンドがあります。
私がMacを使っているので、Macを前提に以下、説明します。
psshのインストール (Mac: brew)
# brewのインストール
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
# psshのインストール
$ brew install pssh
インストール時に起こる可能性があるエラー
もし下記のようなエラーが出たら
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
Error: Failure while executing; `git config --local --replace-all homebrew.private true` exited with 1.
Xcodeのコマンドラインツールをインストールする必要があります。
$ xcode-select --install
もしPermission denied
エラーになったら
Error: An unexpected error occurred during the `brew link` step
The formula built, but is not symlinked into /usr/local
Permission denied @ dir_s_mkdir - /usr/local/Frameworks
Error: Permission denied @ dir_s_mkdir - /usr/local/Frameworks
$ brew doctor
を実行してみましょう。
$ brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!
Warning: The following directories do not exist:
/usr/local/Frameworks
/usr/local/sbin
You should create these directories and change their ownership to your account.
sudo mkdir -p /usr/local/Frameworks /usr/local/sbin
sudo chown -R $(whoami) /usr/local/Frameworks /usr/local/sbin
Warning: You have unlinked kegs in your Cellar
Leaving kegs unlinked can lead to build-trouble and cause brews that depend on
those kegs to fail to run properly once built. Run `brew link` on these:
python@2
下記ディレクトリがないぞ、って怒られてます。
[Warning]: The following directories do not exist:
/usr/local/Frameworks
/usr/local/sbin
ご丁寧に解決方法も教えてくれます。どうせなら実行してくれればいいのに。
You should create these directories and change their ownership to your account.
sudo mkdir -p /usr/local/Frameworks /usr/local/sbin
sudo chown -R $(whoami) /usr/local/Frameworks /usr/local/sbin
ヘルプの確認
*ヘルプの説明は日本語訳してあります。
(pssh version: 2.3.1)
Usage: pssh [OPTIONS] command [...]
Options:
--version バージョンナンバーの表示
--help このヘルプを表示
-h HOST_FILE, --hosts=HOST_FILE
ホストファイルの指定(1行ずつ "[user@]host[:port]")
-H HOST_STRING, --host=HOST_STRING
追加でホスト名を指定 ("[user@]host[:port]")
-l USER, --user=USER ユーザー名 (オプション)
-p PAR, --par=PAR 並列接続する最大数 (オプション)
-o OUTDIR, --outdir=OUTDIR
各ホストからの標準出力を指定のディレクトリ以下にファイルで出力 (オプション)
-e ERRDIR, --errdir=ERRDIR
各ホストからの標準エラー出力を指定のディレクトリ以下にファイルで出力 (オプション)
-t TIMEOUT, --timeout=TIMEOUT
各ホストでの実行タイムアウト時間 (秒) (0 = タイムアウトしない) (オプション)
-O OPTION, --option=OPTION
SSH オプションの指定 (オプション)
-v, --verbose psshの警告や診断メッセージの表示 (オプション)
-A, --askpass パスワードを尋ねる (オプション)
-x ARGS, --extra-args=ARGS
コマンドライン引数の展開。空白、クォート、バックスラッシュで複数指定可。
-X ARG, --extra-arg=ARG
コマンドライン引数の展開
-i, --inline 各サーバーごとに標準出力とエラーを集めて表示する
--inline-stdout 各サーバーからの標準出力をインラインで表示する
-I, --send-input 標準入力を読み取って、それをsshの標準入力に送る
-P, --print プログラムが受け取ったデータをそのまま表示
Example: pssh -h hosts.txt -l irb2 -o /tmp/foo uptime
ホストファイルの作り方
複数のサーバーをまとめて指定するのに、-hオプションでファイルを指定できます。
ファイルにはsshサーバーのリストを書いておきます。
admin@server1.mydomain.com
admin@server2.mydomain.com
admin@server3.mydomain.com
...
~/.ssh/config でサーバーに名前をつけておけば、いろいろsshオプションを指定できます。
.ssh/configファイルの書き方はここを参照してみてください。
Host gateway
HostName hogehoge.mydomain.com
IdentityFile ~/.ssh/id_rsa
User admin
Port 22
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
Host *-*
User admin
HostName localhost
TCPKeepAlive yes
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
Host surprise_az-main
ProxyCommand ssh -W %h:3101 gateway
Host surprise_az-sub
ProxyCommand ssh -W %h:3102 gateway
Host westminster_ca-main
ProxyCommand ssh -W %h:3103 gateway
Host westminster_ca-sub
ProxyCommand ssh -W %h:3104 gateway
surprise_az-main
surprise_az-sub
westminster_ca-main
westminster_ca-sub
...
psshの基本的な使い方
psshは次のように使います。
$> pssh -h hosts.txt -l <login username> -i "<shell command>"
例えば各マシンごとの起動時間を知りたければ、このようになります。
$> pssh -h hosts.txt -l admin -i "uptime"
[1] 12:10:40 [SUCCESS] p19santikos-main
14:10:40 up 10 min, 0 users, load average: 3.62, 3.38, 1.98
Stderr: Warning: Permanently added 'server1.mydomain.com' (RSA) to the list of known hosts.
Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
Killed by signal 1.
[2] 12:10:40 [SUCCESS] brenden_vacaville-main
12:10:40 up 10 min, 0 users, load average: 4.34, 3.78, 2.15
Stderr: Warning: Permanently added 'server1.mydomain.com' (RSA) to the list of known hosts.
Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
Killed by signal 1.
...
-iオプションの代わりに --inline-stdoutを使うとこうなります。シンプルですね。
$> pssh -h hosts.txt -l admin --inline-stdout "uptime"
[1] 12:12:37 [SUCCESS] brenden_vacaville-main
12:12:37 up 12 min, 0 users, load average: 3.86, 3.79, 2.35
[2] 12:12:37 [SUCCESS] p19santikos-main
14:12:37 up 12 min, 0 users, load average: 3.56, 3.45, 2.17
[3] 12:12:37 [SUCCESS] westminster_ca-main
12:12:37 up 12 min, 0 users, load average: 3.42, 3.43, 2.22
[4] 12:12:38 [SUCCESS] muvico_ca-main
12:12:38 up 12 min, 0 users, load average: 1.12, 1.01, 0.58
[5] 12:12:38 [SUCCESS] vannuys_ca-main
12:12:38 up 12 min, 0 users, load average: 1.39, 1.14, 0.70
[6] 12:12:38 [SUCCESS] brenden_nv-main
12:12:38 up 12 min, 0 users, load average: 3.55, 3.31, 2.02
[7] 12:12:39 [SUCCESS] p22santikos-main
14:12:38 up 12 min, 0 users, load average: 3.58, 3.43, 2.20
タイムアウトを設定する
ときどきコマンドの処理時間が長いことがあります。-tでタイムアウト時間を秒数で設定できます。
0を指定すれば、タイムアウトしません。
$> pssh -h hosts.txt -l admin --inline-stdout -t 0 "cd ~/code/; git pull"
tail -f をしたい
ログをファイルに出力している時、tail -f <log file>
でリアルタイムに確認したいことが多々あります。
-P
オプションを使えば可能です。
$> pssh -h hosts.txt -l admin --inline-stdout -t 0 -P "tail -f ~/logs/monitoring.log"
p19santikos-main: "socketIoStatus": "connected",
"socketIoFailCnt": 0,
"currentFragment": "Welcome"
}
{
"ip": "192.168.*.*",
"isAlive": true,
"pingFailCount": 0,
"timestamp": "2016-02-17 14:34:00 CST"
}
brenden_vacaville-main: "socketIoStatus": "connected",
"socketIoFailCnt": 0,
"currentFragment": "Welcome"
}
{
"ip": "192.168.*.*",
"isAlive": true,
"pingFailCount": 0,
"timestamp": "2016-02-17 12:34:00 PST"
}
brenden_nv-main: "socketIoStatus": "connected",
"socketIoFailCnt": 0,
"currentFragment": "Welcome"
}
{
"ip": "192.168.*.*",
"isAlive": true,
"pingFailCount": 0,
"timestamp": "2016-02-17 12:34:00 PST"
}
...
ファイルに保存したい
-o
オプションでディレクトリをすれば、標準出力をファイルにホストごとに保存してくれます。
同様に、-e
オプションで標準エラーです。
ディレクトリがなければ作成してくれます。ファイルが在ると上書きされます。
そのとき、hostnameコマンドを一緒に実行しておけば、ファイルの先頭にホスト名が入るので、あとで便利です。
$> pssh -h hosts.txt -l admin -o ./logs/ "hostname;uptime"
[1] 12:43:25 [SUCCESS] brenden_nv-sub
[2] 12:43:25 [SUCCESS] brenden_vacaville-sub
[3] 12:43:25 [SUCCESS] vannuys_ca-sub
[4] 12:43:26 [SUCCESS] muvico_ca-sub
[5] 12:43:26 [SUCCESS] westminster_ca-sub
[6] 12:43:26 [SUCCESS] p22santikos-sub
[7] 12:43:26 [SUCCESS] p19santikos-sub
$> cat logs/*
brenden_nv-sub
12:43:25 up 12 min, 0 users, load average: 3.46, 3.05, 1.84
brenden_vacaville-sub
12:43:25 up 12 min, 0 users, load average: 3.36, 3.38, 2.29
muvico_ca-sub
12:43:25 up 13 min, 0 users, load average: 3.55, 3.18, 1.97
p19santikos-sub
14:43:26 up 13 min, 0 users, load average: 3.39, 3.17, 2.00
p22santikos-sub
14:43:26 up 13 min, 0 users, load average: 3.43, 3.28, 2.02
vannuys_ca-sub
12:43:25 up 13 min, 0 users, load average: 3.25, 3.23, 2.15
westminster_sub
12:43:25 up 13 min, 0 users, load average: 3.19, 3.21, 2.19
sudoコマンドを実行したい
ときどきsudoコマンドを実行したくなります。でも**sudoでパスワード無しとかは「したくない」**です。
いくつか方法がありますが、私が最終的にたどり着いたのはこれ。
(echo "<password>")|pssh -h hosts.txt -l (login username) -o ./logs/ -x '-tt' -I "sudo whoami"
-I
オプションはpsshに対する標準入力をsshの標準入力に渡すオプションです。
つまりecho "password"の結果を標準入力に渡します。
-x
オプションはサーバーにログインした後のsshに対して、SSHコマンドの引数を展開します。
そこでは-tt
を指定しているわけですが、man ssh
を読むと次のように書いてあります。
*man sshの日本語訳はこちら
-t
**強制的に仮想端末を割り当てます。**これはリモートマシン上で任意の画面ベースのプログラムを実行するとき(たとえば、メニューサービスを実装するときなど)に非常に便利です。複数の-t をつけると、たとえssh がローカル側での端末を持っていない場合でも強制的に仮想端末を割り当てます。
つまり入力画面がなくても擬似的に割り当ててくれるわけです。
sudoコマンドは、ttyがないと実行できません。なので**-tt**でごまかしているわけです。
$ (echo "sudo_password")|pssh -h hosts_linux1.txt -l admin -x '-tt' --inline-stdout -I "sudo whoami"
[1] 10:32:55 [SUCCESS] linux1
[sudo] password for admin:
root
[2] 10:32:55 [SUCCESS] linux2
[sudo] password for admin:
root
[3] 10:32:55 [SUCCESS] linux3
[sudo] password for admin:
root
[4] 10:32:55 [SUCCESS] linux4
[sudo] password for admin:
root
sudoコマンドの代わりに、suコマンドでもできます。
(echo "<password>")|pssh -h hosts.txt -l admin -o ./logs/ -x '-tt' -I "su -c 'echo \"vm.overcommit_memory = 1\" >> /etc/sysctl.conf'"
(おまけ)ファイルを転送したい
psftpとかpscpとかいろいろあるけど、Macで使う場合は、psftpはないです(少なくとも私が探した範囲では。)
困ったことに、転送先のsshサーバーがscpに対応していないことがあります。
(AndroidでSSHサーバーを動かしていると、対応していないアプリがある。。。orz)
単純なファイル転送はこんな感じです。
SSHでscpを使わずにファイルをコピーするの解説が参考になります。
$> ssh hostA 'cat > destfile' < srcfile
でもこの方法だと、srcfileのファイルサイズ * N (Nは台数)となり辛いので、tarで圧縮して転送して、tarで解凍して保存します。
$> tar -cf - test.txt | ssh westminster_ca-stick "cd /sdcard/; tar -xf -"
これをpsshでまとめて転送するには、次のようにやります。
$> tar -cf - test.txt | pssh -h hosts.txt -l admin -t 0 -I "tar -xf -"
[1] 13:50:29 [SUCCESS] brenden_nv-sub
[2] 13:50:29 [SUCCESS] p19santikos-sub
[3] 13:50:29 [SUCCESS] vannuys_ca-sub
[4] 13:50:29 [SUCCESS] westminster_ca-sub
[5] 13:50:29 [SUCCESS] brenden_vacaville-sub
[6] 13:50:29 [SUCCESS] p22santikos-sub
[7] 13:50:30 [SUCCESS] muvico_ca-sub
確認のため、見てみましょう。test.txtには"hoge"とかいてあります。
$> pssh -h hosts.txt -l admin --inline-stdout "cat test.txt"
[1] 13:51:06 [SUCCESS] p19santikos-sub
hoge
[2] 13:51:06 [SUCCESS] vannuys_ca-sub
hoge
[3] 13:51:06 [SUCCESS] muvico_ca-sub
hoge
[4] 13:51:06 [SUCCESS] brenden_vacaville-sub
hoge
[5] 13:51:06 [SUCCESS] westminster_ca-sub
hoge
[6] 13:51:06 [SUCCESS] brenden_nv-sub
hoge
[7] 13:51:07 [SUCCESS] p22santikos-sub
hoge
成功!