SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor

Tailscale chậm: kiểm tra relay hay kết nối trực tiếp

Tailscale qua DERP relay thường chậm, còn direct gần đạt tốc độ đường truyền. Dùng 2 lệnh để kiểm tra, rồi xử lý UDP bị chặn và NAT khó trên VPS.

Vì sao Tailscale chậm: dùng relay thay vì kết nối trực tiếp

Tailscale chậm khi kết nối được chuyển tiếp qua một trong các DERP server dùng chung của nó, và gần đạt tốc độ đường truyền khi kết nối trực tiếp. Kết nối trực tiếp truyền thẳng các packet WireGuard đã mã hóa từ máy này sang máy kia, nên tốc độ bị giới hạn bởi khả năng truyền của 2 đường truyền Internet. Kết nối qua relay gửi mọi packet qua một máy thứ 3 trước, nên phải chịu độ trễ của máy đó và phần bandwidth mà bạn nhận được từ máy đó. Mức ảnh hưởng phụ thuộc vào relay. Tailscale mô tả các DERP server là “thường chậm hơn kết nối trực tiếp”, còn peer relay do bạn tự vận hành thì “thường nhanh hơn DERP” và trong các thử nghiệm ban đầu của Tailscale đạt throughput “gần bằng kết nối trực tiếp”. Trang hiệu năng của Tailscale nêu quy tắc chung trong một câu: “Kết nối trực tiếp hầu như luôn có độ trễ thấp hơn và throughput cao hơn.”

Không có thành phần nào bên trong application cho bạn thấy sự khác biệt này. Việc copy file chỉ đơn giản là chậm, còn SSH session chỉ đơn giản là bị lag. Vì vậy, việc đầu tiên là xác định loại kết nối bạn đang dùng. 2 lệnh sẽ trả lời câu hỏi này trong chưa đầy 1 phút. Sau đó, mọi bước đều nhằm sửa nguyên nhân. Bạn nên hiểu phần setup trước khi bắt đầu, vì coordination server và WireGuard data plane là 2 hệ thống riêng biệt và chỉ data plane mới truyền dữ liệu của bạn.

Hai lệnh cho biết kết nối là trực tiếp hay qua relay

Gửi một ít lưu lượng đến peer trước khi đo. Tailscale xây dựng path theo nhu cầu, vì vậy peer mà bạn chưa trao đổi dữ liệu hôm nay có thể chưa thương lượng path. Khi đó, bạn sẽ đọc một kết quả đã cũ. Chỉ cần chạy một ping hoặc một curl đến địa chỉ tailnet của peer.

tailscale status

Kết quả nằm ở cuối dòng của từng peer.

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 theo sau bởi một địa chỉ và một port nghĩa là các packet đi thẳng đến địa chỉ đó. relay "tor" là tên của một máy chủ DERP (designated encrypted relay for packets), tức một trong các máy relay của Tailscale; mọi packet đến peer đó đều đi qua máy này. Giá trị thứ ba, peer-relay, được giải thích trong phần tiếp theo.

tailscale ping device-b

Một kết nối bình thường bắt đầu qua relay rồi chuyển sang kết nối trực tiếp. Các packet đầu tiên đi qua máy chủ DERP gần nhất trong lúc hai máy thương lượng, sau đó path thay đổi:

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

Lệnh dừng tại đó vì --until-direct mặc định có giá trị true. Kết nối không thể chuyển sang direct sẽ có dạng như sau và kết thúc bằng một câu thay vì một 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

Dòng cuối cùng là kết quả cần tìm. Điều đó nghĩa là Tailscale đã gửi toàn bộ probe cần gửi nhưng không bao giờ tạo được direct path. Để tiếp tục theo dõi một relayed path thay vì dừng ở direct path đầu tiên, hãy chạy tailscale ping --until-direct=false -c 20 device-b và đọc độ phân tán của latency. Relayed path thường có cả giá trị cao hơn và dao động lớn hơn, vì đó là hai network path nối với nhau qua một máy mà bạn không kiểm soát.

Peer-relay trong tailscale status nghĩa là gì?

Peer relay là một máy trong tailnet của bạn, dùng để chuyển tiếp traffic cho các thành viên khác khi không thể thiết lập kết nối trực tiếp. Máy này lắng nghe trên một cổng UDP do bạn chọn, và daemon sẽ ưu tiên dùng nó thay vì DERP. tailscale status đánh dấu một kết nối như vậy peer-relay, còn tailscale ping hiển thị endpoint của 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

