第四章:网络与代理
你的服务器也需要上网
SSH 搞定了。手机也能连服务器了。tmux 会话能抗断线了。基础设施正在成型。
但有个问题几乎每个研究者都会在某个时刻踩到:你的服务器不只需要跟你通信,它还需要跟外面的世界通信。它得从 HuggingFace 下载模型权重,得拉数据集,得让 pip install 和 conda install 正常解析包,得把指标上传到 Weights & Biases。
有些实验室的服务器网络畅通无阻。恭喜你 —— 本章后半部分的代理配置可以跳过。但很多服务器并非如此。大学防火墙、企业内网、隔离集群 —— 你的服务器可能根本无法直接访问 huggingface.co 或 pypi.org,理由五花八门。
而且即使你的服务器可以直接上网,还有另一个与防火墙无关的问题:SSH 连接管理。每次你敲 ssh your-server,你的机器都会打开一个全新的 TCP 连接、做完整的密钥交换和认证流程。当你运行 Claude Code 时 —— 它可能每分钟发出几十个 SSH 命令来检查 GPU 状态、同步文件、读日志、启动训练 —— 这就意味着几十个并行连接。慢,给服务器的 SSH 守护进程造成压力,有时候服务器会直接拒绝连接,因为你触发了速率限制。
本章解决这两个问题。首先,我们配置 SSH 多路复用,让所有连接共享一条隧道。然后,对需要的人,我们设置代理,让服务器通过你的本地机器访问互联网。
SSH ControlMaster(所有人都需要)
这一节不是可选的。不管你的服务器能不能直接上网,不管你用不用代理,ControlMaster 都会让每一次 SSH 交互更快更稳定。现在就设置。
问题
没有 ControlMaster 时,每条 SSH 命令都是独立的。当 Claude Code 执行 ssh your-server "nvidia-smi" 时,实际过程是:
- 打开 TCP 连接
- 协商加密
- 认证(密钥交换)
- 执行命令
- 关闭连接
然后 Claude Code 执行 ssh your-server "cat /tmp/training.log"。同样的流程 —— 完整的连接建立,重来一遍。接着 ssh your-server "tmux list-sessions" 又来一遍。rsync 又一遍。每次健康检查又一遍。
每次连接建立需要 1-3 秒。这些延迟累积起来很可观。更糟的是,有些服务器限制并发 SSH 连接数。你会开始看到 ssh_exchange_identification: Connection closed by remote host 错误,然后你的自动化就停摆了。
方案
SSH ControlMaster 让你建立一条主连接(master),之后所有到同一服务器的 SSH 命令都搭载在这条连接上。不需要新的 TCP 握手,不需要重新认证。第二条 ssh 命令几乎是瞬时的。
在你的本地机器上操作:
mkdir -p ~/.ssh/sockets然后把以下内容加到 ~/.ssh/config(或修改已有的 Host * 块):
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist yes
ServerAliveInterval 60
ServerAliveCountMax 3搞定。逐行解释:
ControlMaster auto —— 第一条到某台服务器的 SSH 连接自动成为 master。你不需要做任何额外操作。后续连接会检测到 master 并复用。
ControlPath ~/.ssh/sockets/%r@%h-%p —— 这是 master 连接创建的 Unix socket 文件路径。%r@%h-%p 模式意味着每个用户、主机、端口组合有独立的 socket —— 到不同服务器的连接不会互相干扰。放在 ~/.ssh/sockets/ 目录下保持整洁。
ControlPersist yes —— 这是最重要的一行。当你关闭启动 master 连接的终端时,master 在后台继续存活。没有这个配置,关终端就会杀掉 master,进而中断所有复用该连接的会话。设为 yes 后,master 会一直持续 —— 直到你主动停止或服务器断开连接。
ServerAliveInterval 60 —— 每 60 秒发送一个心跳包。防止防火墙和 NAT 杀死空闲连接。没有这个,安静了几分钟的连接可能会被悄悄掐掉。
ServerAliveCountMax 3 —— 如果连续 3 个心跳包没有收到回应,就判定连接已死并关闭。配合 60 秒的间隔,意味着死连接在 3 分钟内就会被检测到。
实际效果
ControlMaster 之前:
$ time ssh your-server "echo hello"
hello
real 0m1.842s # 一句 "echo hello" 用了将近 2 秒
$ time ssh your-server "echo hello"
hello
real 0m1.756s # 一样 —— 每次都完整建立连接ControlMaster 之后(第二条命令起):
$ time ssh your-server "echo hello"
hello
real 0m0.089s # 近乎即时 —— 复用 master 连接快了二十倍。更重要的是,永远不会被限速。
管理 Master 连接
你很少需要手动管理 master 连接,但需要时:
# 检查 master 连接是否存活
ssh -O check your-server
# 杀掉 master 连接(所有共享会话都会断开)
ssh -O exit your-server
# 启动一个后台 master
ssh -MNf your-server-MNf 三个参数的含义:-M 强制 master 模式,-N 不执行远程命令,-f 进入后台。这会创建一个持久的 master 连接,什么都不做,只是保持隧道活着。
代理配置(按需)
如果你的服务器可以直接访问 HuggingFace、PyPI 等外部服务,请跳过本节。 不确定?执行
ssh your-server 'curl -s https://huggingface.co | head -1',看看能不能拿到 HTML。能的话,直接跳到本章末尾的检查点。
问题
你的服务器在防火墙后面。或者大学网络封锁了某些端口的出站连接。或者你的云厂商 VPC 没配 NAT 网关。不管什么原因,当你 SSH 到服务器尝试下载模型时:
$ pip install transformers
ERROR: Could not find a version that satisfies the requirement transformers
$ huggingface-cli download meta-llama/Llama-3-8B
ConnectionError: HTTPSConnectionPool(host='huggingface.co', port=443): Max retries exceeded服务器就是连不上外网。每一次包安装、每一次模型下载、每一次数据集获取 —— 全部失败。
解决方案:SSH RemoteForward
思路是这样的。你的本地机器能上网 —— 可能是直连,也可能是通过一个跑在 7890 端口的本地代理(Clash、V2Ray 之类的)。SSH 有一个叫 RemoteForward 的功能,它创建一条隧道:把服务器上的一个端口转发到你本地机器上的一个端口。服务器发送到自己本地 10808 端口的所有流量,都会通过 SSH 连接隧道到你本地机器的 7890 端口,然后从那里访问互联网。
在你本地机器的 ~/.ssh/config 中添加:
Host your-server
HostName your.server.ip
User your-username
RemoteForward 10808 127.0.0.1:7890现在当你 SSH 到 your-server 时,服务器上的 10808 端口会自动指向你本地机器的 7890 端口。服务器不需要任何特殊配置 —— 它只需要知道把 127.0.0.1:10808 当作代理来用。
在服务器上设置代理环境变量:
export http_proxy=http://127.0.0.1:10808
export https_proxy=http://127.0.0.1:10808搞定。现在 pip install、huggingface-cli download、curl 以及所有遵循标准代理环境变量的工具,都会通过你本地机器的网络连接来访问互联网。
为什么 ControlMaster 在这里至关重要
RemoteForward 有一个关键限制:同一个端口只能被一条 SSH 连接持有。如果你 SSH 到服务器两次,第二条连接尝试绑定 10808 端口时会失败 —— 因为第一条连接已经占住了。
这正是我们先设置 ControlMaster 的原因。有了 ControlMaster,master 连接持有 RemoteForward 端口,所有后续连接共享 master。没有端口冲突。一条隧道,多个用户。
没有 ControlMaster 的话,你每次额外连接都会看到:
Warning: remote port forwarding failed for listen port 10808而且你永远不确定到底哪条连接持有可用的隧道。
在哪里设置代理变量
任何需要访问互联网的 shell 会话都需要代理环境变量。但不要把它们加到 .bashrc 里。原因是:如果你的服务器有时能直连(比如后来加了代理,或者防火墙规则改了),.bashrc 里的代理变量意味着每个会话都走隧道 —— 即使隧道没必要或者隧道根本没启动。
取而代之,在需要时显式设置:
- 在训练用的 tmux 会话里:在启动命令开头加上
export http_proxy=...和export https_proxy=... - 一次性命令:加前缀:
http_proxy=http://127.0.0.1:10808 pip install transformers
这样做保持了显式和清晰,避免了三个月后那种"为什么我的连接这么慢"的迷之 debug —— 到那时你早忘了代理的事。
故障排查
如果 curl https://huggingface.co 在服务器上突然不工作了(之前是好的),按这个清单逐步排查:
1. Master SSH 连接还活着吗?
# 在你的本地机器上执行
ssh -O check your-server如果显示 "No ControlPath" 或 "No such file",说明 master 挂了。重新连接:
ssh -MNf your-server2. 你的本地代理在跑吗?
如果你的本地机器通过 7890 端口的代理上网,确保那个代理确实在运行。SSH 隧道转发到 7890 —— 如果那里没有东西在监听,隧道连接了但到不了互联网。
3. 端口确实转发了吗?
SSH 到服务器然后检查:
curl -x http://127.0.0.1:10808 -s https://huggingface.co | head -1如果返回 HTML,隧道正常。如果挂起或报错,回到第 1 步。
4. 其他 SSH 连接抢占了端口?
如果你禁用了 ControlMaster 或从另一台机器连接,另一条连接可能尝试绑定 10808 端口但悄悄失败了。杀掉所有连接,重新建立干净的 master:
# 在你的本地机器上
ssh -O exit your-server 2>/dev/null
ssh -MNf your-server检查点
测试你的服务器能否访问互联网。
如果你的服务器能直接上网(不需要代理):
ssh your-server 'curl -s https://huggingface.co | head -1'如果你配置了代理:
ssh your-server 'curl -x http://127.0.0.1:10808 -s https://huggingface.co | head -1'你应该看到 HTML 输出 —— 类似 <!DOCTYPE html> 开头的内容。如果看到了,说明你的服务器能访问外部网络。模型可以下载了。包可以安装了。你已经准备好配置 Claude Code 了。
同时验证 ControlMaster 正在工作:
# 打开一条连接
ssh your-server "echo 'first connection'"
# 检查 master socket 是否存在
ssh -O check your-server你应该看到 Master running 加一个 PID。以后到这台服务器的每条 SSH 命令都将近乎即时。