Ядро XanMod на серверах Akonit
Скорость VPN упирается не только в канал провайдера. Между вашим устройством и сайтом, который вы открываете, стоит ядро Linux на нашем сервере — оно решает, как быстро разгонять поток, когда притормозить и что делать с потерянными пакетами. Мы заменили стоковое ядро Ubuntu на XanMod и здесь честно рассказываем, что это дало, а что нет.
Что такое XanMod
Это то же ядро Linux, что и в обычной Ubuntu, но пересобранное с набором доработок, которые в основную ветку ядра ещё не приняты или выключены по умолчанию. Проект ведёт Alexandre Frade с 2015 года и публикует готовые пакеты в собственный репозиторий.
Мы используем ветку LTS — самую консервативную из четырёх: она получает
только исправления и живёт до декабря 2028 года. Версия на наших серверах —
6.18.45-x64v3-xanmod1 против 6.8.0-111, который стоял раньше.
| Ветка | Что это | Берём ли |
|---|---|---|
| LTS 6.18.x | Долгая поддержка, только исправления | да |
| MAIN 7.1.x | Стабильная свежая ветка | нет |
| EDGE 7.2.x | Всё новое сразу, включая эксперименты | нет |
| RT | Ядро реального времени, для звука и станков | нет |
Как сервер решает, с какой скоростью вам слать
Сеть не сообщает отправителю, какой у неё запас. Ширину канала между нашим сервером и вашим провайдером никто не объявляет — её приходится угадывать, причём заново для каждого соединения и непрерывно, потому что она меняется каждую секунду. Алгоритм, который этим занят, называется управлением перегрузкой, и именно его версия — главное, что мы получили вместе с новым ядром.
Два способа угадывать
CUBIC — то, что стоит по умолчанию почти везде, — наращивает темп, пока пакеты не начнут теряться. Потеря для него и есть сигнал «перебор»: значит, упёрлись, надо сбросить скорость и наращивать снова. Схема рабочая, но у неё встроенный побочный эффект: чтобы узнать границу, CUBIC обязан регулярно её переходить — то есть намеренно переполнять буфер ближайшего маршрутизатора. Пока буфер полон, все пакеты в нём стоят в очереди, и задержка растёт у всех, кто через этот маршрутизатор идёт.
BBR вместо этого измеряет. Он постоянно оценивает две величины:
- BtlBw — максимальная скорость, с которой данные реально доезжают до вас (пропускная способность самого узкого места на пути);
- RTprop — минимальная задержка туда-обратно, то есть время пути без очередей, чистая физика и расстояние.
Их произведение — объём «трубы»: сколько байт помещается в пути одновременно. Цель BBR — держать в полёте примерно столько и отправлять ровно со скоростью BtlBw. Труба заполнена, очередь пуста, потери не нужны как сигнал.
BBR не выбрасывает пачку пакетов и не ждёт ответа — он раскладывает их во времени
равномерно, с рассчитанным интервалом. Это называется pacing, и делает его планировщик очередей
в ядре. На наших серверах для этого стоит fq, а на исходящем интерфейсе поверх него
работает шейпер CAKE. Запустить BBR без пейсинга технически можно, но тогда он превращается
в обычный залповый отправитель и половина смысла теряется.
Чем плоха первая версия BBR
Именно она стоит в обычном ядре Ubuntu, и именно с неё мы ушли. Четыре её свойства объясняют наши замеры лучше любых слов:
- Она слепа к потерям. Пакеты теряются, а алгоритм этого сигнала не использует вовсе: он ждёт, пока просядет измеренная пропускная способность. На путях с мелкими буферами — а транзит в Россию именно такой — это означает устойчивый фон потерь, который создаёт сам отправитель.
- Держит в полёте до двух объёмов трубы. Коэффициент запаса равен 2, то есть половина отправленного при неудачном стечении стоит в очереди, а не летит.
- Раз в 10 секунд роняет темп почти до нуля. Чтобы заново измерить чистую задержку, нужен момент, когда очередь пуста, — и первая версия для этого оставляет в полёте четыре пакета на 200 мс. Для одного потока это видимая яма в скорости.
- Конфликтует с соседями. В глубоких буферах отбирает канал у соединений на CUBIC, в мелких — топит потерями и себя, и их.
Что чинит третья версия
BBRv3 — это переработанная вторая версия плюс исправления, которые Google внёс после эксплуатации у себя. Сейчас это алгоритм по умолчанию для всего публичного трафика google.com и YouTube, то есть обкатан на планетарном масштабе.
| Что | BBR v1 (стоковое ядро) | BBRv3 (XanMod) |
|---|---|---|
| Реакция на потери | нет | есть, целевой потолок 2% |
| Реакция на ECN | нет | по-DCTCP, с мелким порогом |
| Предел данных в полёте | до 2× объёма трубы | границы inflight_hi / inflight_lo и запас 15% сверху не занимается |
| Замер чистой задержки | 4 пакета на 200 мс, раз в 10 с | около половины трубы, раз в 5 с |
| Выход из разгона | когда скорость перестала расти | то же плюс по потерям и ECN |
| Какой RTT считается сигналом | весь путь туда-обратно | только сетевая часть, без задержки на стороне получателя |
| Цикл прощупывания канала | 8 фаз с фиксированными коэффициентами | состояния DOWN → CRUISE → REFILL → UP; сброс мягче (0.75 → 0.9), рывок вверх сильнее (2.0 → 2.25) |
Читать таблицу стоит через одну мысль: третья версия перестала быть «слепой, но вежливой». Она по-прежнему строит модель канала вместо того, чтобы искать предел столкновением, но теперь у неё есть и аварийные сигналы — потери и ECN, — и явно оставленный запас, и щадящий способ перемерить задержку. Отсюда наш главный результат: данных проходит столько же или больше, а повторных отправок вдвое меньше.
Почему это особенно заметно именно на VPN
Внутри туннеля потеря стоит дороже, чем в обычном соединении, и на то две причины.
- Потерянный пакет тормозит чужой поток. Наш пакет несёт внутри себя ваш трафик — кусок TLS-сессии, видеозвонка, игрового протокола. Пока внешний пакет переотправляется, внутренний поток стоит: данные пришли не по порядку, и разобрать их нельзя, пока не приедет пропущенный. Одна потеря на нашей стороне превращается в паузу у вас.
- Очередь общая для всех на сервере. Если один жадный поток набил буфер на канале сервера, растёт задержка у каждого, кто в этот момент через него идёт. Алгоритм, который не строит очередь, — это не только его скорость, но и чужой пинг.
Независимые исследования BBRv3 показывают и обратную сторону: в некоторых конфигурациях он всё ещё забирает больше канала, чем соседние соединения на CUBIC, а потоки с разной задержкой делят канал неравномерно. Мы выбирали алгоритм по своим замерам на своих маршрутах, а не по обещаниям — и на них выигрыш есть.
Что ещё внутри этого ядра
BBRv3 — причина, по которой мы вообще пошли менять ядро. Но вместе с ним приезжает и остальное.
| Что | Что это значит |
|---|---|
| Сборка с LTO | Компилятор оптимизирует ядро как единое целое, а не по файлам: видит вызовы между модулями и может их встроить. Тот же исходный код, более плотный машинный. |
| Сборка под x86-64-v3 | Отдельные пакеты под поколения процессоров. Наши серверы на современных Ryzen, поэтому берём вариант, где компилятору разрешены AVX2, BMI2 и FMA — инструкции, которых нет в универсальной сборке для машин 2003 года. |
| MGLRU | Переработанное вытеснение памяти: страницы разложены по «поколениям» вместо двух списков, и ядро реже перебирает их целиком, решая, что выгрузить. В основном ядре это есть, но по умолчанию выключено. |
| Патч Cloudflare | Ограничивает время, которое ядро тратит на уплотнение переполненной очереди приёма. Именно этот участок кода даёт всплески загрузки процессора на серверах с тысячами одновременных соединений — то есть на наших. |
| PREEMPT_DYNAMIC | Режим вытеснения задач выбирается на загрузке, а не намертво при сборке. Ядро может прервать само себя ради более срочной работы — для сервера, который обрабатывает пакеты, это про равномерность задержки. |
| sched_ext | Возможность подключать планировщики задач без пересборки ядра. Мы этим не пользуемся, но именно из-за таких вещей ветку LTS мы предпочитаем экспериментальной. |
| Полтора года стека | Между 6.8 и 6.18 — исправления в TCP, драйверах виртуальных сетевых карт и обработке прерываний. Это не «фича», но именно из таких мелочей складывается разница, которую мы измеряем. |
Что мы измерили
Сначала на резервном сервере, которым никто не пользуется, — чтобы поломка ничего не стоила. Потом на боевых, под живой нагрузкой. Ниже оба замера, начиная с того, который важнее.
На боевых серверах
Считаем долю сегментов, которые пришлось отправлять повторно, — этот счётчик ведётся непрерывно на каждом сервере. Сравниваем со средним за сутки до переключения: брать ближайшие два часа нельзя, они попадают то на пик, то на затишье и врут в обе стороны.
| Сервер | Что на нём у людей | Повторных отправок до | После | Разница |
|---|---|---|---|---|
| Хармоник 🇳🇱 | VLESS, ~100 человек | 1.610% | 0.727% | −55% |
| Хёллентэлер 🇩🇪 | VLESS, ~195 человек | 1.826% | 1.282% | −30% |
| Оркан 🇳🇱 | Игровой, Hysteria2 | 0.125% | 0.116% | −7% |
| Яук 🇩🇪 | Игровой, Hysteria2 | 2.079% | 2.054% | −1% |
Проценты легко подогнать, поэтому вот те же данные в штуках. Хёллентэлер — самая загруженная машина из четырёх:
до переключения: 24 020 сегментов/с 510 повторных отправок/с 92.4 Мбит/с 200 человек
после: 24 777 сегментов/с 318 повторных отправок/с 89.8 Мбит/с 195 человек
Сегментов сервер отправляет на 3% больше, а переотправляет на 192 штуки в секунду меньше. Отговорка «просто стало тише» здесь не работает: нагрузка после перезагрузки вернулась к прежней, а у Хёллентэлера даже выросла.
Это не осечка замера, а ожидаемый результат. Их пользователи работают по Hysteria2 поверх UDP, а счётчик считает TCP — до этого трафика новый алгоритм просто не достаёт. Подробнее об этом в разделе «Чего XanMod не делает».
На стенде
Отдельный резервный сервер, никакой посторонней нагрузки. Одна и та же машина, одна цель, один скрипт, восемь прогонов по 10 секунд на каждое ядро, плечо 33 мс. После замера сервер возвращали на старое ядро и повторяли — чтобы отделить эффект ядра от состояния канала.
| Показатель | Стоковое 6.8 · BBR v1 | XanMod 6.18 · BBRv3 | Разница |
|---|---|---|---|
| Скорость, медиана | 514 Мбит/с | 550–575 Мбит/с | +7…12% |
| Потери на гигабит | 0.0203 | 0.0091–0.0111 | −45…−55% |
| Задержка под нагрузкой | 41.9 мс | 40.3–40.9 мс | без изменений |
| Прогонов вообще без потерь | 1 из 8 | 6 из 16 | ×3 |
Стенд и боевые серверы сошлись: −45…−55% в лаборатории и −30…−55% под живой нагрузкой. Совпадение лабораторного замера с боевым — редкость, и здесь оно есть.
А вот прирост скорости надёжным назвать нельзя. Он в ту же сторону, но слабый: вероятность, что случайный прогон на XanMod быстрее случайного прогона на стоковом ядре, — 0.79. Направление есть, утверждения «на 12% быстрее» нет.
Потерянный пакет — это не «чуть медленнее». Это пауза, пока отправитель поймёт, что пакет пропал, и пришлёт заново. Именно из таких пауз складываются рывки в видеозвонке, подвисания в игре и «сайт думает». Пиковая скорость в мегабитах на это влияет куда меньше.
Задержка не выросла — и это часть результата
Ускориться можно нечестно: набить очередь на маршрутизаторе и получить хорошую цифру в тесте скорости ценой выросшего пинга. Мы это проверяли отдельно: задержка под полной нагрузкой осталась прежней. Значит выигрыш не куплен раздуванием очереди.
Чего XanMod не делает
Это самая важная часть статьи. Ядро — не универсальное ускорение, и вот три вещи, на которые оно не влияет вообще.
Hysteria2 работает поверх QUIC, а он управляет перегрузкой сам, внутри программы, минуя ядро. На таких серверах BBRv3 не задействован — им достаётся только общее оздоровление стека.
Если провайдер ограничил порт сервера 20 Мбит/с, никакое ядро не сделает из него 200. Такие машины лечатся сменой тарифа или переносом людей, а не пересборкой ядра.
Шифрование и обработка пакетов упираются в ядра процессора. Сервер на 2 ядрах отдаёт примерно 250 Мбит/с и начинает деградировать при загрузке 75–80% — с XanMod эта граница сдвигается едва заметно.
Чем мы за это платим
| Минус | Что это значит на практике |
|---|---|
| Сторонний репозиторий | Обновления ядра приходят не от Ubuntu, а от проекта XanMod. Исправления безопасности там выходят быстро, но ответственность за них уже не на дистрибутиве. |
| Риск не загрузиться | Новое ядро может не подняться на конкретном железе. Лечится заранее — см. ниже. |
| Пропадает CUBIC | В XanMod список доступных алгоритмов — reno bbr. Софт, который явно
просит cubic, получит отказ. |
| Дольше загрузка | Машина возвращается за 3–4 минуты вместо одной, и в это время SSH принимает подключение, но не отвечает. Выглядит как зависшая — на деле сервер уже работает. |
Как мы это ставим
Порядок один и тот же для каждой машины, и он построен так, чтобы неудача не требовала физического доступа к серверу.
- Проверка совместимости. Ubuntu 24.04, виртуализация KVM (в контейнере ядро не меняется вовсе), поддержка набора инструкций x86-64-v3.
- Установка пакета из репозитория XanMod. Старые ядра остаются на диске.
- Проверка набора модулей нового ядра: фильтрация трафика, WireGuard, шейпер CAKE, BBR. Если хоть одного нет — установка останавливается, ребута не будет.
- Одноразовая загрузка. В загрузчике по умолчанию остаётся старое ядро, а новое ставится «на один раз».
- Перезагрузка и приёмка по списку: все службы, туннели, порты, правила фильтрации, шейпер, возврат пользователей.
Если новое ядро не поднимется, следующая перезагрузка вернёт сервер на старое автоматически, без консоли и без обращения к хостеру. Пока машина не отстоялась, по умолчанию грузится проверенное ядро — новое включается только на текущую загрузку.
Закрепление: ядро не слетает при перезагрузке
Одноразовая загрузка хороша ровно один раз: после неё сервер вернулся бы на старое ядро при первой же перезагрузке. Поэтому после проверки — вернулась ли машина, поднялись ли Xray, агент и туннель — новое ядро закрепляется загрузочным по умолчанию. Порядок именно такой: сначала доказательство, что ядро рабочее, потом закрепление. Не вернулась или служба не встала — закрепление не делается, и сервер остаётся на старом ядре.
Новые серверы получают ядро сразу
Установка встроена в сценарий развёртывания ноды и идёт до остальной настройки — чтобы перезагрузка пришлась на пустую машину, а не на сервер с людьми. Сценарий сам определяет поколение процессора, ставит нужную сборку, проверяет набор модулей и закрепляет ядро. Отдельного действия руками не требуется, и новая нода изначально работает на BBRv3.
Пункт меню загрузчика, лежащий внутри подменю, задаётся полным путём
<подменю>><пункт>. С коротким именем загрузчик молча берёт
первый пункт меню — то есть самое новое ядро. Страховка при этом выглядит настроенной,
но не работает. Проверять надо не конфигурацию, а фактическую версию после перезагрузки.
Откат
Одной перезагрузки достаточно: сервер вернётся на стоковое ядро, потому что оно и остаётся выбранным по умолчанию. Полное удаление — снятие пакета и репозитория, тоже без переустановки системы.
Коротко для тех, кто просто пользуется VPN
- Мне надо что-то делать? Нет. Это изменение на нашей стороне, приложение и подписка не меняются.
- Станет быстрее? Скорее стабильнее, чем быстрее. Меньше потерь — меньше рывков в звонках и подвисаний при загрузке страниц.
- А если сервер был недоступен пару минут? Это перезагрузка при смене ядра. Приложение переключается на другой сервер из вашей подписки само.
- На игровых серверах разница будет? На самом протоколе — нет, он управляет скоростью сам. Общая стабильность машины — да.