Hãy đọc kỹ kết quả này. Đây vẫn không phải là kết nối trực tiếp, nên lần chạy vẫn kết thúc bằng direct connection not established. Điểm thay đổi là máy nào thực hiện việc chuyển tiếp. Một VPS có địa chỉ IP public và băng thông được cấp rộng rãi phù hợp làm relay cho traffic của bạn hơn nhiều so với một node DERP dùng chung. Vì vậy, tính năng này đáng chú ý với bất kỳ ai thuê server. Hãy bật tính năng này trên máy có public endpoint ổn định:

sudo tailscale set --relay-server-port=40000

Cổng 0 sẽ chọn một cổng chưa được sử dụng một cách ngẫu nhiên. Chuỗi rỗng sẽ tắt relay server. Sau đó, cấp quyền cho các thiết bị client sử dụng relay này bằng capability tailscale.com/cap/relay trong file policy của tailnet:

{
  "grants": [
    {
      "src": ["tag:us-east-vpc"],
      "dst": ["tag:us-east-relays"],
      "app": {
        "tailscale.com/cap/relay": []
      }
    }
  ]
}

Cả thiết bị relay và các thiết bị client đều cần Tailscale 1.86 trở lên. Vì vậy, hãy kiểm tra bằng tailscale version trên từng thiết bị trước khi mất cả giờ chỉnh file policy. Bạn nên ghi nhớ thứ tự daemon thử các phương án. Trước tiên, daemon thử kết nối trực tiếp. Nếu kết nối này thất bại, daemon tìm một peer relay mà nó được phép sử dụng. Nếu không có peer relay, daemon chuyển sang DERP. DERP không bao giờ hoàn toàn bị loại khỏi quá trình này, vì đây cũng là kênh để hai máy thương lượng kết nối ngay từ đầu.

Nguyên nhân 1: firewall egress chặn UDP

Tailscale nêu 2 nguyên nhân khiến kết nối tiếp tục đi qua relay. Nguyên nhân đầu tiên là UDP bị chặn. Hãy kiểm tra trực tiếp trên máy:

tailscale netcheck

Phần report ở đây đã được rút gọn. Trường đầu tiên là trường quyết định mọi thứ:

Report:
  * UDP: true
  * IPv4: yes, 203.0.113.9:41641
  * IPv6: no
  * MappingVariesByDestIP: false
  * PortMapping:
  * Nearest DERP: Dallas

UDP: false là dấu hiệu rõ nhất bạn sẽ thấy. Điều đó có nghĩa là máy không thể gửi gói UDP ra các probe server của Tailscale. Tài liệu CLI của Tailscale cũng nêu rằng trong trường hợp này, “khả năng Tailscale thiết lập được kết nối point-to-point là thấp”. Khi đó, traffic sẽ đi qua DERP bằng TCP port 443. Cơ chế fallback này khiến máy vẫn có vẻ hoàn toàn bình thường: máy vẫn connected, vẫn reachable, và mọi byte đều được relay. netcheck mô tả network thay vì một kết nối cụ thể. Vì vậy, hãy xác nhận peer thực sự đang dùng đường nào bằng tailscale status hoặc tailscale ping trước khi thay đổi rule.

Tài liệu ghi nhận 2 outbound rule. “Cho phép các thiết bị nội bộ khởi tạo UDP từ :41641 đến *:*”, đây là traffic WireGuard, và “Cho phép các thiết bị nội bộ khởi tạo UDP đến *:3478”, đây là STUN (session traversal utilities for NAT), protocol mà máy dùng để xác định public address và port của chính nó. Hãy dùng wildcard cho destination. Tailscale sẽ bổ sung relay server theo thời gian, nên danh sách address viết thủ công sẽ trở nên không đầy đủ trong vòng 1 năm.

Trên server thuê, thủ phạm thường là egress policy quá chặt. Policy này có thể có sẵn trong hardened image hoặc do provider áp dụng ở upstream. Trước tiên, hãy kiểm tra outgoing policy mặc định:

sudo ufw status verbose
sudo nft list ruleset

