七、附录:进阶选项 & 排错指南
主流程到这里就结束了。下面这些是给"想搞清楚为什么"或"出了奇怪问题"的玩家准备的,按需查阅。
主流程里那条:
sudo ethtool -K "$NETDEV" rx-udp-gro-forwarding on rx-gro-list off
不做的话,tailscale up 每次启动会印一行 GRO warning。这是真实可观测的性能损失:Tailscale 自家 benchmark 显示打开 UDP GRO forwarding 后,转发吞吐量从 1.3 Gb/s 提升到 10.7 Gb/s(接近 10 倍)。t4g.small/e2-micro 当然达不到这种数字,但能"打满"实例本身的网络上限。
networkd-dispatcher 那段脚本是为了让重启后自动恢复(hook 在网卡 routable 时自动触发,Ubuntu 24.04 默认装了 networkd-dispatcher)。
注意 rx-gro-list 必须设为 off——它会和 GRO forwarding 冲突,开错了反而拖慢。
7.2 进阶:用 tag + autoApprovers 替代手动批准
每次重装 VPS 都要去 admin console 手动点"Use as exit node"和"Disable key expiry"很烦。Tailscale 的标准做法是用 tag + autoApprovers:
admin console → Access controls 里加:
{
"tagOwners": {
"tag:exit-sg": ["autogroup:admin"]
},
"autoApprovers": {
"exitNode": ["tag:exit-sg"]
},
"acls": [
{ "action": "accept", "src": ["autogroup:member"], "dst": ["*:*"] }
]
}
然后 admin console 生成一个带 tag:exit-sg 的 auth key,VPS 上:
sudo tailscale up --advertise-exit-node \
--advertise-tags=tag:exit-sg \
--auth-key=tskey-auth-xxxxx
这样 exit node 自动批准、key 默认不过期。如果将来想限制只有部分设备能用这个 exit node,改 ACL 即可。
7.3 tailscale ping 切换 peer 时看到 DERP
正常,不影响实际使用。详见三 BIS 末尾的解释。简单说:Tailscale 会把不活跃的 peer 降级,tailscale ping 一个最近没用过的 peer 会触发重新激活,前几个包走 DERP 是预期的。但你实际通过 exit node 跑 API 时,每次请求都让该 peer 保持活跃,根本不会经历这个过程。
7.4 排错命令速查
systemctl status tailscaled --no-pager
journalctl -u tailscaled -e --since '1 hour ago'
sudo tailscale status
sudo tailscale netcheck
sudo tailscale ping <peer>
实践上 tailscaled 非常稳,更常见的问题是实例本身被 stop/start 导致 IP 变了——这就是为什么强烈推荐绑 EIP。
7.5 想自己测一下哪个 exit node 更快?
下面这个脚本对每个目标 URL 跑 10 次 TTFB(首字节时间),对带宽跑 3 次 10MB 下载,然后给 min / median / max。多次取 median 才能滤掉单次 DNS 抖动、TLS 握手抖动这种噪声:
#!/bin/bash
NODE=${1:?"Usage: $0 <exit-node-hostname>"}
TTFB_RUNS=10
BW_RUNS=3
echo "=== Switching to $NODE ==="
sudo tailscale up --exit-node="$NODE" --exit-node-allow-lan-access=true
sleep 2
echo
echo "=== Outbound IP ==="
curl -s --max-time 10 ifconfig.me
echo
echo
echo "=== TTFB ($TTFB_RUNS runs each, lower is better) ==="
printf "%-35s %8s %8s %8s\n" "target" "min(ms)" "median" "max"
for url in \
https://api.anthropic.com/ \
https://api.openai.com/ \
https://github.com/ \
https://www.google.com/ \
; do
samples=$(for i in $(seq 1 $TTFB_RUNS); do
curl -w "%{time_starttransfer}\n" -o /dev/null -s --max-time 10 "$url" 2>/dev/null
done | sort -n)
min=$(echo "$samples" | head -1)
max=$(echo "$samples" | tail -1)
median=$(echo "$samples" | awk -v n=$TTFB_RUNS 'NR==int((n+1)/2)')
printf "%-35s %8.0f %8.0f %8.0f\n" "$url" \
"$(echo "$min*1000" | bc -l)" \
"$(echo "$median*1000" | bc -l)" \
"$(echo "$max*1000" | bc -l)"
done
echo
echo "=== Throughput ($BW_RUNS runs of Cloudflare 10MB) ==="
bw_samples=$(for i in $(seq 1 $BW_RUNS); do
curl -o /dev/null --max-time 30 -s -w "%{speed_download}\n" \
"https://speed.cloudflare.com/__down?bytes=10000000"
done | sort -n)
echo "$bw_samples" | awk '{ printf " run: %.1f Mbps\n", $1*8/1000000 }'
median_bw=$(echo "$bw_samples" | awk -v n=$BW_RUNS 'NR==int((n+1)/2)')
echo " ---"
echo "$median_bw" | awk '{ printf " median: %.1f Mbps\n", $1*8/1000000 }'
echo
echo "=== Tailscale path (should be 'via <ip>:41641', not DERP) ==="
tailscale ping "$NODE" | head -3
存成 bench-exit-node.sh,chmod +x,然后:
./bench-exit-node.sh aws-tk-arm
./bench-exit-node.sh aws-sg-arm
| 指标 |
AWS Tokyo |
AWS Singapore |
GCP Singapore |
| Tailscale ping (直连) |
64ms |
82ms |
111ms |
| Anthropic median TTFB |
333ms |
328ms |
381ms |
| OpenAI median TTFB |
279ms |
293ms |
312ms |
| GitHub median TTFB |
295ms |
332ms |
278ms |
| Google median TTFB |
326ms |
347ms |
340ms |
| Cloudflare 10MB median |
47 Mbps |
33 Mbps |
33 Mbps |
| TTFB max (噪声示例) |
1287ms |
1405ms |
1489ms |
怎么读这张表:
- Tailscale ping 是最稳定的信号——它就是你的 Mac 到 VPS 的物理 RTT,跟目标 API 无关。Tokyo 在这一项上稳定领先 18-47ms,物理距离决定的、不会反超。
- 下载带宽也很稳定:Tokyo 47 Mbps 量级,比两个 Singapore 节点(33 Mbps)多 40%。物理距离 + 该路径的拥塞情况决定,多轮测都差不多。
- TTFB median 比单次更可信,但区域差异已经不大:到 Anthropic 三家几乎打平(330ms 量级),Tokyo 在 OpenAI 和 Google 上微胜,GCP Singapore 在 GitHub 上反而最快——说明各家 CDN/边缘节点的部署策略不同,没有一个 region 是全场最优。
- 看一下 max 列就知道为什么必须取 median:单次最坏情况能飙到 1.4 秒,是 median 的 4 倍。如果只跑 1 次就下结论,结论会被随机抖动主导。
结论:日常 Claude Code / ChatGPT 用 Tokyo——基线延迟(ping)和带宽这两个物理决定的、稳定的信号上 Tokyo 都明显胜。TTFB median 各家差距其实不大(30-50ms 量级),不是关键决策因素。如果你的工作负载有大流量传输(model checkpoint、视频会议),Tokyo 的 47 Mbps vs Singapore 的 33 Mbps 这个差距会更明显。
不建议用国内"测速网站"测 exit node:那些站测的是"AWS → 国内服务器"的特定路径(比如教育网 CERNET 出口),跟你"本地 → AWS → Anthropic 美国"的实际链路完全无关。我用 USTC 测速测出来过 Tokyo 上传只有 1.94 Mbps 这种数字,但实际跑 API 完全没问题——纯粹是 USTC 那条 CERNET 链路当时拥塞,与你的使用场景没关系。
关于带宽的现实情况:Claude Code / ChatGPT API 单次几十 KB,1 Mbps 都跑不到。带宽测试主要是 sanity check,确认你的隧道没出毛病。决策的两个最稳定信号是 Tailscale ping(基线延迟)和带宽(吞吐上限)——这两个物理决定,不随机抖动。TTFB 的单次值只是参考,多跑几次取 median 才有意义。