Tailscale 速度慢?如何判斷中繼或直連
Tailscale 使用中繼時速度通常較慢,直連則接近線路速率。用 2 個命令確認連線類型,並了解 UDP 被封鎖與 VPS 硬式 NAT 的修正方法。
為什麼 Tailscale 速度慢:使用中繼而不是直連
使用中繼連線時,Tailscale 的速度會變慢;使用直連時,速度則接近線路速率。直連會將加密的 WireGuard 封包直接從一台機器傳送到另一台機器,因此速度取決於兩端網際網路連線可提供的頻寬。中繼連線會先將每個封包傳送到第三台機器,因此會受到該機器的延遲,以及你能取得的頻寬比例影響。Tailscale 的效能頁面用一句話說明了這點:「直連幾乎總能帶來更低的延遲與更高的吞吐量。」
應用程式本身不會顯示兩者的差異。檔案複製只會變慢,SSH 工作階段只會出現延遲。因此,第一步是確認目前使用的是哪一種連線。執行兩個命令即可在不到 1 分鐘內得到答案,後續工作則是排除造成該狀況的原因。開始前,先了解連線架構會很有幫助,因為協調伺服器與 WireGuard 資料平面是兩個獨立系統,只有資料平面會傳送你的資料。
判斷直連或中繼的 2 個命令
測量前,先向對端傳送一些流量。Tailscale 會依需求建立路徑,因此今天尚未連線過的對端可能還沒有完成協商,此時讀到的結果可能已經過時。對對端的 tailnet 位址執行 1 次 ping 或 1 次 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 伺服器(designated encrypted relay for packets,封包專用的指定加密中繼),也就是 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,並查看延遲分布。中繼路徑通常會顯示較高且變動較大的數值,因為它是由一台不受你控制的機器串接 2 條網際網路路徑而成。
tailscale status 中的 peer-relay 是什麼意思?
peer relay 是 tailnet 中的一台機器。當無法建立直接連線時,它會替其他成員轉送網路流量。它會監聽你指定的 UDP 埠,而 daemon 會優先使用它,而不是 DERP。tailscale status 會標示這類連線 peer-relay,而 tailscale ping 會顯示 relay 的端點:
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 會選擇隨機的未使用埠,設定為空字串則會停用 relay server。接著,在 tailnet policy file 中加入 tailscale.com/cap/relay capability,授予用戶端裝置使用它的權限:
{
"grants": [
{
"src": ["tag:us-east-vpc"],
"dst": ["tag:us-east-relays"],
"app": {
"tailscale.com/cap/relay": []
}
}
]
}relay 裝置與用戶端裝置都需要 Tailscale 1.86 或更新版本。因此,在花費一小時研究 policy file 之前,請先在每台裝置上使用 tailscale version 檢查版本。daemon 嘗試連線的順序值得記住。它會先嘗試建立直接連線。如果失敗,則會尋找允許使用的 peer relay。如果沒有可用的 peer relay,便會退回使用 DERP。DERP 永遠不會完全退出流程,因為兩台機器一開始就是透過它協商連線。
Cause 1: an egress firewall that blocks UDP
Tailscale documents two reasons a connection stays relayed, and the first is blocked UDP. Ask the machine directly:
tailscale netcheckThe report is trimmed here, and the top field is the one that decides everything:
Report:
* UDP: true
* IPv4: yes, 203.0.113.9:41641
* IPv6: no
* MappingVariesByDestIP: false
* PortMapping:
* Nearest DERP: DallasUDP: false is the whole answer when you see it. The machine cannot get a UDP packet out to Tailscale's probe servers, so no direct path can form, and the daemon falls back to DERP over TCP port 443. That fallback is why the machine still looks perfectly healthy: it is connected, it is reachable, and every byte is being relayed.
Two outbound rules are documented. "Let your internal devices start UDP from :41641 to *:*", which is the WireGuard traffic itself, and "Let your internal devices start UDP to *:3478", which is STUN (session traversal utilities for NAT), the protocol the machine uses to learn its own public address and port. Use wildcards for the destinations. Tailscale adds relay servers over time, and a hand written list of addresses will be wrong within a year.
On a rented server the usual culprit is an aggressive egress policy, either one you inherited in a hardened image or one your provider applies upstream. Look at the default outgoing policy first:
sudo ufw status verbose
sudo nft list rulesetDefault: deny (incoming), allow (outgoing) is fine and is not your problem. An outgoing default of deny with a short allow list of TCP 443 and DNS is exactly the shape that keeps a server on a relay forever, because the DERP path over TCP 443 fits through that hole and the direct path does not. Where those rules actually live depends on whether the box is running iptables or nftables underneath, and editing the wrong one is a common way to change nothing.
The inbound side matters too, because a VPS has a public IP address and can therefore be the easy half of the pair. If its firewall accepts inbound UDP on the port tailscaled listens on, peers behind awkward home routers can reach it without any tricks. Find the port that is actually in use:
sudo ss -lunp | grep tailscaled
sudo ufw allow 41641/udp41641 is the default static port. A tailnet with the randomizeClientPort setting turned on makes clients pick a random port instead, in which case take the real number from the ss output rather than from this page. Then check your provider's control panel. Most hosts run a network firewall that is separate from the one inside the server, and a rule you added with ufw does nothing about it.
原因 2:一端或兩端使用嚴格 NAT
第二個已知原因是嚴格 NAT。NAT(網路位址轉譯)是路由器將私有位址改寫為公有位址時所執行的功能。相容性較好的路由器,會為特定的內部 socket 維持相同的公有埠,不論你與哪個對象通訊。這稱為端點獨立映射。嚴格 NAT 則會針對每個目的地配置不同的公有埠,因此機器從 STUN 伺服器得知的位址,並不是對等端可以使用的位址。Tailscale 會在 netcheck 中以 MappingVariesByDestIP: true 回報此狀態。
只有一端使用嚴格 NAT 時仍可運作。如果另一端具有穩定的公有端點,嚴格 NAT 後方的機器仍可向外建立連線,並形成通道。兩端同時使用嚴格 NAT 時就會失敗,因為任一端都無法預測另一端會使用哪個埠出現。
在具有公有 IPv4 位址的 VPS 上,此欄位應顯示 false,因為該位址未經任何轉譯。如果你租用的伺服器顯示 true,表示該位址在供應商的網路中某處遭到轉譯;在機器內設定防火牆規則也無法改變這點。你可以在具有乾淨公有端點的機器上部署對等端轉送服務,或搬遷工作負載。這也是從子網路路由器宣告私有範圍特別有用的情況,因為你只需要一條通往該網路的良好路徑,而不是通往其中每台裝置的良好路徑。
為何使用 exit node 會讓 Tailscale 看起來比實際速度慢
exit node 會增加一個轉送節點,因此讀者常把問題歸咎於通道。選取 exit node 後,請求會離開筆記型電腦,經由通道傳送至 VPS,再從 VPS 離開並進入公用網際網路;回應也會沿相同路徑返回。即使與該 VPS 之間的連線完全直接,總速度也不可能快於 VPS 自身的上行連線,而增加的網路距離會反映在每次頁面載入中。
請分開測量這兩段。先關閉 exit node,再直接以 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 重新啟用 exit node,再對公用網際網路執行一般速度測試。這個數值代表通道加上 VPS 上行連線的效能。如果第一個數值良好、第二個數值不佳,問題就不在 Tailscale,應檢查exit node 自身的網路與規模。如果不確定選取了哪個節點,tailscale exit-node list會顯示目前可用的節點。
CPU 是 exit node 效能上限的另一項因素。Tailscale 建議優先選用較新世代且時脈較高的 CPU,而不是單純追求較多的核心數,因此 vCPU 較多的方案不一定在這裡更快。在負載繁重的共享主機上,方案承諾提供的 CPU 資源不一定等於實際取得的資源;鄰近租戶造成的 steal time會表現為吞吐量隨時段變動,即使你的設定沒有變更。
唯一需要調整的設定:rx-udp-gro-forwarding
Tailscale 文件只提到一項 Linux 設定,適用於會轉送流量的機器,也就是 exit node 和 subnet router。一般用戶端不會受益。這項設定需要 Tailscale 1.54 或更新版本,以及 Linux kernel 6.2 或更新版本,因此請先確認兩者,再進行變更:
tailscale version
uname -r確認條件符合後,在面向網際網路的介面上啟用 UDP GRO(generic receive offload)轉送:
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。讓 kernel 在轉送路徑中持續合併較小的 UDP 封包後,daemon 便能以較少、較大的區段處理相同的位元組數。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 forwarding。這是另一項獨立設定,失敗原因也不同:
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.conf你的 tailscale0 介面使用哪個 MTU?
不要再猜這個數值,直接從機器上讀取:
ip link show tailscale0該輸出中的 mtu 值,就是通道實際使用的數值,而且低於乙太網路介面回報的 1500。這是刻意設計的行為,不是錯誤。你在通道內傳送的每個封包都會再包裝一層:IPv4 使用 20 bytes 的外部 IP 標頭,IPv6 使用 40 bytes,另加 8 bytes 的 UDP 標頭,以及 32 bytes 的 WireGuard 框架與驗證標籤。這些內容都必須符合實際路徑可傳輸的大小。Tailscale 會選擇足夠低的數值,以便通過無法承載完整 1500 bytes 封包的連線,包括 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,daemon 已退回使用 TCP 443 上的 DERP,因此該機器看起來仍保持連線。允許來源連接埠為 41641、目的地為任意位置的對外 UDP,也允許目的地連接埠為 3478、目的地為任意位置的對外 UDP。請同時檢查供應商的網路防火牆與伺服器內部的防火牆,因為兩者是分開的控制項,ufw 規則不會影響供應商的防火牆規則。
透過中繼的 Tailscale 連線是否不如直接連線安全?
不會。DERP 伺服器會轉送無法解密的 WireGuard 封包,因為加密金鑰是在您的裝置上產生,且不會離開這些裝置。使用中繼的代價是延遲與吞吐量,而不是機密性。協調伺服器確實會控制哪些裝置能得知彼此的存在,而在決定要自架多少元件前,值得先了解金鑰材料與連線中繼資料之間的分離。
rx-udp-gro-forwarding 設定是否對每台機器都有幫助?
沒有。文件說明這項設定適用於替其他機器轉送流量的 Linux 機器,也就是 exit node 與子網路路由器。只與自己的對等端通訊的筆電或伺服器,啟用這項設定不會有任何效益。此外,這項設定需要 Tailscale 1.54 或更新版本,以及 Linux kernel 6.2 或更新版本,因此請先檢查 tailscale version 與 uname -r,並記住 ethtool -K 會在重新開機後重設,除非您將其設為持久化。
Tailscale 是否比原生 WireGuard 慢?
兩者都使用 WireGuard 加密網路流量。Tailscale 增加了原生 WireGuard 留給您手動處理的連線建立程序,而這項程序有時會讓您使用中繼。原生 WireGuard 沒有可供使用的中繼:它要嘛直接連線,要嘛完全無法建立連線。因此請比較相同條件,只有在 tailscale status 顯示 direct 時才對 Tailscale 進行基準測試。若要使用手動設定的版本進行比較,自行建置的 WireGuard 伺服器大約需要 40 行設定,而兩種方式之間的取捨詳見兩種方法的比較。