Default: deny (incoming), allow (outgoing) là đúng và không phải nguyên nhân gây lỗi. Outgoing default là deny, chỉ allow một danh sách ngắn gồm TCP 443 và DNS, chính là kiểu cấu hình khiến server luôn dùng relay. DERP qua TCP 443 đi được qua rule này, còn đường direct thì không. Các rule đó thực sự nằm ở đâu còn tùy máy đang chạy iptables hay nftables bên dưới, và chỉnh nhầm firewall là cách phổ biến khiến thay đổi không có tác dụng.

Chiều inbound cũng quan trọng, vì VPS có public IP address nên có thể trở thành phía dễ kết nối hơn trong cặp peer. Nếu firewall cho phép inbound UDP trên port mà tailscaled đang listen, các peer phía sau home router khó kết nối vẫn có thể reach đến VPS mà không cần thủ thuật đặc biệt. Hãy tìm port thực sự đang được sử dụng:

sudo ss -lunp | grep tailscaled
sudo ufw allow 41641/udp

41641 là port static mặc định. Tailnet bật setting randomizeClientPort sẽ khiến client chọn port random. Khi đó, hãy lấy số port thực tế từ output của ss thay vì dùng giá trị trên trang này. Tiếp theo, kiểm tra control panel của provider. Hầu hết host có network firewall tách biệt với firewall bên trong server, và rule bạn thêm bằng ufw sẽ không có tác dụng với firewall đó.

Nguyên nhân 2: NAT cứng ở một hoặc cả hai đầu

Nguyên nhân được ghi nhận thứ hai là NAT cứng. NAT (network address translation) là cơ chế router dùng để chuyển địa chỉ private của bạn thành địa chỉ public. Một router thân thiện giữ nguyên public port cho một socket nội bộ nhất định, bất kể bạn đang kết nối với ai. Cơ chế này được gọi là endpoint-independent mapping. NAT cứng cấp một public port khác nhau cho từng đích đến. Vì vậy, địa chỉ mà máy nhận được từ STUN server không phải là địa chỉ mà peer có thể dùng. Tailscale báo trạng thái này trong netcheck dưới dạng MappingVariesByDestIP: true.

Một đầu dùng NAT cứng vẫn có thể hoạt động. Nếu đầu bên kia có public endpoint ổn định, máy phía sau NAT cứng vẫn có thể kết nối vào và thiết lập được đường truyền. Hai đầu cùng dùng NAT cứng là trường hợp bị lỗi, vì không đầu nào dự đoán được port mà đầu kia sẽ xuất hiện.

Trên VPS có địa chỉ public IPv4, trường này bình thường phải hiển thị false. Địa chỉ public không phải lúc nào cũng có nghĩa là không có lớp NAT nào chuyển đổi nó. Ví dụ, AWS ghi rõ internet gateway của họ thực hiện NAT một-một giữa địa chỉ public IPv4 của instance và địa chỉ private của instance. Mapping một-một tĩnh như vậy giữ nguyên port bất kể bạn đang kết nối với ai, nên đó không phải NAT cứng và trường này vẫn hiển thị false. Nếu trường này hiển thị true trên server bạn thuê, một thành phần nào đó trong network của nhà cung cấp đang cấp port khác nhau cho từng đích đến. Không có firewall rule nào bên trong máy có thể thay đổi điều đó. Bạn có thể đặt peer relay trên một máy có public endpoint sạch, hoặc chuyển workload sang nơi khác. Đây cũng là trường hợp việc quảng bá các private range từ subnet router phát huy tác dụng, vì khi đó bạn chỉ cần một đường truyền tốt vào network thay vì một đường truyền tốt đến từng thiết bị trong network.

Vì sao exit node khiến Tailscale có vẻ chậm hơn thực tế

Exit node là một hop thứ hai, nên người dùng thường đổ lỗi cho tunnel. Khi chọn một exit node, request rời khỏi laptop, đi qua tunnel đến VPS, rời VPS để vào Internet công cộng, rồi response quay lại theo cùng đường đó. Ngay cả khi kết nối trực tiếp đến VPS hoàn toàn ổn định, tổng tốc độ cũng không thể cao hơn uplink của chính VPS. Khoảng cách tăng thêm sẽ thể hiện trong thời gian tải của mọi trang.

