ローカルのシェルスクリプトをリモート実行する
はじめに
「ほぼ」同じ構成のサーバがN台あって,それぞれに対して同じスクリプトを実行したい。このときスクリプトはひとつに集約したい(=各サーバにスクリプト配置したくない)。こういうときって、どうするのが定番なんだろう・・・
— Kensho (@ni66ling) February 14, 2015
ローカルのスクリプトをリモートで実行したいけどどうやれば?という話.
どうやるのが定番なのか?は結局わかっていないが,一応できるにはできたのでメモ.
やりかた
sshコマンドにパイプでローカルのスクリプトを引き渡す.*1
$ cat [local_script.sh] | ssh [user]@[host] bash
補足: ローカルにおける変数をリモートスクリプト内で使うには
プロセス置換*2を用いる.
$ param="local" $ cat <(echo -e "param=\"${param}\"\n") [local_script.sh] | ssh [user]@[host] bash
ただし,懸念点として,リモート実行するスクリプト内容が分散し,見通しが悪くなってしまう問題がある.
もっといい方法をご存知の方がいらっしゃったらお教えいただけると嬉しいです.
補足: cronやjenkinsからの自動実行するには
パスワード認証を自動突破するためにexpectを用いる.*3
ソースコードで示すと以下のとおり.
#!/bin/sh # ローカルのスクリプト(リモート実行する) remote_command_file="./local_script.sh" # expectでSSH接続 ([hoge]は適宜読み替えて) expect -c " set timeout -1 log_file $log_file spawn bash -c \"cat $remote_command_file | ssh [user]@[host] bash\" expect \"password\" ; send \"[password]\n\" expect eof # 注:interactとしないこと exit # 注:interactとしないこと "
expectに関する注意として,interact
とはせず,expect eof; exit
とすること.
ターミナルから実行する場合はinteract
で良いが,cronやjenkinsから実行する場合はexpect eof; exit
とする必要がある.*4*5
*1:注意点として,パイプで渡されるsshでは,bashを引数にすること.bashを引数にしなくても実行はできるが,標準エラー「Pseudo-terminal will not be allocated because stdin is not a terminal.」が出力されてしまう.また,さらに補足すると,この標準エラーは多段接続時に「-t」パラメータを指定しなかった場合にも出力される.
*2:http://sechiro.hatenablog.com/entries/2013/08/15
*3:公開鍵認証かつパスワードなしの設定であれば,expectを使う必要はない.$ ssh -i [鍵path] [user]@[host] bash, もしくは, sshのconfigファイルを読ませるなど
*4:http://yu-write.blogspot.jp/2013/11/shell-crontabexpect.html
*5:ここでかなりハマった…