ssh多路传输multiplexing加速

Multiplexing的优点

SSH multiplexing 的一个优点是克服了创建一个新的TCP连接的消耗。对于一个主机可以接受的连接数量是有限的,并且对于一些主机限制更为明显,这和主机的负载有关,并且开启一个新的连接会有明显的延迟。当使用multiplexing的时候,重用一个已经开启的连接可以明显加速。

multiplexing和独立会话的区别是,在服务器和客户端,都只看到一个进程(即使多次连接会话)。并且,在服务器和客户机上,可以看到只打开了一个TCP连接;只不过,在客户端,后建立的会话都是通过 stream 的本地套接字来访问的。OpenSSH使用现有的TCP连接来实现多个SSH会话,这种方式降低了新建TCP连接的负载。

Multiplexing 的创建过程:

  • 设置 ControlMaster 开启一个本地Unix domain socket

  • 其余的所有的ssh命令连接都通过Unix domain socket连接到 ControlMaster

ControlMaster 提供了以下优点:

  • 使用已经存在的unix socket

  • 没有新增的TCP/IP连接

  • 不再需要密钥交换

  • 不再需要认证等

以下是在一个Mac OS X 客户端,登陆过Linux服务器之后,关闭终端,然后又发起4次ssh登陆(没有退出)情况下的检查:

  • Mac OS X客户端检查进程:

    ps aux | grep 192.168.1.1 | grep -v grep
    

