Tailscale 速度慢怎么办?如何排查中继与直连
Tailscale 速度慢通常是因为流量经过了 DERP 中继而非直连。本文提供两条核心命令检测连接状态,并针对 UDP 端口封锁及 NAT 限制提供修复方案,助您恢复接近线路带宽的传输速度。
为什么 Tailscale 速度慢:中继连接而非直连
当 Tailscale 使用中继连接时速度较慢,而直连时则接近线路带宽上限。直连会将加密的 WireGuard 数据包直接从一台机器发送到另一台,因此传输速度取决于两端互联网链路的承载能力。中继连接则会将每个数据包先发送至第三方服务器,因此会受到该服务器延迟及其分配带宽的限制。Tailscale 官方性能页面对此有一句总结:“直连几乎总是能带来更低的延迟和更高的吞吐量。”
应用程序内部无法直接显示连接类型。文件拷贝变慢,SSH 会话出现延迟,因此首要任务是确认当前的连接类型。两条命令即可在 1 分钟内查明原因,后续工作则是针对性修复。在开始之前,了解其架构很有必要,因为 协调服务器与 WireGuard 数据平面是相互独立的系统,只有数据平面负责传输实际流量。
区分直连与中继的两个命令
在进行任何测量前,请先向对端发送一些流量。Tailscale 按需建立路径,如果您今天尚未与该对端通信,则可能尚未协商出路径,此时读取到的结果将是过期的。向对端的 tailnet 地址发送一个 ping 或一个 curl 即可。
tailscale status结果显示在每个对端信息行的末尾。
100.113.160.82 device-a tagged-devices linux active; offers exit node; direct 203.0.113.9:41641
100.104.93.78 device-b you@ android active; relay "tor"direct 后跟地址和端口,表示数据包正直接发送至该地址。relay "tor" 指代 DERP 服务器(指定加密数据包中继),这是 Tailscale 的中继服务器之一,发往该对端的每个数据包都会经过它。第三个值 peer-relay 将在下一节中介绍。
tailscale ping device-b健康的连接通常以中继方式开始,随后切换状态。当两台机器进行协商时,首批数据包会通过最近的 DERP 服务器,随后路径会发生切换:
pong from device-b (100.113.160.82) via DERP(tor) in 51ms
pong from device-b (100.113.160.82) via DERP(tor) in 48ms
pong from device-b (100.113.160.82) via 203.0.113.9:41641 in 35ms运行会在该处停止,因为 --until-direct 默认值为 true。无法直连的连接显示如下,其末尾是一句说明而非 pong:
pong from device-b (100.104.93.78) via DERP(tor) in 53ms
pong from device-b (100.104.93.78) via DERP(tor) in 60ms
direct connection not established最后一行即为诊断结果。这意味着 Tailscale 已发送了所有预定的探测包,但始终未能获取直连路径。若要持续观察中继路径而不止步于首次直连,请运行 tailscale ping --until-direct=false -c 20 device-b 并查看延迟分布。中继路径通常表现出更高的数值和更大的波动,因为它是两条互联网路径在您无法控制的机器上拼接而成的。
Tailscale 状态中的 peer-relay 是什么意思?
Peer relay 是指您 Tailnet 中一台负责为其他成员转发流量的机器,用于在无法建立直接连接时提供中转。它会监听您指定的 UDP 端口,且守护进程会优先使用它而非 DERP。tailscale status 会标记此类连接 peer-relay,而 tailscale ping 会打印出该中继节点的端点:
pong from device-b (100.97.143.93) via peer-relay(203.0.113.42:40000:vni:1) in 4ms
direct connection not established请仔细阅读。这仍然不是直接连接,因此运行结果最终仍会显示 direct connection not established。改变的是中继方。对于拥有公网 IP 地址且带宽充足的 VPS 而言,作为您自身流量的中继节点,其效果远优于共享的 DERP 节点,这就是为什么这对租用服务器的用户至关重要。请在拥有纯净公网端点的机器上启用它:
sudo tailscale set --relay-server-port=40000端口设置为 0 会随机选择一个未使用的端口,设置为空字符串则会禁用中继服务器。随后,在您的 Tailnet 策略文件中通过 tailscale.com/cap/relay 功能授权客户端设备使用该中继:
{
"grants": [
{
"src": ["tag:us-east-vpc"],
"dst": ["tag:us-east-relays"],
"app": {
"tailscale.com/cap/relay": []
}
}
]
}中继设备和客户端设备都需要 Tailscale 1.86 或更高版本,因此在花费时间配置策略文件前,请先在每台设备上使用 tailscale version 进行检查。守护进程的尝试顺序值得牢记:它首先尝试直接连接。如果失败,它会寻找允许使用的 peer relay。如果不存在,则回退到 DERP。DERP 永远不会完全被排除,因为它是两台机器最初进行协商的通道。
原因 1:出口防火墙拦截 UDP
Tailscale 文档指出了连接保持中继状态的两个原因,第一个就是 UDP 被拦截。请直接查询机器状态:
tailscale netcheck报告在此处被截断,最上方的字段决定了一切:
Report:
* UDP: true
* IPv4: yes, 203.0.113.9:41641
* IPv6: no
* MappingVariesByDestIP: false
* PortMapping:
* Nearest DERP: Dallas当你看到 UDP: false 时,这就是完整答案。机器无法向 Tailscale 的探测服务器发送 UDP 数据包,因此无法建立直接路径,守护进程只能回退到通过 TCP 443 端口的 DERP 中继。这种回退机制解释了为什么机器看起来一切正常:它已连接,可访问,且所有字节都在通过中继传输。
文档中记录了两条出站规则。“允许内部设备发起从 :41641 到 *:* 的 UDP 连接”,这是 WireGuard 流量本身;“允许内部设备发起通往 *:3478 的 UDP 连接”,这是 STUN(NAT 会话穿透工具),即机器用于获知自身公网地址和端口的协议。目标地址请使用通配符。Tailscale 会不断增加中继服务器,手动编写的地址列表在一年内就会失效。
在租用的服务器上,常见原因是激进的出口策略,这可能是你从加固镜像中继承的,也可能是服务商在上游施加的。首先检查默认的出站策略:
sudo ufw status verbose
sudo nft list rulesetDefault: deny (incoming), allow (outgoing) 是正常的,不是问题所在。如果默认出站策略为 deny,且仅允许 TCP 443 和 DNS,这正是导致服务器始终处于中继状态的原因,因为通过 TCP 443 的 DERP 路径可以通过该限制,而直接路径则不行。这些规则实际存在的位置取决于 底层运行的是 iptables 还是 nftables,修改错误的规则通常会导致配置无效。
入站侧同样重要,因为 VPS 拥有公网 IP 地址,因此可以作为连接对端中更容易建立连接的一方。如果其防火墙允许入站 UDP 流量通过 tailscaled 监听的端口,位于复杂家用路由器后的对端无需任何技巧即可连接到它。查找实际使用的端口:
sudo ss -lunp | grep tailscaled
sudo ufw allow 41641/udp41641 是默认的静态端口。如果 tailnet 开启了 randomizeClientPort 设置,客户端会随机选择端口,此时请从 ss 的输出中获取实际数字,而不是参考本页。随后检查服务商的控制面板。大多数主机商运行的网络防火墙独立于服务器内部的防火墙,你通过 ufw 添加的规则对它无效。
原因 2:一端或两端存在严格 NAT
第二种已知的故障原因是严格 NAT。NAT(网络地址转换)是路由器将私有地址重写为公网地址的过程。友好的路由器会为特定的内部套接字分配固定的公网端口,无论通信对象是谁,这被称为“端点独立映射”。而严格 NAT 会针对不同的目标分配不同的公网端口,因此机器从 STUN 服务器获取的地址,对等节点将无法使用。Tailscale 会在 netcheck 中将其报告为 MappingVariesByDestIP: true。
如果仅有一端存在严格 NAT,连接通常可以建立。只要另一端拥有稳定的公网端点,位于严格 NAT 后的机器仍能主动发起连接并建立路径。当两端同时存在严格 NAT 时,连接会失败,因为双方都无法预测对方将出现在哪个端口上。
对于拥有公网 IPv4 地址的 VPS,此字段应显示为 false,因为没有任何设备在转换该地址。如果你租用的服务器显示为 true,说明地址在服务商的网络中被转换了,机器内部的防火墙规则无法改变这一点。此时,你可以选择在拥有纯净公网端点的机器上部署对等中继(peer relay),或者迁移工作负载。这也是 通过子网路由器广播私有网段 发挥作用的场景,因为你只需要建立一条通往该网络的有效路径,而无需为网络中的每台设备都建立有效路径。
为什么出口节点会让 Tailscale 看起来比实际更慢
出口节点是第二个跳点,用户常将此归咎于隧道本身。启用出口节点后,请求会从您的笔记本电脑发出,通过隧道到达 VPS,再从 VPS 发往公网,响应则沿原路返回。即使到该 VPS 的连接极其顺畅,总速度也无法超过 VPS 自身的上行链路带宽,且增加的传输距离会体现在每个页面的加载中。
请分别测量这两个环节。关闭出口节点,然后针对 VPS 的 tailnet 地址单独测试隧道性能:
sudo tailscale set --exit-node=
sudo apt install -y iperf3
iperf3 -s在客户端针对该 tailnet 地址运行 iperf3 -c 100.113.160.82。该数值即为隧道性能。现在通过 sudo tailscale set --exit-node=100.113.160.82 重新开启出口节点,并对公网进行常规测速。该数值即为隧道性能加上 VPS 的上行链路性能。如果第一个数值正常而第二个数值较差,则问题不在 Tailscale,应检查 出口节点自身的网络状况与规格。如果您不确定选择了哪个节点,tailscale exit-node list 可以显示当前可用的节点信息。
CPU 是限制出口节点性能的另一个因素。Tailscale 的建议是优先选择主频更高的新一代 CPU,而非单纯追求核心数量,因此配置更多的 vCPU 并不会自动提升速度。在繁忙的共享主机上,您获得的 CPU 性能往往低于承诺值,而 来自邻居进程的 CPU 窃取时间 会导致吞吐量随时间波动,即便您的配置未做任何更改。
唯一的调优参数:rx-udp-gro-forwarding
Tailscale 记录了一个 Linux 设置,它适用于转发流量的机器,即出口节点(exit nodes)和子网路由器(subnet routers)。普通客户端无法从中获益。此功能需要 Tailscale 1.54 或更高版本以及 Linux 内核 6.2 或更高版本,因此在进行任何更改前请确认版本:
tailscale version
uname -r确认无误后,在面向互联网的接口上开启 UDP GRO(通用接收卸载)转发:
NETDEV=$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")
sudo ethtool -K $NETDEV rx-udp-gro-forwarding on rx-gro-list off检查设置是否生效:
ethtool -k $NETDEV | grep -E 'rx-udp-gro-forwarding|rx-gro-list'你应该能看到 rx-udp-gro-forwarding: on 和 rx-gro-list: off。此设置之所以有效,是因为 Tailscale 的流量基于 UDP,允许内核在转发路径中保持小 UDP 数据包的合并状态,意味着守护进程在处理相同字节数时,处理的分段数量更少。ethtool -K 在重启后会失效,因此需要持久化。在系统使用 networkd-dispatcher 的情况下:
printf '#!/bin/sh\n\nethtool -K %s rx-udp-gro-forwarding on rx-gro-list off \n' "$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")" | sudo tee /etc/networkd-dispatcher/routable.d/50-tailscale
sudo chmod 755 /etc/networkd-dispatcher/routable.d/50-tailscale手动运行一次脚本并检查其退出状态是否为 0。转发节点首先需要开启 IP 转发,这是一个独立的设置,若未配置会导致转发失败:
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conftailscale0 接口使用的 MTU 是多少?
不要再猜测这个数值,直接从机器上读取:
ip link show tailscale0输出中的 mtu 值就是隧道实际使用的 MTU,它低于以太网接口报告的 1500。这是有意为之,并非缺陷。你在隧道内发送的每个数据包都会被封装:IPv4 包含 20 字节的外层 IP 头部(IPv6 为 40 字节),UDP 头部占 8 字节,WireGuard 帧和认证标签占 32 字节。所有这些内容必须能够容纳在实际链路所能承载的范围内。Tailscale 会选择一个足够小的值,以确保在承载能力不足 1500 字节的链路上(包括 PPPoE 连接、部分移动网络和 IPv6 隧道)也能正常传输。
MTU 问题的症状非常典型,不要仅凭速度慢就断定是该问题。SSH 连接响应正常,ping 也能正常工作,但大文件传输或大型 HTTPS 页面会完全卡住,而不是仅仅变慢。这种模式意味着超大数据包在某处被丢弃,且没有 ICMP 消息返回通知发送方。调高 tailscale0 的 MTU 至 1500 只会加剧问题,因为原本就无法容纳的数据包会变得更大。真正的解决方法是通过二分法查找有效路径 MTU,并在转发流量的路由器上限制 TCP MSS。连接类型不会改变这一点:中继路径和直连路径使用相同的接口 MTU。
FAQ
如何判断 Tailscale 连接是直连还是中继?
运行 tailscale status 并查看对端信息行的末尾。direct 203.0.113.9:41641 表示直连,relay "tor" 表示所有数据包均通过该 DERP 服务器转发,peer-relay 表示数据包通过你 tailnet 中的另一台机器中转。若需二次确认,请运行 tailscale ping <peer>:健康的路径会以 DERP 开头,随后打印带有明文地址和端口的 pong;而中继路径则会持续打印 DERP pong,直到运行结束并显示 direct connection not established。请先向对端发送一些流量,因为 Tailscale 仅在有需求时才会建立路径。
为什么我的 VPS 始终无法建立直连?
在 VPS 上运行 tailscale netcheck。如果输出 UDP: false,说明出口防火墙丢弃了出站 UDP 数据包,导致守护进程回退到通过 TCP 443 端口的 DERP 中继,这也是机器看起来仍处于连接状态的原因。请允许从 41641 端口到任意目的地的出站 UDP 流量,以及到任意目的地 3478 端口的出站 UDP 流量。请同时检查云服务商的网络防火墙和服务器内部的防火墙,因为它们是独立的控制层,ufw 规则无法影响服务商侧的设置。
Tailscale 中继连接的安全性是否低于直连?
不是。DERP 服务器转发的是它无法解密的 WireGuard 数据包,因为加密密钥是在你的设备上生成的,且从不离开设备。中继带来的代价是延迟和吞吐量,而非机密性。协调服务器控制的是设备间如何发现彼此,在决定是否自托管之前,理解 密钥材料与连接元数据之间的分离 很有必要。
rx-udp-gro-forwarding 设置对所有机器都有帮助吗?
没有。该设置仅针对转发他人流量的 Linux 机器(即出口节点和子网路由器)进行说明。仅与自身对端通信的笔记本电脑或服务器无法从中获益。此外,该功能要求 Tailscale 1.54 或更高版本以及 Linux 内核 6.2 或更高版本,因此请先检查 tailscale version 和 uname -r,并记住 ethtool -K 在重启后会重置,除非你将其持久化。
Tailscale 比原生 WireGuard 慢吗?
两者都使用 WireGuard 加密流量。Tailscale 增加了原生 WireGuard 需要手动完成的连接建立过程,而正是这个过程有时会导致连接落入中继。原生 WireGuard 没有中继可供回退:它要么直连,要么连接失败。因此,请在同等条件下进行比较,仅在 tailscale status 显示 direct 时对 Tailscale 进行基准测试。如果你想通过手动配置的版本进行对比,自行构建的 WireGuard 服务器 大约需要 40 行配置,而你所做的权衡已在 两种方案的对比 中说明。