TL;DR
必要なもの (awscli, jq, peco, tmux, tmux-cssh) をすべて入れた状態で
$ sh -c "tmux-cssh -i <ssh_key> -u <ssh_user> $(aws ec2 describe-instances | jq -r '.Reservations[] | .Instances[] | select(.State.Name != "terminated") | select(has("PublicIpAddress")) | [.PublicIpAddress,.PrivateIpAddress,.State.Name,(.Tags[] | select(.Key == "Name") | .Value // "")] | join("\t")' | peco | awk '{ print $1 }' | tr '\n' ' ')"
とすることで、peco で EC2 インスタンスを選んで同時に SSH アクセスすることができます。
tmux 上で生活している方は、一度 tmux detach
して tmux の外に出ないと tmux-cssh が実行できません。
また、ターミナル起動時に自動で tmux を立ち上げる設定は無効化しておく必要があります。
awscli + jq + peco でインスタンスを選ぶ
まず、peco を使って SSH したいインスタンスを選びます。
$ aws ec2 describe-instances | jq -r '.Reservations[] | .Instances[] | select(.State.Name != "terminated") | select(has("PublicIpAddress")) | [.PublicIpAddress,.PrivateIpAddress,.State.Name,(.Tags[] | select(.Key == "Name") | .Value // "")] | join("\t")' | peco | awk '{ print $1 }' | tr '\n' ' '
長いのでそれぞれ分解してみましょう。
awscli
$ aws ec2 describe-instances
EC2 インスタンス一覧を JSON で取得する
jq
$ jq -r
-r
でクォート付けずに出力する
以下クエリ部分については、
-
.Reservations[] | .Instances[]
- すべてのインスタンス
-
select(.State.Name != "terminated")
- Terminate されてないインスタンス
- (
== "running"
でもよさそう)
-
select(has("PublicIpAddress"))
- Public IP がアサインされてるインスタンス
-
[.PublicIpAddress,.PrivateIpAddress,.State.Name,(.Tags[] | select(.Key == "Name") | .Value // "")]
- Public IP, Private IP, State, Name タグの値を抽出(なければ空文字列)
[
"54.65.201.60",
"172.31.10.134",
"running",
"web-01"
]
[
"54.65.201.225",
"172.31.10.130",
"running",
"web-02"
]
...
-
join("\t")
- 抽出した情報をハードタブで結合する (TSV)
54.65.201.60 172.31.10.134 running web-01
54.65.201.225 172.31.10.130 running web-02
...
peco
ここまでで得られた情報をよしなに絞り込んで選択する
QUERY>
54.65.201.60 172.31.10.134 running web-01
54.65.201.225 172.31.10.130 running web-02
54.65.205.189 172.31.10.131 running web-03
54.65.230.123 172.31.10.132 running worker-01
54.65.202.2 172.31.10.133 running worker-02
awk
$ awk '{ print $1 }'
タブ区切り文字列のうち、先頭の項目(今回だと Public IP)を抽出
54.65.201.60
54.65.201.225
tr
$ tr '\n' ' '
1行1インスタンスの形で出力されているものをスペース区切りに変換
54.65.201.60 54.65.201.225
tmux-cssh で選んだインスタンスに同時 SSH
tmux-cssh は、tmux を使って複数ホストに同時 SSH するツールです。tmux-cssh 上の入力はすべてのホストに反映されるので、複数ホスト上で同一作業を行いたい場合に有用です(構成管理ツールの話はとりあえず置いといて…)。
tmux-cssh
$ tmux-cssh -i <ssh_key> -u <ssh_user> <host01> <host02>
-i
で秘密鍵を指定、-u
でユーザを指定する。
今回はすべてのホストでキーペアとユーザが同じである場合のみ有効です。
sh -c
いるの?
<host01> <host02>
を単純に変数展開とかで渡すと、この部分は tmux-cssh の複数引数とみなされず、1つの引数として解釈されます。ホスト名としてスペース区切りの文字列1個渡される形です。
$ tmux-cssh -i <ssh_key> -u <ssh_user> '<host01> <host02>'
これだと実際に実行される ssh コマンドがおかしくなります。
$ ssh -i <ssh_key> <ssh_user>@'<host01> <host02>'
なので、tmux-cssh -i -u
の部分も含めて1個の文字列としてみなしたうえで、それ自身を eval する必要があるのです。
これだと
$ ssh -i <ssh_key> <ssh_user>@<host01>
$ ssh -i <ssh_key> <ssh_user>@<host02>
ってなります。
とりあえず思いついたのがこれなので、もっといい方法があるかもしれない。
おわりに
最近仕事で複数(5個以上)インスタンスに同時 SSH して同じコマンドを流し込みたい、ということがあって面倒だったのでこの方法を考えました。
この記事では最初にワンライナーとして紹介してますが、実際自分は envchain を使っていることもありシェル関数として定義したものを利用しています。まあワンライナーが長い上に SSH ユーザと鍵を指定する必要があるのでどのみち関数定義しておいたほうが普段使いによいでしょう。
あわせて御覧ください: dtan4/dot.zsh/.zshrc.peco