SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor

Почему Tailscale работает медленно: прямое соединение

Узнайте, как проверить тип соединения через tailscale status. Мы разберем, почему ретрансляция DERP снижает скорость и как исправить блокировки UDP для прямого доступа.

Почему Tailscale работает медленно: ретрансляция вместо прямого соединения

Tailscale работает медленно, когда соединение проходит через один из общих серверов DERP, и приближается к линейной скорости при прямом соединении. Прямое соединение передает зашифрованные пакеты WireGuard напрямую от одной машины к другой, поэтому скорость ограничена только пропускной способностью интернет-каналов обоих узлов. Ретранслируемое соединение сначала отправляет каждый пакет через третью машину, поэтому оно наследует задержки этой машины и ту долю её пропускной способности, которую вы получаете. Стоимость такой передачи зависит от ретранслятора. Tailscale называет свои серверы DERP «обычно более медленными, чем прямые соединения», в то время как собственный ретранслятор узлов «обычно быстрее, чем DERP», и, согласно ранним тестам Tailscale, достигает пропускной способности, «близкой к прямому соединению». На странице производительности Tailscale общее правило сформулировано одной строкой: «Прямые соединения почти всегда обеспечивают меньшую задержку и более высокую пропускную способность».

Внутри вашего приложения разница никак не отображается. Копирование файлов просто идет медленно, SSH-сессия просто тормозит. Поэтому первая задача — выяснить, какой тип соединения у вас установлен. Две команды позволяют узнать это менее чем за минуту, а всё остальное — это устранение причин. Стоит разобраться в устройстве системы до начала работы, поскольку координационный сервер и плоскость данных WireGuard — это отдельные системы, и только плоскость данных передает ваши байты.

Две команды для определения прямого или ретранслируемого соединения

Отправьте немного трафика узлу, прежде чем проводить измерения. Tailscale выстраивает путь по запросу, поэтому для узла, с которым вы сегодня не общались, соединение могло быть ещё не согласовано, и вы получите устаревшие данные. Достаточно одного ping или одного curl на tailnet-адрес узла.

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 и оцените разброс задержки. Ретранслируемый путь обычно показывает как более высокие значения, так и большую вариативность, поскольку он состоит из двух интернет-каналов, соединенных на машине, которую вы не контролируете.

Что означает peer-relay в выводе tailscale status?

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. Изменился лишь узел, выполняющий ретрансляцию. VPS с публичным IP-адресом и широким каналом связи гораздо лучше подходит для передачи вашего трафика, чем общий узел DERP, поэтому это важно для всех, кто арендует сервер. Включите эту функцию на машине с доступным публичным адресом:

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

Порт 0 выбирает случайный свободный порт, а пустая строка отключает сервер ретрансляции. Затем предоставьте клиентским устройствам разрешение на его использование с помощью возможности tailscale.com/cap/relay в файле политики вашей сети tailnet:

