场景:目标服务器在学校内网,人在校外访问。原有方案 frp + 公网 VPS 中转,本文记录迁移到 Tailscale 的完整过程:怎么装、怎么判断打洞成败、打洞失败后如何用 Peer Relay 兜底,以及最后拿到直连的全过程。所有数据均为实测。
一、frp 的结构性问题
frp 的原理是把内网端口逐条映射到公网 VPS 上:
Mac ──→ VPS(frps) ──frp隧道──→ 校内服务器(frpc)
这个架构有两个绕不开的代价:
- 所有流量过 VPS。带宽上限 = VPS 带宽,传文件、跑 Jupyter 全被卡死;VPS 流量费也跟着涨。
- 每个服务一条规则 。想 SSH 加一条,想 Web 再加一条,端口开多了维护成本直线上升。

frp 适合的场景是"临时对外暴露一两个端口"。要的是组网------设备之间任意互访------就该换思路。
二、方案选型
| 方案 | 原理 | 需要公网IP | 速度 | 维护成本 |
|---|---|---|---|---|
| frp | 端口转发 | 需要 VPS | 受限于 VPS | 每服务一条规则 |
| WireGuard 自建 | 加密隧道 | 需要 VPS | 好 | 配置文件手写 |
| Tailscale | WireGuard + 自动打洞 | 不需要 | 直连=裸链路 | 几乎为零 |
| ZeroTier | P2P 虚拟局域网 | 不需要 | 同上 | 略高于 TS |
选 Tailscale:底层就是 WireGuard,控制面托管,装上登录即用。它的核心逻辑是:能 P2P 直连就直连(流量不过任何第三方),打洞失败才回落到官方 DERP 中继。
关键认知:DERP 节点大多在境外(离我最近的是香港),中继模式下速度和延迟都可能不如国内 VPS 中转。所以用 Tailscale 的成败,完全取决于能不能打通直连。下文的重心就在这。
三、安装与登录
校内服务器(Ubuntu)
bash
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale up 会打印一个 https://login.tailscale.com/a/xxxx 授权链接,复制到本地浏览器打开,用 GitHub/Google 账号登录。登录后拿到虚拟 IP:
bash
$ tailscale ip -4
100.80.171.73
确认开机自启(官方脚本默认已注册 systemd 服务):
bash
systemctl is-enabled tailscaled # 期望 enabled
顺手到管理后台(login.tailscale.com → Machines)把这台服务器 Disable key expiry。否则约 180 天后节点密钥过期,无头服务器会掉线且需要重新浏览器认证,很烦。
Mac
App Store 装 Tailscale,或 brew install --cask tailscale,菜单栏登录同一账号。
注意 Mac 版命令行路径特殊(后面反复用到,建议加 alias):
bash
echo "alias tsping='/Applications/Tailscale.app/Contents/MacOS/Tailscale ping'" >> ~/.zshrc && source ~/.zshrc
四、核心技能:怎么判断 Tailscale 能不能成
这是本文最重要的部分。很多人装完发现慢就弃坑,其实慢的原因 99% 是在走 DERP 中继,而这是可以提前诊断的。

1. netcheck:网络体检
两端各跑一次:
bash
tailscale netcheck
只看三个字段:
* UDP: true ← false = 出站UDP被封,直接判死
* MappingVariesByDestIP: false ← true = 对称NAT,打洞基本没戏
* Nearest DERP: Hong Kong ← 中继兜底位置,顺带看延迟
MappingVariesByDestIP 是重点:false 表示 NAT 对不同目标使用相同的外部映射(cone 型),对端可以预测你的端口洞在哪,打洞希望大。
2. tailscale ping:看流量实际走哪条路
普通 ping 走 ICMP,看不出路径。必须用 tailscale ping:
bash
tsping 100.80.171.73
两种输出,天壤之别:
pong from ... via DERP(hkg) in 424ms ← 走香港中继,慢
pong from ... via 223.100.xxx.xxx:41641 in 29ms ← 直连,快
判读规则:
- 前 1~2 发走 DERP 是正常的 ------那是协商阶段,打洞完成后会升级成
via <公网IP>:<端口>; - 连续多发全是
via DERP(xxx)且末尾出现direct connection not established,才是真的打洞失败; tailscale ping --c 20 <IP>多打几发再下结论,有些 NAT 组合要几十秒才完成协商。
五、实战记录:打洞失败与排查
装好后的第一次测试就不顺利。校内端 netcheck 很健康:
* UDP: true
* IPv4: yes, 223.100.xxx.xxx:xxxxx ← 学校出口NAT
* MappingVariesByDestIP: false ← cone型,理想情况
Mac 端同样健康(北京家宽,出口 222.130.x.x):
* UDP: true
* MappingVariesByDestIP: false
* Nearest DERP: Hong Kong (124.2ms)
两端都是教科书级的"好 NAT",理论上必通。实际结果:
$ tsping --c 20 100.80.171.73
pong from titan (100.80.171.73) via DERP(hkg) in 414ms
pong from titan (100.80.171.73) via DERP(hkg) in 413ms
...(20发全部如此)
2026/08/24 03:10:08 direct connection not established
20 发全走香港中继,延迟稳定在 410ms 左右,SSH 卡得没法用。