Hãy đo riêng hai phần. Tắt exit node, rồi kiểm tra riêng tunnel đến địa chỉ tailnet của VPS:

sudo tailscale set --exit-node=
sudo apt install -y iperf3
iperf3 -s

Chạy iperf3 -c 100.113.160.82 từ client đến địa chỉ tailnet đó. Kết quả này là tốc độ tunnel. Bây giờ bật lại exit node bằng sudo tailscale set --exit-node=100.113.160.82, rồi chạy speed test thông thường đến Internet công cộng. Kết quả này là tốc độ của tunnel cộng với uplink của VPS. Nếu kết quả đầu tốt nhưng kết quả sau kém, Tailscale không phải vấn đề. Hãy kiểm tra network và sizing của chính exit node. tailscale exit-node list hiển thị các lựa chọn nếu bạn không chắc mình đã chọn node nào.

CPU là yếu tố còn lại giới hạn tốc độ của exit node. Theo hướng dẫn của Tailscale, nên ưu tiên CPU thuộc thế hệ mới với clock speed cao hơn thay vì nhiều core hơn. Vì vậy, plan có nhiều vCPU hơn không tự động nhanh hơn trong trường hợp này. Trên shared host đang bận, CPU được cam kết không phải lúc nào cũng là CPU thực tế bạn nhận được. Steal time từ noisy neighbour sẽ biểu hiện thành throughput thay đổi theo từng giờ dù phía bạn không có thay đổi.

Một thiết lập tinh chỉnh duy nhất: rx-udp-gro-forwarding

Tailscale tài liệu hóa một thiết lập Linux duy nhất. Thiết lập này áp dụng cho các máy chuyển tiếp lưu lượng, tức exit node và subnet router. Client thông thường không hưởng lợi. Tính năng này cần Tailscale 1.54 trở lên và Linux kernel 6.2 trở lên, vì vậy hãy xác nhận cả hai trước khi thay đổi:

tailscale version
uname -r

Sau khi đã đáp ứng các điều kiện đó, hãy bật tính năng chuyển tiếp UDP GRO (generic receive offload) trên interface hướng ra Internet:

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

Kiểm tra xem thiết lập đã có hiệu lực chưa:

ethtool -k $NETDEV | grep -E 'rx-udp-gro-forwarding|rx-gro-list'

Bạn phải thấy rx-udp-gro-forwarding: on và rx-gro-list: off. Thiết lập này hữu ích vì traffic của Tailscale sử dụng UDP. Khi kernel giữ các UDP packet nhỏ ở dạng đã được coalesced trong suốt đường chuyển tiếp, daemon sẽ xử lý ít segment lớn hơn với cùng số byte. ethtool -K không được giữ lại sau khi reboot, vì vậy hãy lưu cấu hình này. Trên hệ thống sử dụng 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

Chạy script thủ công một lần và kiểm tra exit status có bằng 0 không. Forwarding node cũng phải bật IP forwarding ngay từ đầu. Đây là một thiết lập riêng và có thể gây ra lỗi riêng:

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

Interface tailscale0 của bạn đang dùng MTU bao nhiêu?

Đừng đoán giá trị này. Hãy đọc trực tiếp trên máy:

ip link show tailscale0

Giá trị mtu trong output đó là giá trị mà tunnel thực sự sử dụng. Giá trị này thấp hơn 1500 mà interface ethernet của bạn báo cáo. Đây là thiết kế có chủ đích, không phải lỗi. Mỗi packet bạn gửi bên trong tunnel đều được bọc thêm: IP header bên ngoài dài 20 byte với IPv4 hoặc 40 byte với IPv6, UDP header dài 8 byte, cùng phần framing và authentication tag của WireGuard dài 32 byte. Tất cả phải nằm trong giới hạn mà đường truyền thực tế có thể tải. Vì vậy, Tailscale chọn giá trị đủ thấp để hoạt động trên cả những đường truyền không tải được packet đủ 1500 byte. Trường hợp này gồm các kết nối PPPoE, một số mạng di động và tunnel IPv6.

