Hacker News 中文摘要

RSS订阅

让 Tailscale 更快 -- Making Tailscale Faster

关注过Tailscale(Tailscale)的人知道我们这群极客十分在乎网络连接,常谈起NAT穿透(NAT Traversal)。驯服NAT是Tailscale核心价值,即便网络不友好也能找到路径。数据平面也须高性能。多年来我们持续投资提速:先在Linux设备提高TCP吞吐,继而在wireguard-go实现裸机超10Gb/s,后又利用分段卸载让UDP应用吞吐提升4倍多,并构建Tailscale Peer Relays改善复杂条件性能,使Tailscale能承载更敏感负载。

正文

如果你关注过 Tailscale,就知道我们只是一群非常在意互联网连接性的极客。我们最爱聊的话题之一是 NAT 穿透——这是 Tailscale 的核心价值之一:我们驯服了 NAT。不是每个网络都友好,但 Tailscale 在多种条件下都能找到通路。但对一个互联网协议来说,这还不是全部:数据平面也必须高性能。

多年来我们一直在让 Tailscale 变快。最初是提高 Linux 设备上的 TCP 吞吐,然后对 wireguard-go 做出重大突破,在裸机上超过 10Gb/s。后来我们利用分段卸载,让基于 UDP 的应用吞吐提升 4 倍以上。除了数据平面的改进,我们还构建了 Tailscale Peer Relays 这样的基础能力,在棘手条件下改善网络性能。

这些工作让 Tailscale 能用于对性能更敏感的场景:持续集成、agentic 工作流、远程开发环境、机器人边缘设备、重数据和遥测负载等等。

我们认为 Tailscale 很快,但也认为还能更快。

今天我们详细说明如何用多队列技术提升 app connectors、subnet routers 和 exit nodes 的吞吐(将在 2026 年下半年落地)。还会预览即将在稳定版客户端中部署的吞吐与内存开销改进,以及我们想为客户解决的性能工具问题。

小包的内存开销更低

大多数网络包都很小,比如 1 KiB。但为了使用 Linux 最高效的吞吐工具,比如通用接收卸载(GRO),Tailscale 必须准备一次接收 64 KiB 流量。有点像集装箱运输:港口、货轮和卡车都按一种集装箱形状建造,无论它装得多满。

Tailscale 必须拆开这些集装箱——每个包都要单独解密和投递。为 Tailscale 的加密和网络基础提供支撑的 wireguard-go 实现只提供一个 64 KiB 的缓冲区用于拆包。所以一个 1 KiB 的包每次都会被复制进它自己的 64 KiB 缓冲区。这是个很值得优化的目标。

在 Linux 和 Android 上,Tailscale 现在让这些包留在原地。它在单次大读取中识别每个包的起点和终点,而不是把它复制到别处。小包在内存中保持小,许多包共用一次分配,被复制的时间也更少。仅这一点就在许多网络配置下带来约 5% 的提速。

另外,我们缩短了包队列——即包在流水线各阶段之间等待的队伍。队列的作用是吸收流量突发。测试显示大部分深度都没被用上,而更短的队列意味着更少的等待时间和更低的内存开销。

省下来的内存去了哪?我们把它让给了一些最辛苦的节点:subnet routers 和 app connectors。

subnet routers、app connectors 和 exit nodes 的多队列

不同 tailnet 里的 subnet router 可能完全不同。对于运行小型 homelab 网络的人,一个 subnet router 能轻松处理少量 192.168.x.y 的非 Tailscale 设备。而面向云部署、拥有数百个 peer 的 subnet router 会承载多得多的流量。

直到最近,subnet routers、app connectors 和 exit nodes 都在一条有序的单线程流水线中处理多个独立流的包。这意味着一条车道被许多连接共享,因为接收方应用绝不能看到自己的包乱序到达。