可以看到,系统只建立了一个ssh连接进程(有一个 [mux] 标记是 Multiplexing

huatai          59773   0.0  0.0  2490964    600   ??  Ss    3:03PM   0:00.17 ssh: /Users/huatai/.ssh/192.168.1.1-22-root [mux]
  • Mac OS X客户端检查连接:

    netstat -an | grep 192.168.1.1
    

可以看到,除了第一个ssh连接是直接连接服务器的22端口,其他ssh连接都是连接本地的socket:

tcp4       0      0  192.168.1.2.51210      192.168.1.1.22        ESTABLISHED
5a240da08f6284a9 stream      0      0                0 5a240da069f57959                0                0 /Users/huatai/.ssh/192.168.1.1-22-root.FqablQb5EvN7v2Yj
5a240da083696e09 stream      0      0                0 5a240da07cccff31                0                0 /Users/huatai/.ssh/192.168.1.1-22-root.FqablQb5EvN7v2Yj
5a240da08fe2c701 stream      0      0                0 5a240da05ffa3f91                0                0 /Users/huatai/.ssh/192.168.1.1-22-root.FqablQb5EvN7v2Yj
5a240da08a80ec79 stream      0      0                0 5a240da069f56441                0                0 /Users/huatai/.ssh/192.168.1.1-22-root.FqablQb5EvN7v2Yj
5a240da069f57251 stream      0      0 5a240da06f2ca8c9                0                0                0 /Users/huatai/.ssh/192.168.1.1-22-root.FqablQb5EvN7v2Yj
  • 在Linux服务器上观察可以看到进程:

    ps aux | grep ssh | grep -v grep
    

显示只有一个客户端连接:

root      1052  0.0  0.0  82548  3536 ?        Ss   Jan27   0:05 /usr/sbin/sshd -D
root     34640  0.0  0.0 140788  4988 ?        Ss   15:03   0:00 sshd: root@pts/0,pts/1,pts/2,pts/3

观察客户端ssh连接也确实只有一个:

netstat -an | grep 192.168.1.2

输出显示:

tcp        0     36 192.168.1.1:22         192.168.1.2:51210       ESTABLISHED

从上述观察来看,启用了 Multiplexing 之后,客户机和服务器只需要建立一个SSH连接,就能够满足客户端大量的并发连接

设置Multiplexing

OpenSSH通过 ControlMasterControlPathControlPersist 配置来实现multiplexing:

  • ControlMaster 决定ssh是否监听控制连接并且如何处理

  • ControlPath 是控制套接字的位置

  • ControlPersist 是配合 ControlMaster 设置,如果设置成 yes 则会在后台始终开启主连接来接受新的连接,只到被明确地杀死或者通过 -O 参数关闭。

备注

ControlPersist 选项需要 OpenSSH 5.6以上版本支持,否则会ssh时会提示错误:

Bad configuration option: ControlPersist
  • 在用户目录下配置 ~/.ssh/config 内容如下:

    Host machine1
      HostName machine1.example.org
      ControlPath ~/.ssh/controlmasters/%r@%h:%p
      ControlMaster auto
      ControlPersist 10m
    

上述配置,就会在 ~/.ssh/controlmasters/ 目录下创建控制套接字

  • Host machine1 也如果配置成 Host * 则表示匹配所有的主机,则不需要设置 HostName

  • ControlPath ~/.ssh/controlmasters/%r@%h:%p 设置用于共享连接的控制unix套接字路径。变量 %r%h%p 表示远程ssh连接的用户名,主机名和端口。

  • ControlMaster 设置成 auto 则会无条件接受新的连接,如果这个参数设置成 10 就只能接受10个多路会话。

  • ControlPersist 10m 设置主连接保持在后台打开的时间是10分钟。如果没有客户端连接,这个后台master连接将自动终止。

备注

从OpenSSH 6.7开始 %r@%h:%p 可以合并成 %C ,这个参数会自动生成 %l%h%p%r 的哈希,优点是可以唯一标识连接

也可以配置成通用配置(对所有服务器生效):

配置所有主机登陆激活ssh multiplexing,压缩以及不检查服务器SSH key(注意风险控制)
Host *
  ServerAliveInterval 60
  ControlMaster auto
  ControlPath ~/.ssh/%h-%p-%r
  ControlPersist yes
  StrictHostKeyChecking no
  Compression yes

备注

这里还添加了压缩以及不检查服务器SSH key(有安全风险,但是对于有安全控制对测试环境,不断重建服务器并使用相同IP,可能需要这个参数)

管理连接套接字

  • 选项 -O 用来管理连接:

    ssh -O check machine1
    ssh -O stop machine1
    

备注

这个 -O stop 会清除掉旧的控制套接字,原先的已经连接的会话不受影响。新的连接将建立新的TCP连接。

手工建立multiplex连接

使用参数 -M-S 对应的就是 ControlMasterControlPath ,所以可以使用如下命令创建多路传输:

后续的ssh可以使用 -S 参数复用前面创建的控制套接字:

ssh -S /home/fred/.ssh/controlmasters/fred@server.example.org:22 server.example.org

结束multiplex连接

ssh -O stop server1
ssh -O stop -S ~/.ssh/controlmasters/fred@server1.example.org:22 server1.example.org

mux_client_request_session: read from master failed: Broken pipe

有时候执行SSH的时候会遇到长时间无响应,最后出现报错:

mux_client_request_session: read from master failed: Broken pipe

这是因为在 ~/.ssh/config 中配置了 ControlPersist yes :

  • ControlPersist yes 结合 ControlMaster 一起使用的时候,指定主连接应在初始客户端连接关闭后在后台保持打开状态(等待将来的客户端连接)。

  • 如果设置为 no (默认值),则主连接不会置于后台,并会在初始客户端连接关闭后立即关闭。如果设置为 yes0 ,则主连接将无限期地保留在后台(直到通过诸如 ssh -O exit 之类的机制被杀死或关闭)。

  • 如果设置为以秒为单位的时间,或 sshd_config(5) 中记录的任何格式的时间,则后台主连接将在保持空闲(没有客户端连接)指定时间后自动终止。

这个设置带来一个问题,就是在网络不稳定情况下(如wifi或网络连接出现问题,或者ISP问题导致互联网断开超过15分钟),此时持久连接会由于网络问题而终端。在这种网络问题出现后,如果再次运行 ssh 访问服务器,会尝试使用使用之前的socket连接,并在超时后打开一个新的连接。此时就会收到 mux_client_request_session: read from master failed: Broken pipe 报错。

没有什么好的解决方法,要么移除 ControlPersist yes ,要么忽略网络错误(不在可控范围内)。

使用 ssh -v 或者 ssh -vv 可以看到详细的ssh连接排查信息,可以帮助发现上述问题

参考