此时体感对比:frp 反而更快(我的 VPS 在国内,链路是两段短程;DERP 绕香港还叠加共享限速)。
诊断结论:netcheck 只测出站映射的规律性,测不出"入站包能否到达你"。家宽运营商 CGNAT 会过滤入站 UDP,校园网防火墙也可能拦入站方向------两边都拦,STUN 打洞的双向探测就永远碰不上。纸面参数全好但打洞失败,基本就是这个原因。
六、解法:Peer Relay,把中继搬回国内
打洞失败不等于放弃。Tailscale 从 1.86 开始支持 Peer Relay:把自己 tailnet 里的某台机器指定为中继节点,打洞失败时流量优先走它,而不是境外 DERP。
我的 frp VPS(华为云北京)正好是现成的中继位。配完后链路变成:
Mac ──→ 国内VPS(peer relay) ──→ 校内服务器 ← 与frp同路径,但全端口通用
Mac ══════════════════════════→ 校内服务器 ← 哪天能直连则自动升级
优先级自动排序:直连 > peer relay > DERP,无需手动切换。
配置步骤(VPS 上三条命令)
bash
# 1. 安装并登录同一账号
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# 2. 指定为 relay 节点,监听 UDP 40000
sudo tailscale set --relay-server-port=40000
# 3. 云厂商安全组放行 UDP 40000 入站(控制台操作)
ACL 授权
管理后台 → Access controls,JSON 里加一条 grant(dst 必须填完整的 MagicDNS 域名或 Tailscale IP,裸短主机名会报 invalid address):
json
{
"grants": [
{
"src": ["autogroup:member"],
"dst": ["100.127.83.114"],
"app": { "tailscale.com/cap/relay": [] }
}
]
}
src: ["autogroup:member"]= 我名下所有设备都可使用 relay。官方不建议写"*"(会把 tailnet 里所有设备包括未来新加入的都压到这台 VPS 上);dst填 VPS 的 Tailscale IP(VPS 上tailscale whoami查看);- 保存即生效,客户端几十秒内拉取新策略,不用重启。
验证
bash
$ tsping 100.80.171.73
pong from ... via peer-relay(100.127.83.114:40000:vni:619) in ~xx ms ← 生效
也可以 tailscale status | grep peer-relay 检查连接类型。
七、结果:意外之喜,直连成功
ACL 保存后再测,惊喜来了:
$ tsping 100.80.171.73
pong from school-244 (100.80.171.73) via 223.100.xxx.xxx:41641 in 29ms
直接打到学校出口的公网端口------直连,29ms。 比 DERP 的 410ms 快了 14 倍。

推测是 VPS 加入 tailnet 后触发两端重新交换端点信息,凑齐了此前缺失的打洞条件(也可能是时间差让某端 NAT 映射刚好可用)。不管具体机制如何,结论是:别在第一次打洞失败时就弃坑,把基础设施(relay 节点、ACL)铺好,多给几次协商机会。
最终形态:
| 指标 | frp | Tailscale (DERP) | Tailscale (直连) |
|---|---|---|---|
| SSH RTT | 两跳国内 | ~410ms | 29ms |
| 带宽上限 | VPS带宽 | 共享限速 | 两端裸带宽 |
| 新增服务 | 加规则 | 无需配置 | 无需配置 |
iperf3 实测方法(服务器起 iperf3 -s,Mac 上):
bash
iperf3 -c 100.80.171.73 -t 10 -P 4 # 上行
iperf3 -c 100.80.171.73 -t 10 -P 4 -R # 下行
测速时务必另开终端跑 tsping 确认当前是 via IP:port 还是 via DERP------前者才是有效数据。
八、避坑清单
- Mac 没有
tailscale裸命令 ,CLI 在/Applications/Tailscale.app/Contents/MacOS/Tailscale,先加 alias。 tailscale ping≠ping。判断路径只能用前者。- 服务器记得 Disable key expiry,否则 180 天后掉线。
- Peer Relay 要求三端 Tailscale ≥ 1.86 ,老版本先
sudo tailscale update。 - ACL 里
dst不认短主机名 ,报invalid address就换成完整 MagicDNS 名或 100.x IP。 - netcheck 参数好≠打洞必成,CGNAT 过滤入站是 netcheck 看不见的。失败时别急着换工具。
/var/lib/tailscale/别删 ,登录凭据都在tailscaled.state里,删了要重新认证。- iOS/Android 不能当 relay 节点,但可以作为使用者。
九、总结
- frp 解决的是"暴露端口",异地组网该用的是"虚拟局域网",两者不是竞品而是不同抽象层。
- Tailscale 的体验取决于打洞是否成功,
netcheck+tailscale ping两个命令即可完成全链路诊断。 - 打洞失败时的正确姿势不是退回 frp,而是自建中继:Peer Relay 把境外 DERP 换成自己的国内 VPS,速度下限追平 frp,上限是直连裸带宽。
- 我的案例最终以 29ms 直连收尾,frp 退役。
环境备注:Ubuntu 22.04(校内 Dell 机架服务器)/ macOS + Tailscale App / 华为云北京轻量服务器作 relay / 三端版本 1.86+。