tl;dr
デプロイツールによっては 同じサーバに SSH で何回もコマンドを実行することがある(ansible とか)。
コマンドごとに毎回コネクション(トンネル)を確立するとオーバーヘッドも大きくなる。
OpenSSH は $HOME/.ssh/config
に次の設定を書くことで、接続を多重化(multiplex)することができる。
# $HOME/.ssh/config Host machine1 HostName machine1.example.org ControlPath ~/.ssh/controlmasters/%r@%h:%p ControlMaster auto ControlPersist 1h
multiplex 前後の速度差
SSH を使ってリモートサーバで echo コマンドを実行した時の速度差を計測
before multiplex
$ time ssh REMOTE -C echo real 0m0.259s user 0m0.012s sys 0m0.004s
after multiplex
$ time ssh REMOTE -C echo real 0m0.012s user 0m0.000s sys 0m0.004s
多重化接続してみる
多重化接続の流れ
SSH で多重化接続するには
ControlMaster=yes
で多重化のためのコントロールソケットを作成し- 以降の接続では上で作成されたソケットを利用して接続する
多重化接続の設定
必要な設定は以下の3つ
ControlMaster
多重化接続方法ControlPath
多重接続のソケットファイルのパスControlPersist
多重化接続の永続化設定
~/.ssh/ssh_config
で設定することも、接続のたびにコマンドラインで渡すことも可能。
多重化接続する
動きを確認しやすいように、設定ファイルを利用せずに毎回オプションを渡して多重接続してみる。
まずは ControlMaster=yes
にしてコントロールソケットを作成し、 ControlPath
でコントロールソケットのパスを指定する。
$ ssh -o "ControlMaster=yes" -o "ControlPath=~/.ssh/controlmasters/%r@%h:%p" REMOTE
ソケットファイルが作成されることを確認
$ file ~/.ssh/controlmasters/jsmith@REMOTE:22 /home/jsmith/.ssh/controlmasters/jsmith@REMOTE:22: socket
以降の接続では作成したコントロールソケットファイルを ControlPath
で指定し、モード ControlMaster=no
(デフォルト) で接続すると、多重化される。
$ ssh -o "ControlMaster=no" -o "ControlPath=~/.ssh/controlmasters/%r@%h:%p" REMOTE
LISTEN 状態のソケットを確認
$ lsof -i
で LISTEN
状態の ssh ソケットを確認。
通常の設定では SSH 接続するたびに、クライアントとサーバの接続が確立される。
同じリモートサーバに3並列で接続すると
$ lsof -i | grep ssh ssh 21909 jsmith 3r IPv4 577301 0t0 TCP LOCAL:40531->REMOTE:ssh (ESTABLISHED) # REMOTE:ssh (ESTABLISHED) ssh 21912 jsmith 3r IPv4 577324 0t0 TCP LOCAL:40532->REMOTE:ssh (ESTABLISHED) # REMOTE:ssh (ESTABLISHED) ssh 21912 jsmith 3r IPv4 577324 0t0 TCP LOCAL:40532->REMOTE:ssh (ESTABLISHED) ssh 21914 jsmith 3r IPv4 577345 0t0 TCP LOCAL:40533->REMOTE:ssh (ESTABLISHED) # REMOTE:ssh (ESTABLISHED) ssh 21976 jsmith 3u IPv4 577588 0t0 TCP LOCAL:40534->REMOTE:ssh (ESTABLISHED) # REMOTE:ssh (ESTABLISHED) ssh 21976 jsmith 3u IPv4 577588 0t0 TCP LOCAL:40534->REMOTE:ssh (ESTABLISHED) # REMOTE:ssh (ESTABLISHED) ssh 21976 jsmith 3u IPv4 577588 0t0 TCP LOCAL:40534->REMOTE:ssh (ESTABLISHED) # <- multiplexing control socket
リモートサーバで sshd
のログレベルを DEBUG
にすると、同じコネクションに対して接続が増えるたびに channel 番号が 0 から採番されていることが確認できる。
# egrep 'channel [0-9]' /var/log/auth.log Nov 30 17:04:53 REMOTE sshd[20480]: debug1: channel 0: new [server-session] ... Nov 30 17:06:58 REMOTE sshd[20480]: debug1: channel 1: new [server-session] ... Nov 30 17:12:51 REMOTE sshd[20480]: debug1: channel 2: new [server-session] ...
多重化接続の終了
多重接続用に作成下コントロールソケットのとじ方について
永続化させた場合
ControlPersist=yes
の場合、コネクションは張られたままとなる。
コネクションが残り続けてほしくない場合、明示的に切断する必要がある。
graceful disconnect
新規に多重化コネクションは受け付けなくし、コントロールソケットを利用したチャンネルがひとつもなくなったタイミングでコネクションは切断する。
ssh -O stop hostname
とする。
$ ssh -O check REMOTE Master running (pid=16124)
check
コマンドから多重化が ACTIVE 状態とわかる。
stop
コマンドを実行
$ ssh -O stop REMOTE Stop listening request sent. $ ssh -O check REMOTE Control socket connect(/home/jsmith/.ssh/controlmasters/jsmith@REMOTE:22): No such file or directory $ lsof -i | grep ssh ssh 22331 jsmith 3r IPv4 555740 0t0 TCP LOCAL:38772->REMOTE:ssh (ESTABLISHED)
コントロールソケットを利用した最後のチャンネルが切断されると、このソケットも閉じられる。
forceful disconnect
コントロールソケットを利用したアクティブなチャンネルが残っていても、強制的に切断するには $ ssh -O exit hostname
とする。
$ ssh -O check REMOTE Master running (pid=16260)
check
コマンドから多重化が ACTIVE 状態とわかる。
exit
コマンドを実行
$ ssh -O exit REMOTE Exit request sent. $ ssh -O check REMOTE Control socket connect(/home/jsmith/.ssh/controlmasters/jsmith@REMOTE:22): No such file or directory $ lsof -i | grep ssh $
リモートサーバーにログインしていると、強制的にログアウトさせられる
jsmith@REMOTE:~$ Shared connection to REMOTE closed. [jsmith@LOCAL ~]$
タイムアウト設定した場合
ControlPersist=1m
のようにすると、コントロールソケットを利用した接続が1分以上一つも無ければ、自動的に切断される。
$ ssh -C echo REMOTE # 新規にコネクションをはる $ lsof -i | grep ssh ssh 16447 jsmith 3u IPv4 556983 0t0 TCP LOCAL:38776->REMOTE:ssh (ESTABLISHED) $ ssh -O check REMOTE Master running (pid=16447)
1分間放置する
$ ssh -O check REMOTE Control socket connect(/home/jsmith/.ssh/controlmasters/jsmith@REMOTE:22): No such file or directory $ lsof -i | grep ssh $
ローカルサーバの SSH の LogLevel
を DEBUG
にしていると、タイムアウトとともにマスター接続をしたターミナルには次のようなログが出力される。
debug1: ControlPersist timeout expired debug1: channel 0: free: /home/jsmith/.ssh/controlmasters/jsmith@REMOTE:22, nchannels 1 Transferred: sent 2664, received 1888 bytes, in 71.8 seconds Bytes per second: sent 37.1, received 26.3 debug1: Exit status -1 debug1: compress outgoing: raw data 147, compressed 134, factor 0.91 debug1: compress incoming: raw data 101, compressed 86, factor 0.85
多重化の接続数制限
多重化の接続数を制限したい場合、サーバー側(sshd)の MaxSessions
で制御する。
In sshd_config(5), the directive MaxSessions specifies the maximum number of open sessions permitted per network connection. This is used when multiplexing ssh sessions over a single TCP connection. Setting MaxSessions to 1 disables multiplexing and setting it to 0 disables login/shell/subsystem sessions completely. The default is 10. The MaxSessions directive can also be set under a Match conditional block.
http://www.openbsd.org/cgi-bin/man.cgi?query=ssh_config&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html
例えば sshd_config
が MaxSessions 3
の時に 4 接続目を行うと、多重化されず新たに接続が確立される。
$ ssh REMOTE mux_client_request_session: session request failed: Session open refused by peer ControlSocket /home/jsmith/.ssh/connections/jsmith@REMOTE:22 already exists, disabling multiplexing ...
リモートサーバには以下のようなログが出力される
Nov 30 18:56:21 REMOTE sshd[14885]: error: no more sessions
~/.ssh/config 設定
これらをまとめて SSH の設定ファイルは次のようにする良い
# $HOME/.ssh/config Host machine1 HostName machine1.example.org ControlPath ~/.ssh/controlmasters/%r@%h:%p ControlMaster auto ControlPersist 1h
ControlPath
- %r はリモートサーバのユーザー名(%u はローカルサーバのユーザー名)
- %h はリモートサーバのホスト名
- %p はポート番号
ControlMaster
1回目の接続は ControlMaster=yes, 2回目以降の接続は ControlMaster=no で接続する必要がある。
auto とすると、マスター接続がない場合は yes、ある場合は no で接続してくれる。
yes/no 以外にも X 環境でパスワードを入力する ask/autoask モードもある。
ControlPersist
コネクションが残ってもかなわなければ yes で、定期的に掃除したい場合は 1h のように接続判定用のタイムアウト期間を設定する。
References
- 実務家向け OpenSSH の Multiplexing の詳細解説
http://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing - MAN:SSH(1)
http://www.openbsd.org/cgi-bin/man.cgi?query=ssh&sektion=1&arch=&apropos=0&manpath=OpenBSD+Current - MAN:SSH_CONFIG(5)
http://www.openbsd.org/cgi-bin/man.cgi?query=ssh_config&sektion=5 - SSH プロトコルについて
http://www.cisco.com/web/about/ac123/ac147/archived_issues/ipj_12-4/124_ssh.html