Оглавление
- Где на самом деле живёт задержка LLM-стрима
- Что BBR и fq действительно меняют в Linux
- Baseline начинается с identity: какой сокет, интерфейс и qdisc вы меняете
- Сначала докажите сетевую причину, а уже потом меняйте congestion control
- Снимите контрольный профиль CUBIC и не меняйте workload одновременно
- Включайте BBR и fq только на canary-узле
- Как читать ss и qdisc без ложной победы по одной метрике
- Rollback — это часть эксперимента, а не аварийная кнопка
- Когда BBR/fq не поможет — и может отвлечь от настоящей причины
- Production rollout: ценность не в sysctl, а в контроле сетевого пути
- Чек-лист перед rollout и главный вывод
Готовы перейти на современную серверную инфраструктуру?
В King Servers мы предлагаем серверы как на AMD EPYC, так и на Intel Xeon, с гибкими конфигурациями под любые задачи — от виртуализации и веб-хостинга до S3-хранилищ и кластеров хранения данных.
- S3-совместимое хранилище для резервных копий
- Панель управления, API, масштабируемость
- Поддержку 24/7 и помощь в выборе конфигурации
Результат регистрации
...
Создайте аккаунт
Быстрая регистрация для доступа к инфраструктуре
Где на самом деле живёт задержка LLM-стрима
У LLM API слово latency слишком легко превращается в один общий график. Для диагностики этого недостаточно. Минимум разделите путь на три наблюдаемых интервала: время до первого байта/токена, паузы между последующими чанками и полную длительность ответа. Первый интервал часто определяется очередью запросов, prefill, доступностью GPU, cold start модели или работой gateway. Второй уже сильнее зависит от decode cadence, buffering и сетевой доставки. Третий смешивает всё сразу.
Практический пример: TTFT вырос с обычного уровня, а после первого токена поток идёт ровно. Это плохой кандидат для BBR: congestion control не сокращает очередь на GPU. Обратная картина — первый токен появляется вовремя, но далее клиент видит нерегулярные паузы, RTT под нагрузкой растёт, на sender-сокете видны retransmissions или очередь qdisc. Вот здесь сетевой слой уже заслуживает отдельного canary-теста.
Ещё одна типичная ловушка — настроить не тот хост. Если внешний TCP завершается на reverse proxy, ingress-controller или L7 gateway, именно он является sender для клиентского соединения. Изменение TCP congestion control на backend с vLLM не обязано повлиять на WAN-сегмент до пользователя. Для архитектуры gateway полезно сначала свериться с материалом про Private AI API, а для проблем на GPU — с разбором GPU throttling через DCGM и Prometheus.
Поэтому первый gate простой: вы должны назвать конкретный TCP sender, интерфейс, клиентский сегмент и пользовательскую метрику, которую собираетесь улучшить. Если этого нет, смена алгоритма — тюнинг вслепую.

Что BBR и fq действительно меняют в Linux
BBR — congestion control для TCP, который опирается на модель пропускной способности пути и RTT и управляет тем, сколько данных и с какой скоростью отправлять. В Linux алгоритм для новых соединений выбирается через net.ipv4.tcp_congestion_control; список уже зарегистрированных вариантов виден в net.ipv4.tcp_available_congestion_control. Документация ядра подчёркивает важную деталь: изменение sysctl задаёт алгоритм именно для новых TCP-соединений, поэтому проверять результат на старом keep-alive сокете бессмысленно.
fq — classless qdisc, рассчитанный прежде всего на локально генерируемый трафик. Он разделяет потоки и умеет уважать pacing, заданный TCP stack. Это не «ускоритель BBR», а scheduler на egress, который помогает выпускать пакеты во времени более управляемо. После перехода Linux на EDT TCP-level pacing больше не делает fq формально обязательным для работы BBR; Google отдельно отмечает, что на современных ядрах BBR может работать и с другими qdisc, хотя fq остаётся полезным на нагруженных серверах.
Отсюда главный operational вывод: тестировать нужно пару механизмов, а не магическую строку из старого блога. Сначала убедитесь, какой congestion control реально назначен сокету; затем — какой root qdisc реально висит на нужном интерфейсе. net.core.default_qdisc показывает дефолт для создаваемых qdisc, но фактическое состояние интерфейса смотрите через tc qdisc show.
Не путайте и версии BBR. Ветка Google BBRv3 существует отдельно от стандартного bbr, поставляемого конкретным дистрибутивным ядром. Нельзя по одному имени sysctl заключить, что у вас именно v3: это проверяется по происхождению и сборке ядра. Для production-runbook разумнее начинать с того BBR, который поддерживается вашим kernel/vendor profile, а экспериментальную ветку выносить в отдельный change.

