Skip to content

第四章:网络与代理


你的服务器也需要上网

SSH 搞定了。手机也能连服务器了。tmux 会话能抗断线了。基础设施正在成型。

但有个问题几乎每个研究者都会在某个时刻踩到:你的服务器不只需要跟通信,它还需要跟外面的世界通信。它得从 HuggingFace 下载模型权重,得拉数据集,得让 pip installconda install 正常解析包,得把指标上传到 Weights & Biases。

有些实验室的服务器网络畅通无阻。恭喜你 —— 本章后半部分的代理配置可以跳过。但很多服务器并非如此。大学防火墙、企业内网、隔离集群 —— 你的服务器可能根本无法直接访问 huggingface.copypi.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" 时,实际过程是:

  1. 打开 TCP 连接
  2. 协商加密
  3. 认证(密钥交换)
  4. 执行命令
  5. 关闭连接

然后 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 命令几乎是瞬时的。

在你的本地机器上操作:

bash
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 之前:

bash
$ time ssh your-server "echo hello"
hello
real    0m1.842s       # 一句 "echo hello" 用了将近 2 秒

$ time ssh your-server "echo hello"
hello
real    0m1.756s       # 一样 —— 每次都完整建立连接

ControlMaster 之后(第二条命令起):

bash
$ time ssh your-server "echo hello"
hello
real    0m0.089s       # 近乎即时 —— 复用 master 连接

快了二十倍。更重要的是,永远不会被限速。

管理 Master 连接

你很少需要手动管理 master 连接,但需要时:

bash
# 检查 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 到服务器尝试下载模型时:

bash
$ 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 当作代理来用。

在服务器上设置代理环境变量:

bash
export http_proxy=http://127.0.0.1:10808
export https_proxy=http://127.0.0.1:10808

搞定。现在 pip installhuggingface-cli downloadcurl 以及所有遵循标准代理环境变量的工具,都会通过你本地机器的网络连接来访问互联网。

为什么 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 连接还活着吗?

bash
# 在你的本地机器上执行
ssh -O check your-server

如果显示 "No ControlPath" 或 "No such file",说明 master 挂了。重新连接:

bash
ssh -MNf your-server

2. 你的本地代理在跑吗?

如果你的本地机器通过 7890 端口的代理上网,确保那个代理确实在运行。SSH 隧道转发到 7890 —— 如果那里没有东西在监听,隧道连接了但到不了互联网。

3. 端口确实转发了吗?

SSH 到服务器然后检查:

bash
curl -x http://127.0.0.1:10808 -s https://huggingface.co | head -1

如果返回 HTML,隧道正常。如果挂起或报错,回到第 1 步。

4. 其他 SSH 连接抢占了端口?

如果你禁用了 ControlMaster 或从另一台机器连接,另一条连接可能尝试绑定 10808 端口但悄悄失败了。杀掉所有连接,重新建立干净的 master:

bash
# 在你的本地机器上
ssh -O exit your-server 2>/dev/null
ssh -MNf your-server

检查点

测试你的服务器能否访问互联网。

如果你的服务器能直接上网(不需要代理):

bash
ssh your-server 'curl -s https://huggingface.co | head -1'

如果你配置了代理

bash
ssh your-server 'curl -x http://127.0.0.1:10808 -s https://huggingface.co | head -1'

你应该看到 HTML 输出 —— 类似 <!DOCTYPE html> 开头的内容。如果看到了,说明你的服务器能访问外部网络。模型可以下载了。包可以安装了。你已经准备好配置 Claude Code 了。

同时验证 ControlMaster 正在工作:

bash
# 打开一条连接
ssh your-server "echo 'first connection'"

# 检查 master socket 是否存在
ssh -O check your-server

你应该看到 Master running 加一个 PID。以后到这台服务器的每条 SSH 命令都将近乎即时。

Released under the MIT License.