{
  "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 — это самый важный индикатор. Он означает, что машина не смогла отправить UDP-пакет на серверы проверки Tailscale. Согласно справочнику CLI Tailscale, в этом случае «маловероятно, что Tailscale сможет установить прямое соединение». Трафик в таком случае передается через DERP по TCP-порту 443. Именно из-за этого резервного пути машина выглядит полностью работоспособной: она подключена, доступна, а каждый байт передается через ретранслятор. netcheck описывает состояние сети в целом, а не конкретного соединения, поэтому перед изменением правил подтвердите путь, который фактически использует узел, с помощью tailscale status или tailscale ping.

Документация предписывает два исходящих правила. «Разрешите внутренним устройствам инициировать UDP-трафик с :41641 на *:*» — это сам трафик WireGuard, и «Разрешите внутренним устройствам инициировать UDP-трафик на *:3478» — это STUN (session traversal utilities for NAT), протокол, который машина использует для определения своего публичного адреса и порта. Используйте подстановочные знаки (wildcards) для адресов назначения. Tailscale со временем добавляет новые серверы ретрансляции, поэтому список адресов, составленный вручную, устареет в течение года.

На арендованном сервере типичной причиной является агрессивная политика исходящего трафика — либо унаследованная в составе защищенного образа, либо применяемая провайдером на вышестоящем уровне. Сначала проверьте политику исходящего трафика по умолчанию:

sudo ufw status verbose
sudo nft list ruleset

Default: deny (incoming), allow (outgoing) — это нормальное состояние, проблема не в нем. Исходящая политика по умолчанию deny с коротким списком разрешенных портов (TCP 443 и DNS) — это именно та конфигурация, из-за которой сервер всегда будет использовать ретрансляцию, так как путь DERP через TCP 443 проходит через это ограничение, а прямой путь — нет. Место, где фактически прописаны эти правила, зависит от того, использует ли система iptables или nftables, и редактирование не того инструмента — частая причина отсутствия изменений.

Входящие соединения также важны, так как VPS имеет публичный IP-адрес и может выступать в роли «простой» стороны соединения. Если файрвол разрешает входящий UDP-трафик на порту, который прослушивает tailscaled, узлы за сложными домашними роутерами смогут подключиться к нему без дополнительных ухищрений. Узнайте порт, который используется в данный момент:

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

41641 — это стандартный статический порт. Если в tailnet включена настройка randomizeClientPort, клиенты выбирают случайный порт; в этом случае возьмите реальное число из вывода ss, а не из этой страницы. Затем проверьте панель управления вашего провайдера. Большинство хостеров используют сетевой файрвол, отдельный от того, что работает внутри сервера, и правило, добавленное вами через ufw, никак на него не влияет.

Причина 2: жесткий NAT на одном или обоих концах

Вторая задокументированная причина — жесткий NAT. NAT (преобразование сетевых адресов) — это механизм маршрутизатора, который заменяет ваш частный адрес на публичный. «Дружелюбный» маршрутизатор сохраняет один и тот же публичный порт для заданного внутреннего сокета независимо от того, с кем вы общаетесь; это называется отображением, не зависящим от конечной точки (endpoint independent mapping). Жесткий NAT назначает разные публичные порты для разных направлений, поэтому адрес, который машина получила от STUN-сервера, не будет тем адресом, который сможет использовать другой узел. Tailscale сообщает об этом в netcheck как MappingVariesByDestIP: true.

Один жесткий NAT преодолим. Если у другой стороны есть стабильная публичная конечная точка, машина за жестким NAT все равно может установить соединение, и путь будет сформирован. Два жестких NAT одновременно — это случай, при котором соединение не устанавливается, так как ни одна из сторон не может предсказать порт, на котором появится другая.

На VPS с публичным IPv4-адресом это поле обычно должно содержать false. Публичный адрес не всегда означает отсутствие трансляции: например, AWS указывает, что ее интернет-шлюз выполняет NAT «один к одному» между публичным IPv4-адресом инстанса и его частным адресом. Такое статическое отображение сохраняет один и тот же порт при общении с любым узлом, поэтому это не жесткий NAT, и в поле по-прежнему будет false. Если на арендованном сервере отображается true, значит, сетевое оборудование провайдера назначает разные порты для разных направлений, и никакие правила межсетевого экрана внутри машины это не исправят. Ваши варианты: разместить ретранслятор (peer relay) на машине, имеющей «чистую» публичную конечную точку, или перенести рабочую нагрузку. Это также тот случай, когда анонсирование ваших частных диапазонов через subnet router дает преимущество, поскольку вам потребуется один надежный путь в сеть, а не надежный путь к каждому устройству в ней.

Почему при использовании exit node Tailscale кажется медленнее, чем есть на самом деле

Exit node — это второй узел в цепочке, и пользователи часто ошибочно винят в задержках сам туннель. При включенной exit node запрос уходит с вашего ноутбука, проходит через туннель до VPS, выходит с VPS в публичный интернет, а ответ возвращается тем же путем. Даже при идеально прямом соединении до этого VPS общая скорость не может превышать пропускную способность канала самого VPS, а дополнительное расстояние сказывается на времени загрузки каждой страницы.

Измеряйте эти два участка по отдельности. Отключите exit node и протестируйте туннель напрямую, обращаясь к tailnet-адресу вашего VPS:

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

Запустите iperf3 -c 100.113.160.82 с клиента, указав этот tailnet-адрес. Полученное значение — это скорость вашего туннеля. Теперь включите exit node обратно с помощью sudo tailscale set --exit-node=100.113.160.82 и запустите обычный тест скорости до публичного интернета. Это число будет отражать скорость туннеля плюс пропускную способность канала VPS. Если первое значение в норме, а второе низкое, проблема не в Tailscale, и искать причину нужно в сети и параметрах самой exit node. Команда tailscale exit-node list покажет доступные узлы, если вы не уверены, какой именно узел выбрали.

Вторая причина ограничения скорости exit node — процессор. Рекомендация Tailscale заключается в том, чтобы отдавать предпочтение современным поколениям CPU с более высокой тактовой частотой, а не большему количеству ядер, поэтому тариф с большим числом vCPU не всегда будет быстрее. На загруженном общем хосте выделенные вам ресурсы CPU могут быть недоступны в полном объеме, и steal time от «шумных соседей» проявляется как изменение пропускной способности в течение дня, даже если с вашей стороны ничего не менялось.

Единственный параметр настройки: rx-udp-gro-forwarding

Tailscale описывает одну настройку Linux, которая применяется к машинам, пересылающим трафик, то есть к exit nodes и subnet routers. Обычный клиент не получит преимуществ. Требуется Tailscale версии 1.54 или новее и ядро Linux версии 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, и если позволить ядру сохранять небольшие 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.conf

Какое значение MTU использует ваш интерфейс tailscale0?

Перестаньте угадывать это число и посмотрите его прямо на машине:

ip link show tailscale0

Значение mtu в этом выводе — это то, что реально использует ваш туннель, и оно меньше 1500, которые сообщает ваш Ethernet-интерфейс. Это сделано намеренно, а не является ошибкой. Каждый пакет, который вы отправляете внутри туннеля, упаковывается: внешний IP-заголовок размером 20 байт для IPv4 или 40 для IPv6, UDP-заголовок размером 8 байт, а также фрейминг WireGuard и тег аутентификации размером 32 байта. Всё это должно поместиться в то, что может передать реальный канал связи. Tailscale выбирает значение, достаточно низкое для работы в сетях, которые пропускают пакеты меньше стандартных 1500 байт, включая PPPoE-соединения, некоторые мобильные сети и IPv6-туннели.

Симптом проблемы с MTU специфичен, поэтому не стоит диагностировать её только по медленной работе. SSH работает отзывчиво, ping выполняется успешно, а затем передача больших объемов данных или загрузка тяжелых HTTPS-страниц полностью зависает, а не просто замедляется. Такой паттерн означает, что пакеты избыточного размера где-то отбрасываются, а ICMP-сообщение, которое должно уведомить отправителя, не возвращается. Увеличение MTU tailscale0 до 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 pongs до завершения работы с direct connection not established. Сначала отправьте немного трафика на узел, так как Tailscale строит путь только по запросу.

Почему мой VPS никогда не получает прямое соединение?

Выполните tailscale netcheck на VPS. Если команда выводит UDP: false, значит, исходящий UDP-трафик блокируется — чаще всего из-за внешнего файрвола, и Tailscale считает прямое соединение маловероятным. Убедитесь с помощью tailscale status или tailscale ping, что узел использует DERP, который работает через TCP 443, поэтому машина продолжает отображаться как подключенная. Разрешите исходящий UDP-трафик с порта 41641 на любой адрес и исходящий UDP-трафик на любой адрес через порт 3478. Проверьте как сетевой файрвол провайдера, так и локальный файрвол на сервере, так как это разные уровни контроля, и правило ufw не влияет на настройки провайдера.

Является ли ретранслируемое соединение Tailscale менее безопасным, чем прямое?

Нет. Сервер DERP пересылает пакеты WireGuard, которые он не может расшифровать, так как ключи шифрования генерируются на ваших устройствах и никогда их не покидают. Цена ретрансляции — это задержка и пропускная способность, а не конфиденциальность. Сервер координации управляет лишь тем, какие устройства узнают друг о друге, и разделение между ключевым материалом и метаданными соединения стоит изучить, прежде чем решать, какую часть инфраструктуры вы хотите разместить самостоятельно.

Помогает ли настройка rx-udp-gro-forwarding всем машинам?

Нет. Она предназначена для Linux-машин, которые пересылают трафик для других узлов, то есть для exit nodes и subnet routers. Ноутбук или сервер, который общается только со своими узлами, не получит преимуществ. Также требуется Tailscale версии 1.54 или новее и ядро Linux 6.2 или новее, поэтому сначала проверьте tailscale version и uname -r. Помните, что ethtool -K сбрасывается после перезагрузки, если не настроить сохранение параметров.

Работает ли Tailscale медленнее, чем обычный WireGuard?

Оба решения используют WireGuard для шифрования трафика. Tailscale добавляет механизм настройки соединения, который в обычном WireGuard нужно выполнять вручную, и именно этот механизм иногда приводит к использованию ретранслятора. У обычного WireGuard нет ретранслятора: он либо подключается напрямую, либо не подключается вовсе. Сравнивайте аналогичные конфигурации и проводите тесты производительности Tailscale только тогда, когда tailscale status показывает direct. Если вы хотите сравнить с версией, настроенной вручную, собственный сервер WireGuard потребует около сорока строк конфигурации, а компромиссы этого подхода описаны в сравнении двух методов.