Baseline начинается с identity: какой сокет, интерфейс и qdisc вы меняете
До любых изменений соберите evidence bundle. Нужны версия ядра, доступные congestion control, текущий алгоритм, реальный qdisc интерфейса, состояние NIC и несколько живых TCP-сокетов. Это защищает от самой неприятной ошибки: вы включили BBR на backend, а клиентский TCP на самом деле живёт в другом network namespace, на proxy или вообще в отдельном узле.
Сначала определите интерфейс по маршруту до тестового клиента: ip route get CLIENT_IP. Если сервис в контейнере, проверьте, где заканчивается TCP — в pod, host network, ingress или внешнем балансировщике. На bare metal и обычном VPS sender чаще очевиден, но и там source policy routing или bonding могут отправлять трафик не через тот интерфейс, который вы выбрали по имени.
IFACE=ens5
uname -r
ip route get 203.0.113.10
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.core.default_qdisc
tc qdisc show dev "$IFACE"
ip -s link show dev "$IFACE"
ss -tin state established | head -n 80
Адрес 203.0.113.10 здесь — документационный пример: замените его IP своего canary-клиента. Не копируйте имя ens5 вслепую. Сохраните вывод рядом с change ticket: после rollout и rollback вы будете сравнивать не память инженера, а одно и то же состояние.
Отдельно запишите topology class: физический dedicated, KVM/VPS, bonding, VLAN, NAT, внешний LB. BBR работает на TCP sender, но гипервизор, vSwitch, NIC ring, ToR и провайдерский uplink остаются другими очередями. Если проблема живёт там, красивый ss на гостевой ОС не доказывает, что вы контролируете bottleneck.