Triệu chứng của lỗi MTU khá đặc trưng, nên không được chẩn đoán chỉ dựa vào việc kết nối chậm. SSH vẫn phản hồi, ping vẫn hoạt động, nhưng các lần truyền dữ liệu lớn hoặc các trang HTTPS lớn bị treo hoàn toàn thay vì chỉ chạy chậm. Mẫu này cho thấy packet quá lớn bị drop ở đâu đó, nhưng không có ICMP message quay về để báo cho bên gửi. Tăng MTU tailscale0 lên gần 1500 sẽ làm tình hình tệ hơn, vì các packet vốn đã không vừa sẽ còn lớn hơn. Cách xử lý đúng là tìm path MTU hoạt động bằng phương pháp chia đôi và giới hạn TCP MSS trên router chuyển tiếp lưu lượng. Loại kết nối không thay đổi điều này: path qua relay và path trực tiếp đều sử dụng cùng một MTU của interface.

FAQ

Làm thế nào để biết kết nối Tailscale của tôi là kết nối trực tiếp hay qua relay?

Chạy tailscale status và đọc phần cuối dòng của peer. direct 203.0.113.9:41641 là kết nối trực tiếp, relay "tor" nghĩa là mọi packet đều đi qua DERP server đó, còn peer-relay nghĩa là packet đi qua một máy trong tailnet của bạn. Để kiểm tra thêm, chạy tailscale ping <peer>: đường truyền hoạt động bình thường sẽ bắt đầu trên DERP, sau đó in ra một pong kèm địa chỉ và port thông thường; đường truyền qua relay sẽ in các pong DERP cho đến khi kết thúc bằng direct connection not established. Trước tiên hãy gửi một ít traffic đến peer, vì Tailscale chỉ tạo đường truyền khi có nhu cầu.

Tại sao VPS của tôi không bao giờ có kết nối trực tiếp?

Chạy tailscale netcheck trên VPS. Nếu lệnh in ra UDP: false, UDP outbound không đi ra được, thường là do egress firewall, và Tailscale cho biết khi đó kết nối point-to-point khó thiết lập. Xác nhận bằng tailscale status hoặc tailscale ping để kiểm tra peer có đang ở trên DERP hay không. DERP chạy qua TCP 443, nên máy vẫn hiển thị là đã kết nối. Cho phép UDP outbound từ port 41641 đến mọi đích và UDP outbound đến mọi đích trên port 3478. Kiểm tra cả network firewall của provider và firewall bên trong server, vì đây là 2 lớp kiểm soát riêng biệt; một rule ufw không tác động đến firewall của provider.

Kết nối Tailscale qua relay có kém an toàn hơn kết nối trực tiếp không?

Không. DERP server chuyển tiếp các packet WireGuard mà nó không thể giải mã, vì encryption key được tạo trên các thiết bị của bạn và không bao giờ rời khỏi những thiết bị đó. Relay làm tăng latency và giảm throughput, không làm giảm tính bảo mật của dữ liệu. Coordination server có quyền kiểm soát việc thiết bị nào biết về thiết bị nào. Sự tách biệt giữa key material và connection metadata là điều cần hiểu trước khi quyết định mức độ tự host.

Setting rx-udp-gro-forwarding có giúp mọi máy không?

Không. Tài liệu ghi rõ setting này dành cho các máy Linux forward traffic cho máy khác, gồm exit node và subnet router. Laptop hoặc server chỉ giao tiếp với các peer của chính nó sẽ không nhận được lợi ích nào. Setting này cũng yêu cầu Tailscale 1.54 trở lên và Linux kernel 6.2 trở lên. Vì vậy, trước tiên hãy kiểm tra tailscale version và uname -r. Đồng thời lưu ý rằng ethtool -K sẽ reset sau khi reboot nếu bạn không persist setting này.

Tailscale có chậm hơn WireGuard thuần không?

Cả 2 đều dùng WireGuard để mã hóa traffic. Tailscale bổ sung bước thiết lập kết nối mà với WireGuard thuần bạn phải tự cấu hình. Chính bước thiết lập này đôi khi khiến kết nối phải đi qua relay. WireGuard thuần không có relay để chuyển sang: nó kết nối trực tiếp hoặc hoàn toàn không kết nối được. Vì vậy, hãy so sánh các cấu hình tương đương và chỉ benchmark Tailscale khi tailscale status cho biết direct. Nếu muốn dùng bản cấu hình thủ công để so sánh, một WireGuard server tự dựng cần khoảng 40 dòng config. Phần đánh đổi của cách này được trình bày trong so sánh 2 phương pháp.