Pages

Wednesday, 30 March 2016

重用SSH连接


平时需要经常用到 SSH,比如登录远程服务器,用 Git 推送和更新代码等。建立一次 SSH 连接可能并不需要多久长时间,但是如果要频繁登录同一台服务器,就未免显得有些繁琐和浪费时间。如果是用用户名和密码登录,每次都要输入密码就更加让人崩溃。还有使用 Git 的时候,短时间内可能需要经常 git pull 和 git push,如果每次操作都需要重新建立连接,等待过程就让人心生厌恶了。
实际上,SSH 有个「鲜为人知」的特性可以做到重用连接,只有在第一次登录的时候会创建新的连接,后续的会话都可以重用这个已经存在的连接。这样,后续的登录就会非常快,而且不需要输入密码认证。配置也很简单,直接上代码。
修改 ~/.ssh/config 文件,添加如下配置:
Host *
    ControlMaster auto
    ControlPath /tmp/ssh_mux_%h_%p_%r
    ControlPersist 600
意思也很好理解:
Host * 这一行表示下面这些配置和规则影响到的 host,* 表示所有的远程 host 都生效。如果要指定某个(些)特定的 host,可以使用类似 Host *.example.com 的配置。
ControlMaster auto 这个选项告诉 SSH 客户端尝试重用现有的连接(master connection)。
ControlPath 指定了这个连接的 socket 保存的路径,这里配置的是在 /tmp 目录,实际上可以在任何有读写权限的路径下。/tmp/ssh_mux_%h_%p_%r 配置了 socket 文件名,%h 表示远程主机名(host),%p表示远程 SSH 服务器的端口(port),%r 表示登录的远程用户名(remote user name)。这些 socket 可以随时删掉(rm),删除后首次会话又会创建新的 master 连接。曾经遇到过这种情况,本地断网了,打开的几个远程终端都卡死,网络恢复后也一直这样,甚至打开新的终端也登录不上。这个时候只需要把之前的 socket 文件都删掉,重新登录就可以了。
ControlPersist 这个选项比较重要,表示在创建首个连接(master connection)的会话退出后,master 连接仍然在后台保留,以便其他复用该连接的会话不会出现问题。这个特性在使用 Git 的时候就非常有用,在频繁提交和拉代码的时候,每次 SSH 会话都是很短暂的,如果 master 连接能保持在后台,后续的操作就会如丝般顺滑。
只需要添加上面几行配置,SSH 的体验就瞬间上升了好几个档次,简直是懒人必备.
------------------------------------------------------------

ssh 会话复用及用户级的sleep.target


这里看到 ssh 的 Control master 特性之后,就在~/.ssh/config里启用了这个特性:
1
2
3
4
ControlPath ~/.ssh/master-%r@%h:%p
ControlMaster auto
ControlPersist yes
Compression yes
会话连接复用,对于以交互操作的使用,很不错的!对低延迟的服务器可能只是少了用户认证过程,但对于连接国外服务器,少了 TCP 握手、SSH 握手与认证等来来回回的过程,连接会快非常多的,尤其是对于常用的服务器,比如 GitHub 之类的,提速非常明显。
然后,问题就来了:系统挂起再恢复之后,大部分连接会 stalled,需要手工断开连接。即使配置了超时,它也不一定及时。于是我想,既然 netctl-auto 之类的服务能够在挂起系统时适当处理,那么我是不是也能写一个用户级的 systemd 服务来处理这件事情呢?
于是我按系统级的配置方法弄好了,结果什么也没有发生……后来才明白,只有系统级的 sleep.target,没有用户级的啊。
在 Arch Linux 官方论坛看到有人这么尝试,让系统级的 systemd 调用用户级的 systemd。配置有点不对,但是想法是非常好的!
于是就有了我现在用的方案:
user-sleep@.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=sleep.target of a systemd user session
Before=sleep.target
StopWhenUnneeded=yes
[Service]
Type=oneshot
User=%I
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%I/bus
RemainAfterExit=yes
ExecStart=/usr/bin/systemctl --user start sleep.target
ExecStop=/usr/bin/systemctl --user stop sleep.target
[Install]
WantedBy=sleep.target
启用(enable)user-sleep@1000.service之后,系统挂起时,就会调用 ID 为 1000 的用户的用户级 systemd,也 reach sleep.target 啦。当然这个用户级的 sleep.target 也得自己写:
sleep.target
1
2
3
4
5
[Unit]
Description=Sleep
Documentation=man:systemd.special(7)
DefaultDependencies=no
StopWhenUnneeded=yes
然后就可以让用户级的服务WantedBy=sleep.target啦.
from http://lilydjwg.is-programmer.com/2016/3/8/ssh-session-reuse-and-user-level-sleep-target-for-systemd.196900.html 

相关帖子:http://briteming.blogspot.com/2014/03/ssh-config-file.html