Сначала докажите сетевую причину, а уже потом меняйте congestion control
Хороший canary начинается с гипотезы, которую можно опровергнуть. Например: «при одинаковом запросе TTFT остаётся стабильным, но p95 паузы между SSE-чанками растут вместе с RTT/retransmissions на клиентском TCP». Это гораздо сильнее, чем «пользователь говорит, что ответ идёт рывками».
На application layer зафиксируйте TTFT, интервал между чанками и полную длительность ответа. На network layer — RTT, retransmissions, delivery/pacing rate конкретного сокета и статистику qdisc. На host layer — CPU softirq, NIC drops и saturation, если они есть в вашей телеметрии. Важен один и тот же запрос или фиксированный набор canary-запросов, одинаковая модель, одинаковый лимит токенов и один и тот же клиентский регион. Иначе вы сравните разные workloads.
Если ss -tin показывает app_limited, это сильный disqualifier для идеи «нам мешает congestion control»: sender не заполняет доступный путь постоянно. Для LLM это нормально — decode выдаёт токены дискретно. В такой ситуации BBR может быть технически активен, но пользовательский ритм определяет приложение. Аналогично, если retransmissions не меняются, qdisc backlog почти пуст, а паузы коррелируют с GPU decode, сетевой эксперимент лучше остановить.
Не забывайте прокси. Если Nginx/Envoy/HAProxy завершает клиентский TCP, снимайте socket evidence там. Backend-соединение между proxy и inference node может выглядеть идеально и никак не описывать WAN-очередь. Разницу между L4 и L7 особенно важно учитывать в streaming API: семантика buffering и timeout может дать те же симптомы, что и плохая сеть.
Снимите контрольный профиль CUBIC и не меняйте workload одновременно
Перед BBR нужен контрольный профиль текущего congestion control. На большинстве систем это будет CUBIC, но не предполагайте — прочитайте sysctl и фактический сокет. Ключевая дисциплина эксперимента: не менять одновременно модель, batching, max tokens, proxy buffering, kernel, MTU и congestion control. Один change — одна причинная гипотеза.
Для LLM streaming полезнее всего воспроизводить не гигантский download, а настоящий API-путь. Генератор трафика должен проходить через тот же TLS terminator и тот же network segment, что и пользователь. Сохраняйте timestamp первого байта и времена каждого чанка. Если ваш клиент умеет только «полное время запроса», вы не увидите разницу между TTFT и последующими паузами.
Сетевые counters снимайте в том же окне. ss -tin показывает RTT, congestion window, pacing rate и другие параметры TCP; для BBR там же могут появляться bandwidth/min_rtt state и признак app_limited. tc -s qdisc show нужен, чтобы видеть фактическую egress-очередь, а не только выбранный алгоритм TCP.
IFACE=ens5
ss -tin state established | head -n 120
tc -s qdisc show dev "$IFACE"
nstat -az | grep -E 'TcpRetransSegs|IpOutDiscards'
ip -s link show dev "$IFACE"
Не задавайте заранее «нормальный процент улучшения». Такой порог без вашего traffic mix будет выдуманным benchmark. Сначала соберите распределение метрик на baseline, затем сравните canary в одинаковых условиях. Если разница меньше естественного разброса или исчезает после повторного прогона, честный вывод — причинность не доказана.
Для контекста по другим слоям полезно сопоставить этот эксперимент с NUMA-locality для GPU-инференса: там действует тот же принцип — низкоуровневый счётчик сам по себе не равен улучшению application SLO.
Включайте BBR и fq только на canary-узле
Когда baseline сохранён, переключайте один canary-host или небольшой canary-pool. Не начинайте с fleet-wide sysctl. Изменение congestion control затронет новые TCP-соединения, поэтому после переключения создайте новые клиентские сессии. Если приложение держит очень длинные keep-alive connections, старые сокеты могут продолжать жить с прежним профилем и смешивать результаты.
Сначала убедитесь, что модуль BBR доступен на вашем ядре. Затем переключите congestion control и qdisc. Команда tc qdisc replace ... root fq меняет root qdisc интерфейса прямо сейчас — это production change, а не безобидное чтение. Выполняйте его только на изолированном canary, где вы заранее знаете исходную конфигурацию и путь rollback.
IFACE=ens5
sudo modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
sudo tc qdisc replace dev "$IFACE" root fq
sysctl net.ipv4.tcp_congestion_control
tc -s qdisc show dev "$IFACE"
ss -tin state established | head -n 120
Не записывайте настройки в /etc/sysctl.d до окончания canary. Сначала докажите эффект и rollback. Persistence — отдельный change. Так проще избежать ситуации, когда временный эксперимент пережил reboot и внезапно стал новым baseline.
На виртуальной машине отдельно проверьте, что вы действительно контролируете нужный egress. Гостевой fq не заменяет очереди vSwitch или физического uplink. На выделенном сервере граница контроля шире: вы управляете kernel, qdisc, NIC и часто topology целиком, но провайдерская сеть и клиентский last mile всё равно остаются вне хоста.

