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 的其中一台中繼機器;傳送給該對端的每個封包都會經過這台伺服器。第 3 個值 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 永遠不會完全退出流程,因為兩台機器一開始就是透過它進行協商。
原因 1:阻擋 UDP 的出口防火牆
Tailscale 記錄了連線持續經由 relay 的兩個原因,第一個是 UDP 遭到阻擋。直接向該機器查詢:
tailscale netcheck此處已將報告截短,而最上方的欄位會決定所有結果:
Report:
* UDP: true
* IPv4: yes, 203.0.113.9:41641
* IPv6: no
* MappingVariesByDestIP: false
* PortMapping:
* Nearest DERP: Dallas看到 UDP: false 時,答案就很明確。該機器無法將 UDP 封包送出至 Tailscale 的探測伺服器,因此無法建立直接路徑,daemon 會改用 TCP port 443 上的 DERP。這也是該機器看似完全正常的原因:它已連線、可連線,而且每個位元組都經由 relay 轉送。
文件列出兩條對外傳送規則。「允許內部裝置從 :41641 發起 UDP 至 *:*」,這是 WireGuard 本身的流量;以及「允許內部裝置發起 UDP 至 *:3478」,這是 STUN(NAT 的工作階段穿越工具),也是該機器用來得知自身公開位址與連接埠的通訊協定。目的地請使用萬用字元。Tailscale 會持續加入 relay 伺服器,手動建立的位址清單在一年內就可能失效。
在租用的伺服器上,常見原因是過於嚴格的出口政策。這可能來自沿用的強化映像檔,也可能是供應商在上游套用的政策。先查看預設的對外傳送政策:
sudo ufw status verbose
sudo nft list rulesetDefault: deny (incoming), allow (outgoing) 沒有問題,不是故障原因。若對外傳送的預設政策為 deny,只允許 TCP 443 與 DNS,伺服器就會永久使用 relay。原因是 TCP 443 上的 DERP 路徑可以通過該規則,但直接路徑無法通過。這些規則實際位於何處,取決於 該主機底層使用 iptables 或 nftables;修改錯誤的防火牆是導致設定完全沒有變化的常見原因。
入站方向同樣重要,因為 VPS 具備公開 IP 位址,因此可以成為連線雙方中較容易連入的一端。如果其防火牆允許對該連接埠的入站 UDP,而 tailscaled 正在監聽該連接埠,位於複雜家用路由器後方的對等端就能直接連入,無須額外處理。找出目前實際使用的連接埠:
sudo ss -lunp | grep tailscaled
sudo ufw allow 41641/udp41641 是預設的固定連接埠。啟用 randomizeClientPort 設定的 tailnet 會讓用戶端改用隨機連接埠。此時應從 ss 的輸出取得實際連接埠,而不是使用本頁的數值。接著檢查供應商的控制台。多數主機會另外執行與伺服器內部防火牆分離的網路防火牆,而使用 ufw 新增的規則不會影響該防火牆。
原因 2:一端或兩端使用硬式 NAT
文件記載的第二個原因是硬式 NAT。NAT(網路位址轉換)是路由器將私有位址改寫為公有位址時所執行的處理。友善的路由器會針對特定的內部 socket,不論你與哪個對象通訊,都維持相同的公有埠。這稱為端點獨立映射。硬式 NAT 則會針對不同目的地配置不同的公有埠,因此機器從 STUN server 得知的位址,並不是對等端可以使用的位址。Tailscale 會在 netcheck 中以 MappingVariesByDestIP: true 回報此狀態。
只有一端使用硬式 NAT 時仍可運作。如果另一端具有穩定的公有端點,硬式 NAT 後方的機器仍可建立連入連線,並形成連線路徑。兩端同時使用硬式 NAT 時就會失敗,因為雙方都無法預測對方會使用哪個埠出現。
在具有公有 IPv4 位址的 VPS 上,此欄位應顯示 false,因為該位址未經 NAT 轉換。如果你租用的 server 顯示 true,表示該位址在 provider 的網路中某處遭到轉換;在機器內部設定任何防火牆規則都無法改變這點。你可以在具有乾淨公有端點的機器上放置對等端 relay,或遷移工作負載。這也是透過子網路路由器 廣告你的私有範圍 特別有用的情況,因為你只需要建立一條通往該網路的良好路徑,而不必為其中每台裝置各自建立良好路徑。
為什麼使用 exit node 時,Tailscale 看起來比實際速度慢
exit node 會增加一個跳點,因此讀者常把問題歸咎於 tunnel。選取 exit node 後,請求會離開筆電,經由 tunnel 傳送到 VPS,再從 VPS 連到公開網際網路;回應也會沿相同路徑返回。即使 VPS 本身具備完全直接的連線,總速度也不可能超過 VPS 的對外連線速度,而增加的網路距離會反映在每次頁面載入中。
請分別測量這兩段。先關閉 exit node,再直接以 VPS 的 tailnet 位址測試 tunnel:
sudo tailscale set --exit-node=
sudo apt install -y iperf3
iperf3 -s在用戶端對該 tailnet 位址執行 iperf3 -c 100.113.160.82。這個數值代表 tunnel 的速度。接著使用 sudo tailscale set --exit-node=100.113.160.82 重新啟用 exit node,並對公開網際網路執行一般速度測試。這個數值代表 tunnel 加上 VPS 對外連線的速度。如果第一個數值良好、第二個數值不佳,問題就不在 Tailscale,應改查exit node 自身的網路與規格。如果不確定選取了哪個 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手動執行一次該 script,並確認其結束狀態為 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.conftailscale0 介面使用哪個 MTU?
不要猜這個數值,直接從機器讀取:
ip link show tailscale0該輸出中的 mtu 值,就是通道實際使用的 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 server,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。請同時檢查供應商的網路防火牆和 server 內部的防火牆,因為兩者是分開的控制項,而 ufw 規則不會影響供應商的防火牆規則。
經由中繼的 Tailscale 連線是否比直接連線不安全?
不會。DERP server 會轉送無法解密的 WireGuard 封包,因為加密金鑰是在你的裝置上產生,且不會離開這些裝置。使用中繼的代價是延遲和輸送量,而不是機密性。協調 server 能控制的是哪些裝置可以得知彼此的存在;在決定要自架多少元件前,值得先了解金鑰資料與連線中繼資料之間的區分。
rx-udp-gro-forwarding 設定是否對每台機器都有幫助?
不會。這項設定是針對替其他裝置轉送流量的 Linux 機器所記載,也就是 exit node 和 subnet router。只與自身對等端通訊的筆電或 server 不會因此受益。此外,它也要求 Tailscale 1.54 或更新版本,以及 Linux kernel 6.2 或更新版本,因此請先檢查 tailscale version 和 uname -r,並記住 ethtool -K 會在重新開機後重設,除非將其設為持久設定。
Tailscale 是否比單純的 WireGuard 慢?
兩者都使用 WireGuard 加密流量。Tailscale 會額外處理連線設定,而單純的 WireGuard 需要你手動完成這些工作;有時因此會使用中繼。單純的 WireGuard 沒有可使用的中繼:它要麼直接連線,要麼完全無法建立連線。因此請比較相同條件,只有在 tailscale status 顯示 direct 時,才對 Tailscale 進行效能測試。如果要使用手動設定的版本作為比較,自行建置的 WireGuard server 約需 40 行設定,而兩種作法之間的取捨詳見兩種作法的比較。