在降低内存占用后,我们有余力实现多队列系统:用多条车道代替一条,按机器资源而非 peer 数量来扩展。每条包流分到一条车道并留在那里,而车道并行运行,让工作分散到各个 CPU 核心。

结果是 subnet routers 和 app connectors 的聚合容量更高,接收与转发包之间的延迟更低。你已有的硬件被更高效地利用。app connectors 和 exit nodes 通常服务大量短连接用户,提升尤其明显。

“这转化为更低的延迟,本质上是从我们从网线上读到数据、到把它交给操作系统这段时间里更快的处理,”Tailscale 技术团队成员 Alex Valiushko 说。

用 writev 提升吞吐

Tailscale 客户端利用 Linux 的 writev 能力,可以一次操作把多段包数据交给 Linux 内核,而不必先复制并合并这些片段再交给内核。writev 里的 v 代表“向量”:Tailscale 可以描述需要移动的独立数据片段,而不移动它们。这意味着内存中更少的包数据复制、更少的写操作和更高的吞吐。

用 netmap 缓存加快启动

目前这些提速只在 Linux 以及部分适用的 Android 系统上可用。但我们也在做适用于其他系统的功能。Tailscale 客户端很快就能用 netmap 缓存在许多情况下更快启动。

一台机器连接 Tailscale 时,通常先连接 Tailscale 的控制平面,在典型网络上大约 100 毫秒。机器完成认证并拿到一份“网络地图”(netmap),描述它能访问哪些设备以及如何访问。这个启动过程应该让人觉得快,甚至瞬间完成,网络良好时通常确实如此。

但当你连着糟糕的飞机 Wi-Fi,或身处有激进过滤的酒店,或其他不太好的连接环境时,机器可能要花很久才能连上控制平面——有时根本连不上。问题出在哪往往不明显,但结果是你连不到其他设备。

即使在理想网络条件下,100 毫秒的启动延迟对一些延迟敏感的工作负载来说也可能太多。

Netmap 缓存在控制平面无法快速到达时帮助机器建立连接。启用后,tailnet 上的每台设备都会在磁盘上存一份 netmap 副本。设备启动时可以用这份缓存建立与其他设备的连接,直到它能联系上控制平面获取最新信息。(这些连接由设备之间直接协商,和往常一样,Tailscale 看不到任何流量。)

“糟糕的网络条件——那正是人们能从 netmap 缓存中获得很大用处的场景,”技术团队成员 Claus Lensbøl 说。“(设备客户端会说)‘你知道吗?我们还没和控制平面通话。我们大概很快就通了。在那之前,你还是可以先开始做事。’”

有一些限制。缓存只有在设备此前至少连接过一次 tailnet、从控制平面获取过网络地图时才有效。此外,netmap 缓存要求设备有持久磁盘空间来存储缓存。我们已经注意尽量减少不必要的磁盘写入,但有些情况下你可能不想启用它。比如在特别大的 tailnet 上,更新缓存可能带来大量磁盘流量。同样,使用 SD 卡等慢速或易磨损存储的设备可能宁愿不启用。

不过对大多数 tailnet 上的大多数设备来说,这个功能能明显加快设备启动时彼此建立联系的速度。我们看到控制平面可达性差的 tailnet,在“热”缓存启动下通过数据平面开始传输,比“冷”启动快一到两个数量级。对启动延迟不稳定、或离 DERP 服务器和控制平面很远的设备,好处尤其明显。

这些提速什么时候能看到

  • 通过缓冲区改动带来的内存降低(Linux/Android)预计在 v1.104 客户端中推出。
  • 惠及 subnet routers 和 app connectors 的多队列计划在 v1.104 之后的版本中推出。
  • 吞吐提升(Linux/Android)已在 2026 年春季部分实现;利用内存和吞吐方面的额外收益计划在 v1.104 之后的版本中推出。
  • Netmap 缓存在当前 Tailscale 客户端中作为功能开关可用;经过进一步测试后,预计在 v1.104 中默认提供。移动客户端预计在 v1.104 之后的版本中提供。