Как читать ss и qdisc без ложной победы по одной метрике
После переключения легко попасть в ловушку «в ss появился bbr — значит всё работает». Это доказывает только выбор congestion control для данного сокета. Для production-вывода нужны как минимум mechanism gate и application gate.
В mechanism gate смотрите, что новый сокет действительно использует BBR, что qdisc — fq, а pacing/delivery state выглядит правдоподобно. Сравните RTT, retransmissions и qdisc backlog с baseline. Если у BBR-сокета app_limited, не трактуйте низкий delivery rate как проблему сети: приложение само не подаёт достаточно данных. Для токенного стрима это частый режим.
В application gate проверяйте то, ради чего вообще делался change: распределение межтокенных пауз, tail latency стрима, число разрывов/повторных подключений и пользовательский SLO. TTFT держите рядом как контроль: если он внезапно изменился, вероятно, в эксперимент вмешалась очередь inference, autoscaling или другая нагрузка.
Сравнивать нужно идентичные traffic cohorts. Один быстрый клиент в соседнем дата-центре не доказывает эффект для мобильных пользователей через длинный WAN. И наоборот, один проблемный оператор связи не должен определять fleet-wide kernel policy. Разделите хотя бы внутренний/региональный и дальний внешний path.
Ещё один важный counterfactual — нагрузка. Повторите тест в спокойном окне. Если отличие BBR/fq проявляется только при заполненном egress и исчезает при низкой загрузке, это логично и полезно: вы локализовали queueing problem. Если «эффект» одинаков даже когда очередь пуста, ищите confounder.

Rollback — это часть эксперимента, а не аварийная кнопка
Если canary выглядит лучше, следующий шаг — не rollout, а возврат к baseline. Это отрицательный контроль. Сначала верните исходный congestion control и qdisc, откройте новые TCP-соединения и повторите тот же canary-набор. Если streaming metric возвращается к прежнему распределению, причинная связь становится сильнее. Если улучшение остаётся, BBR мог просто совпасть по времени с другой переменной.
Исходный qdisc нельзя угадывать. Сохраните его до эксперимента и восстанавливайте из вашего IaC/change record. Ниже показан только пример с переменными; подставлять fq_codel или cubic автоматически нельзя, если это не ваш реальный baseline.
IFACE=ens5
BASE_CC=cubic
BASE_QDISC=fq_codel
sudo sysctl -w net.ipv4.tcp_congestion_control="$BASE_CC"
sudo tc qdisc replace dev "$IFACE" root "$BASE_QDISC"
sysctl net.ipv4.tcp_congestion_control
tc -s qdisc show dev "$IFACE"
После rollback повторите mechanism gate и application gate в том же порядке. Если низкоуровневые TCP counters вернулись, а пользовательская метрика нет, значит bottleneck мигрировал или вы измеряете не тот слой. Например, сеть перестала быть ограничением, и теперь tail latency определяется gateway CPU, TLS, storage или decode cadence.
Именно поэтому rollback должен быть симметричным rollout. Не «вернули sysctl и разошлись», а заново проверили identity, сокет, qdisc и клиентский результат. Такой процесс длиннее, зато он предотвращает накопление мифических kernel tweaks, эффект которых никто не может воспроизвести.

