Эксплуатация нод

Exit-ноды в Xray: почему «Авто» моросит

Как устроена кнопка «Авто»: жёсткий список скрытых хостов, пара стратегия↔observatory и медленное плечо, из-за которого скачет задержка.

10 мин чтения Обновлено · Dele Deleev

Как устроена кнопка «Авто»

Когда в подписке есть локация «Авто / Оптимальный», раскидывающая пользователя по нескольким exit-нодам, — это не магия, а конкретный механизм: отдельный шаблон подписки с injectHosts плюс балансировщик xray. Навигатор «Авто» — обычный host, но с привязкой к шаблону-балансиру. В шаблоне ключ injectHosts[0].selector — жёсткий список UUID хостов (type: uuids), tagPrefix: proxy. Эти хосты инжектятся в клиентскую конфигурацию как outbounds proxy, proxy-2 и так далее; балансировщик выбирает между ними по селектору ["proxy"].

Важно: ноги балансира — ровно то, что перечислено в injectHosts, а не «все скрытые хосты автоматически».

Правила состава ног

  • Ноги должны быть скрытыми хостами (isHidden: true). Видимый UUID коллизит сам с собой — он уже присутствует в подписке как прямая локация — и панель молча выкидывает его из рендера плеч. Классический итог: настроено 4 плеча, у пользователей доезжают 2, явной ошибки нигде нет.
  • Один вход на ноду на порт. Второй вход той же ноды — только на другом порту (другой endpoint), иначе дубль.
  • Отдельный инбаунд для плеча не обязателен: плечо может жить на общем инбаунде с видимым хостом. Важно лишь, чтобы хост-плечо был скрытым и попадал в injectHosts.

Управление пулом — аддитивно и обратимо: снапшот шаблона → правка списка UUID (добавить/убрать элемент, не пересобирать) → PATCH → проверить в выдаче подписки, что число proxy-outbounds совпало с ожидаемым.

Пара «стратегия ↔ источник RTT»

Частая ошибка — стратегия без парного observatory-блока:

  • leastLoad ↔ burstObservatory (pingConfig);
  • leastPing ↔ observatory (probeUrl / subjectSelector).

Перепутать — получить балансировщик без источника RTT, который ведёт себя непредсказуемо. Меняя стратегию, всегда меняйте и парный блок.

Медленное плечо: почему «моросит»

Одиночные ноды могут быть здоровы — синтетика показывает UP по каждой, — а пользователи на «Авто» жалуются: задержка скачет, страницы то летают, то замирают. Причина в том, что observatory меряет RTT пробой от плеча до контрольной точки (например gen_204), а не сквозную задержку до реального выхода. Далёкое медленное плечо (условно ~500 мс против ~180 мс у остальных) балансировщик считает равноценным и льёт на него заметную долю коннектов — в разобранном кейсе до 38%. Пользователь видит то 180, то 500 мс. Это и есть «моросит».

Лечение — убрать медленное плечо из пула явно, не полагаясь на leastLoad. При желании оставить его отдельной ручной локацией. И держать инвариант в мониторинге: плечо с реальной задержкой выше порога — алерт, чтобы оно не вернулось в пул незамеченным. Однородность пула по латентности важнее его размера.

Входной инбаунд балансировщика — не фантом

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

Правило: перед сносом любого инбаунда сверяйте список хостов. Если на инбаунд ссылается хоть один хост (inbound.configProfileInboundUuid) — он отдаётся пользователю. Пугает имя — переименуйте в balancer-in, но не удаляйте.

Подводные камни

  • nodes[] у хоста — no-op без injectHosts. Клиент идёт по address, а не по nodes[].
  • Анти-рецепт: observatory + leastPing под нагрузкой. При 300–400 Мбит и выше observatory начинает прыгать между соседними по пингу нодами каждые ~10 секунд — внутри сессии рвётся контекст (cookies, гео-IP). К тому же ICMP-подобный пробинг не равен реальному TCP-handshake: сервер отвечает на ping, а xray на нём мог отвалиться. Для failover между exit-нодами надёжнее внешний nginx-stream: переключение ~3 секунды, реальный TCP-check, в логах видно, что переключилось.
  • Коллизия «видимый хост + его нога на одном reality-инбаунде». При fingerprint: chrome ClientHello детерминированный — две идентичные сессии на один ключ, гонка. Разводите видимый хост и ногу по разным инбаундам и ключам (например :443 vision и :8443 xhttp).
  • Список локальных direct-доменов может жить только в основном шаблоне. В балансир-шаблон его нужно перенести отдельно, иначе локальные сервисы поедут через VPN.

Вывод. Балансировщик exit-нод в Xray — это injectHosts (жёсткий список UUID скрытых хостов) плюс правильная пара стратегия/observatory. Главные грабли: положить в пул видимый хост (выпадет), медленную далёкую ногу (часть пользователей «моросит») или перепутать leastLoad с burstObservatory. Управляйте пулом аддитивно, со снапшотом, и проверяйте выдачу подписки после каждой правки.

Источники

Частые вопросы

Частые вопросы

Как устроена локация «Авто / Оптимальный» в подписке технически?
Это отдельный шаблон подписки с injectHosts плюс балансировщик xray. Ключ injectHosts[0].selector — жёсткий список UUID хостов типа uuids, который инжектится в клиент как outbounds proxy, proxy-2 и так далее.
Почему в пуле «Авто» настроено 4 плеча, а у пользователей доезжают только 2?
Плечи должны быть скрытыми хостами (isHidden: true). Если плечо — видимый хост, оно коллизирует само с собой, потому что уже присутствует в подписке как прямая локация, и панель молча выкидывает его из рендера плеч.
Какую пару стратегия/observatory нужно использовать в балансировщике?
leastLoad работает в паре с burstObservatory (pingConfig), а leastPing — с observatory (probeUrl/subjectSelector). Перепутанная пара даёт балансировщик без источника RTT, который ведёт себя непредсказуемо.
Почему на «Авто» задержка скачет, хотя все ноды показывают UP?
Observatory меряет RTT пробой до контрольной точки, а не сквозную задержку до реального выхода. Далёкое медленное плечо балансировщик считает равноценным остальным и льёт на него заметную долю коннектов, из-за чего задержка у пользователя скачет между низкими и высокими значениями.
Можно ли удалить инбаунд, который никто напрямую не слушает?
Не всегда — такой инбаунд может быть входной точкой балансировщика, к которой привязан хост-навигатор. Перед сносом нужно сверить список хостов: если на инбаунд ссылается хоть один хост, он отдаётся пользователю как целая локация.
Похожее

Ещё в базе знаний

Подключите рабочий VPN

Инструкции базы знаний написаны под ключи VPN PRO: ключ выдаёт Telegram-бот за минуту, работоспособность гарантируем. 3 дня бесплатно, без карты.

Гарантия работоспособности · оплата СБП, картой МИР или криптовалютой