性能仍然难以诊断和测试

我们觉得 Tailscale 快,但你不该只凭我们这句话。所以我们正在探索一套了解 Tailscale 的监控和测试工具包。我们想给客户真正需要的工具,以 Tailscale 原生的方式测试、诊断和理解自己的网络配置。

我们看到现代性能测试存在这些缺口:

  • 分发成本:大多数性能工具是点对点的,需要你在每个端点上安装东西。
  • 工作流僵化:很容易跑错测试、拿到错误输出,然后去追一个根本不存在的问题。
  • 协议支持:许多工具不支持较新的协议,比如 QUIC 和 HTTP/3。
  • Tailscale 感知:通用工具不是 Tailscale 原生的。它无法告诉你连接是在用 DERP 还是直连、peer relay 是否有帮助,或连接路径如何随时间变化。

现有工具不理解 Tailscale 原生的路径和状态。所以我们正在探索能理解的工具。来帮助我们塑造 Tailscale 性能测试的未来。

评论

讨论整体偏向吐槽 Tailscale 的性能:多人拿它和裸 WireGuard 对比后认为速度和延迟不达标,联合创始人则亲自出面反驳「换内核实现就会更快」的直觉,双方分歧落在瓶颈究竟在实现方式还是硬件加速等结构性限制上。

认为速度慢是 Tailscale 最大的短板

  • 有人质疑它慢得离谱:在通常使用的 Windows 和 Mac 客户端上跑不过 1Gbps,Linux 上做合成大包测试也很难摸到 10Gbps,换成 IMIX 这类更贴近真实的流量模型更没竞争力;而这类问题本可通过内核 WireGuard、以及 IPsec 的 DPDK/XDP 零拷贝方案解决,官方却没表现出兴趣(较多人) > Tailscale 很慢,在常见的 Windows 和 Mac 客户端上达不到 1Gbps,Linux 上做合成大包测试也难到 10Gbps(@iscoelho)

联合创始人:内核 WireGuard 并不天然更快

  • 回应称问题没有「换成内核实现就提速」那么简单:有一段时间 wireguard-go 靠优化反而比内核 WireGuard 更快,对方后来吸收了这些改进,现在两边一起往下一个数量级推进;真要做超高带宽,DPDK 这类用户态方案才是长期最优解,内核模式未必是纯粹的优势(高赞) > 真正高带宽的场景下,DPDK 这类方案才是长期最好的选择,而且它们主要跑在用户态,是有充分理由的(@apenwarr)

自建裸 WireGuard 就够用,Tailscale 变得多余

  • 有用户称自己过去非常喜欢 Tailscale,后来在家用网络用动态 DNS 暴露一个 WireGuard 端口,就觉得 Tailscale 不再必要:裸 WireGuard 更稳定,不用再和手机上的 DNS 问题较劲,体感更快,配置也简单得出奇(少数人)

使用中的具体痛点

  • 有人反馈在自己的场景里,250 个会话、隧道内只有 60MB/s 时延迟就呈抛物线式恶化(少数人)
  • 有人希望走出口节点时能更省电(少数人)
  • 还有人追问博文为何聚焦 Linux/Android:是团队从这里起步,还是所用方法只在 Linux/Android 上可行(少数人)

有价值的补充

  • 用户态实现并非天生劣于内核态:wireguard-go 曾因优化更好而快过内核 WireGuard,内核实现后来吸收了这些改进。
  • IPsec 的 DPDK/XDP 实现能到 100Gbps 甚至 400Gbps,靠的是用户态加零拷贝,而不是内核态本身。
  • WireGuard 使用的加密套件得不到硬件加速器支持,这是冲上几百 Gbps 的结构性障碍,可能要换掉整个包格式才行。
  • 联合创始人提到 WireGuard 还需要支持后量子加密,或许两个问题会一并解决,届时 Tailscale 可以跟进。