Когда BBR/fq не поможет — и может отвлечь от настоящей причины
Первый disqualifier — HTTP/3. Клиентский HTTP/3 работает поверх QUIC, а не TCP; Linux sysctl net.ipv4.tcp_congestion_control не управляет QUIC congestion control. У Google для QUIC BBR есть отдельная реализация в quiche. Если внешний API уже на HTTP/3, тюнинг TCP BBR на этом сегменте — просто не тот механизм. Сначала разберите транспорт; для этого пригодится статья про HTTP/3 для AI API.
Второй disqualifier — app_limited и низкая интенсивность данных. LLM token streaming часто не похож на bulk transfer: приложение выдаёт небольшие чанки с паузами между decode steps. Если bottleneck bandwidth не заполняется, congestion control имеет мало пространства для улучшения пользовательского ритма.
Третий — buffering выше TCP. L7 proxy может копить данные до размера/таймера, библиотека — не flush-ить каждый chunk, а клиент — сам буферизовать UI. Тогда сеть доставляет всё корректно, но пользователь видит «пачки». Смотрите место фактической задержки, а не только wire metrics.
Четвёртый — saturation на GPU или admission control. Если очередь запросов растёт, TTFT уезжает, а inter-token gap ухудшается из-за конкуренции за compute, BBR здесь не лекарство. Иногда именно ограничение дорогих запросов через quota/rate policy стабилизирует сервис лучше сетевого тюнинга; см. разбор rate limits и quotas для дорогих API.
Пятый — bottleneck за пределами вашего host. Wi‑Fi пользователя, мобильная сеть, межоператорский peering и provider uplink могут доминировать. BBR на sender способен изменить своё поведение, но не даёт вам контроль над чужой очередью. Поэтому коммерчески ценен не сам алгоритм, а возможность воспроизводимо управлять своей частью пути и честно отделять её от внешней.
Production rollout: ценность не в sysctl, а в контроле сетевого пути
Если canary прошёл mechanism gate, application gate и rollback-контрфакт, можно обсуждать rollout. Делайте его по topology class: одинаковые kernel build, NIC/driver, qdisc baseline, MTU и роль в трафике. Не смешивайте в один wave разные типы виртуализации и физические серверы — одинаковый sysctl поверх разных egress paths не создаёт одинаковую систему.
Persistence включайте отдельным шагом. Зафиксируйте sysctl в управляемой конфигурации, qdisc — в network bootstrap/systemd unit или другом IaC-механизме, который ваша команда умеет проверять и откатывать. После reboot должен существовать тот же evidence gate: congestion control доступен, новые сокеты его используют, qdisc соответствует профилю, application canary проходит.
Для выделенной инфраструктуры это и есть естественный offer bridge. Dedicated host даёт ownership над kernel profile, qdisc, NIC, IRQ/CPU placement, reboot window и rollback. Это не обещание, что «dedicated + BBR всегда быстрее». Наоборот: ценность в том, что один и тот же baseline можно воспроизвести, а изменение — изолировать. Если команда не контролирует host networking или провайдер запрещает нужные kernel/qdisc changes, эксперимент может быть организационно невозможен.
При этом сеть не существует отдельно от inference topology. Если после устранения egress queue bottleneck переезжает в CPU/memory locality, вернитесь к системной диагностике. В multi-GPU сценариях полезно сопоставить сетевой rollout с NUMA-locality и другими host-level gates, а не пытаться одним TCP tweak закрыть всю цепочку.

Чек-лист перед rollout и главный вывод
BBR + fq имеет смысл не тогда, когда «так советуют для VPS», а когда вы доказали sender-side queueing problem на реальном LLM streaming path. Перед production change проверьте: известен фактический TCP sender; TTFT отделён от межтокенных пауз; baseline CUBIC/текущего CC сохранён; root qdisc известен; на canary открываются новые соединения; mechanism и application metrics улучшаются согласованно; rollback возвращает картину к baseline; после изменения bottleneck не переехал в GPU, gateway или другой сетевой слой.
Если хотя бы один из этих пунктов не выполняется, результат лучше назвать «интересным наблюдением», а не оптимизацией. Такой консерватизм экономит время: kernel tuning, который нельзя причинно связать с SLO, быстро превращается в технический долг.
Источники для перепроверки механики — актуальная документация Linux IP sysctl, описание tc-fq и материалы Google BBR. Они важнее старых рецептов с одной строкой sysctl.conf, потому что поведение pacing и kernel networking менялось.
Начните с одного canary-host и одного фиксированного набора streaming-запросов. Если эффект выдерживает повтор, rollback и reboot — тогда у вас есть инженерное основание для rollout. Если нет, это тоже хороший результат: вы исключили один слой и можете идти дальше по цепочке без гадания.