Зачем на VDS вообще трогать tc и qdisc
Сетевые «лаги» на сервере часто выглядят нелогично: CPU свободен, диски в норме, полоса пропускания ещё есть, но SSH начинает «залипать», API отвечает с задержкой, а ping до VDS прыгает в разы. Частая причина — bufferbloat: избыточные очереди в сетевом стеке, драйвере или в слое виртуализации, которые раздуваются при передаче больших объёмов данных и начинают задерживать весь остальной трафик.
В Linux за дисциплины очередей отвечает qdisc (queueing discipline). «По умолчанию» это может быть fq_codel, pfifo_fast, fq или корневой mq с дочерними очередями. Но дефолтные настройки не обязаны совпадать с вашим профилем нагрузки, особенно если вы параллельно держите интерактивные сервисы и фоновые выгрузки.
Особенно часто tc/qdisc реально помогают, когда вы:
- отдаёте большие файлы или бэкапы;
- держите веб/апи и параллельно выгружаете медиа;
- используете VPN/туннели или проксируете трафик;
- делаете traffic shaping (ограничение скорости, rate limit) на уровне сервера.
Ключевая идея: чтобы управлять задержкой под нагрузкой, «узкое горлышко» должно быть там, где вы контролируете очередь. На VDS это почти всегда исходящий трафик на вашем виртуальном интерфейсе. Входящий трафик уже «прилетел», и там всё сложнее (ниже разберём, что можно сделать на практике).
Что такое bufferbloat простыми словами
Представьте очередь в магазине: если очередь бесконечная и касса обслуживает медленно, люди будут стоять долго. В сетях такая очередь — это буферы, где пакеты ждут отправки. Когда TCP, драйвер или виртуализация позволяют копить слишком много пакетов «на потом», задержка для интерактивного трафика (SSH, короткие HTTP-запросы, ответы БД, мониторинг) растёт, хотя пропускная способность может оставаться высокой.
На VDS bufferbloat обычно проявляется при смешанном трафике: один процесс начинает долго и стабильно «качать» (upload или download), и на этом фоне резко ухудшается латентность для всего остального.
Если под нагрузкой ping «раздувается», а без нагрузки он стабильный — это почти всегда про очереди: на вашем сервере, на аплинке или в сети клиента. tc/qdisc помогает, когда проблема в вашей зоне контроля (обычно egress на VDS).

Быстрая диагностика: есть ли bufferbloat прямо сейчас
1) Посмотреть, какие qdisc активны
tc -s qdisc show
И отдельно по интерфейсу (например, eth0 или ens3):
tc -s qdisc show dev eth0
В выводе важны:
- название qdisc (например,
fq_codel,cake,pfifo_fast,mq); - счётчики
drops,overlimits,backlog; - динамика: если
backlogрастёт при нагрузке и «висит» — очередь раздувается.
2) Простой тест «нагрузка + ping»
Смысл теста — создать устойчивую нагрузку на канал и параллельно измерять задержку. Чаще всего проще тестировать снаружи: запустить выгрузку/загрузку и одновременно пинговать VDS (или ближайший к нему узел).
Быстрый сценарий: в одном окне держите ping, во втором создаёте исходящий поток (например, rsync/backup, выгрузка в хранилище, заливка артефактов CI). Если при исходящей нагрузке ping растёт на десятки или сотни миллисекунд — это типичный симптом очередей на egress, который обычно лечится shaping’ом + AQM.
Если вы хотите системно наблюдать влияние ограничений на задержку и скорость, полезно завести отдельный лист с замерами (скорость, RTT без нагрузки, RTT под нагрузкой) и повторять их после каждого изменения. Также по теме мониторинга shaping’а пригодится материал про наблюдаемость сетевого трафика на сервере: мониторинг и контроль сетевого трафика на VDS.
Термины, без которых легко запутаться
qdisc— дисциплина очереди: как пакеты ставятся в очередь и как извлекаются.- AQM (Active Queue Management) — активное управление очередью: алгоритмы, которые предотвращают раздувание (CoDel, PIE и т.д.).
- FQ (Fair Queuing) — справедливое разбиение на потоки, чтобы один поток не «съедал» всю очередь.
- Shaping — формирование трафика: вы специально делаете интерфейс «узким горлышком» со скоростью чуть ниже реальной, чтобы очередь контролировалась вашим qdisc.
- Policing — «обрезание» трафика дропами. Для TCP часто хуже shaping’а, но иногда это единственный быстрый инструмент для входящего направления.
fq_codel vs CAKE: что выбрать на VDS
fq_codel
fq_codel — популярный компромисс: FQ + CoDel. Часто стоит по умолчанию в современных дистрибутивах и даёт хороший контроль задержки, если вы сделали shaping и перенесли bottleneck «к себе».
Плюсы: простота, широкая поддержка, адекватные дефолты. Минусы: меньше встроенных «удобств», чем у CAKE, и обычно приходится отдельно настраивать шейпер (HTB/TBF) и отдельно AQM.
CAKE
cake — более «всё-в-одном»: shaping + fair queuing + AQM с удобными режимами. На практике CAKE часто даёт более предсказуемую латентность при смешанном трафике, но важно корректно подобрать скорость. По CPU на типичных VDS это редко становится проблемой, если вы не упираетесь в очень высокие скорости или экстремальный PPS.
Плюсы: мощнее, хорошие дефолты, удобнее для «человеческого» shaping’а. Минусы: не всегда доступен в минимальных ядрах/сборках, и подбор bandwidth критичнее.
Главный принцип shaping’а: лимит чуть ниже реального
Чтобы победить bufferbloat, недостаточно просто включить «правильный» qdisc. Нужно добиться, чтобы очередь формировалась в вашем Linux, а не в виртуальном свитче, аплинке или где-то на пути. Для этого выставляют rate limit чуть ниже реально достижимой скорости именно в вашем сценарии.
Пример: если по факту вы стабильно отдаёте 1 Гбит/с, имеет смысл начать с shaping на 900–970 Мбит/с и посмотреть на задержку под нагрузкой. Слишком низко — потеря скорости. Слишком высоко — очередь останется «где-то там», и latency снова поплывёт.
Если вы ещё подбираете конфигурацию сервера под нагрузку (CPU/частота/сеть), полезно свериться с базовыми принципами выбора тарифа: как выбрать VDS по CPU и RAM под задачу. На практике сетевой shaping нередко упирается не в «битрейт», а в PPS и CPU.
Практика: настройка tc для исходящего трафика (egress)
Ниже — рабочие рецепты, которые чаще всего применимы на VDS. Предполагаем интерфейс eth0. Перед изменениями зафиксируйте текущее состояние:
ip -br link
ip -br addr
tc -s qdisc show dev eth0
Важно: все изменения ниже не переживают перезагрузку сами по себе. Для постоянства настройку обычно переносят в systemd unit, сетевые скрипты дистрибутива или конфигурацию вашего оркестратора.
Вариант A: CAKE как основной shaping + AQM
Сначала уберём текущий корневой qdisc (если он есть). Команда безопасна: если qdisc нет — будет ошибка, её можно игнорировать.
tc qdisc del dev eth0 root
Добавляем CAKE с лимитом (пример — 900 Мбит/с):
tc qdisc add dev eth0 root cake bandwidth 900Mbit besteffort
Проверяем статистику:
tc -s qdisc show dev eth0
На что смотреть в CAKE:
drops— дропы будут, это нормальная часть AQM; важно, чтобы это не превращалось в постоянную «мясорубку»;backlog— если он стабильно большой, лимит может быть слишком высоким (очередь выстраивается не там) или трафик слишком «тяжёлый» для выбранногоbandwidth;- реальные ощущения: параллельный трафик + ping/SSH/ответы API.
Вариант B: HTB + fq_codel (классический shaping)
Если CAKE недоступен или вы хотите «традиционную» схему, используйте корневой шейпер htb и внутри него AQM fq_codel.
Сбросим root qdisc:
tc qdisc del dev eth0 root
Создадим корневой HTB и класс по умолчанию:
tc qdisc add dev eth0 root handle 1: htb default 10
tc class add dev eth0 parent 1: classid 1:10 htb rate 900Mbit ceil 900Mbit
Теперь прикрутим fq_codel к этому классу:
tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel
Проверка:
tc -s qdisc show dev eth0
Схема HTB + fq_codel даёт предсказуемый результат: HTB формирует скорость, а fq_codel удерживает очередь «в человеческих» пределах.
Если нужно ограничить скорость для одного сервиса или порта
Для точечного traffic shaping можно классифицировать трафик фильтрами и отправлять его в отдельный класс с другим лимитом. Это полезно, когда бэкапы не должны мешать API, или когда вы хотите «придушить» выдачу больших файлов, сохранив отзывчивость сайта.
Популярные варианты классификации: DSCP, порт, cgroup (по процессам), либо fwmark через netfilter. На VDS часто удобнее идти через fwmark: помечаете пакеты в nftables/iptables и в tc матчащите по fw.
Важная оговорка: универсальная «копипаста» без контекста опасна — легко ограничить не то (например, SSH, мониторинг или DNS) и получить неожиданные таймауты. Держите логику простой:
- создайте два класса: «обычный» и «фоновый»;
- для фонового поставьте меньший rate;
- на выходе классов всё равно используйте
fq_codel(или CAKE на корне), чтобы bufferbloat не вернулся.

Входящий трафик (ingress): почему сложнее и что реально сделать
Входящий трафик вы не «замедлите» так же красиво, как исходящий: пакеты уже пришли на интерфейс, и если вы начнёте их дропать, TCP перестроится, но управляемости меньше. Тем не менее есть два практических подхода:
- Ingress policing: ограничивать вход дропами (быстро, но грубо; TCP может проседать сильнее).
- IFB + shaping: перенаправлять ingress в виртуальный интерфейс IFB и шейпить «как egress». Гибче, но заметно сложнее в поддержке.
Для большинства серверных задач критичнее стабилизировать именно исходящий трафик: он чаще становится источником очередей на стороне VDS и сильнее влияет на интерактивность (SSH, панель администрирования, ответы API).
Проверка результата: как понять, что стало лучше
После включения shaping’а и qdisc ориентируйтесь не только на цифры tc -s, но и на прикладное поведение сервисов:
- ping до VDS под исходящей нагрузкой растёт умеренно, без «ступенек» и секундных всплесков;
- SSH остаётся отзывчивым во время выгрузок;
- HTTP tail latency (95/99 перцентиль) уменьшается;
- в логах приложений меньше таймаутов на внешние вызовы.
С точки зрения метрик tc хорошо, когда:
backlogне «зависает» на больших значениях;- дропы есть, но они «рабочие» (не превращаются в постоянную лавину);
- нет постоянных
overlimitsиз-за слишком низкого лимита (признак, что вы перерезали канал сильнее, чем нужно).
Типичные ошибки при настройке tc на VDS
Ограничение «впритык» или выше реальной скорости
Если поставить rate limit равным «номиналу», вы часто не получаете контроля над очередью: где-то по пути останется буфер, который и будет раздуваться. Лимит должен быть чуть ниже реально достижимой скорости именно в вашем сценарии (с учётом PPS, MTU и особенностей виртуализации).
Shaping включили, а проблема осталась
Тогда вероятно одно из двух: bufferbloat не на вашем сервере (например, у клиентов в домашней сети), либо bottleneck не там: вы шейпите не тот интерфейс или не то направление (например, упираетесь во входящий поток, а лечите исходящий).
Забыли про multi-queue и корневой mq
На некоторых интерфейсах вы увидите корневой mq и дочерние qdisc на очередях. Это нормально: стек использует несколько очередей. В таких случаях важно понимать, на каком уровне вы включаете shaping: иногда нужно навесить qdisc на корень, иногда — на конкретную очередь (зависит от драйвера и виртуального интерфейса в вашей VDS).
Сделали shaping и получили падение скорости и рост CPU
Причины обычно приземлённые: слишком сложная иерархия классов, слишком низкий лимит, либо вы упёрлись в обработку большого количества мелких пакетов. Упростите конфигурацию и начните с базового: CAKE с одним лимитом или HTB + fq_codel без фильтров.
Шпаргалка: минимальные команды для «включить и проверить»
Посмотреть текущее
tc -s qdisc show dev eth0
Сбросить root qdisc
tc qdisc del dev eth0 root
Поставить CAKE 900Mbit
tc qdisc add dev eth0 root cake bandwidth 900Mbit besteffort
Поставить HTB 900Mbit + fq_codel
tc qdisc add dev eth0 root handle 1: htb default 10
tc class add dev eth0 parent 1: classid 1:10 htb rate 900Mbit ceil 900Mbit
tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel
Итоги
tc и правильно выбранный qdisc — это не «тюнинг ради тюнинга», а практичный способ убрать bufferbloat и стабилизировать задержку на VDS под реальной нагрузкой. Начните с простого: определите интерфейс, замерьте задержки под нагрузкой, включите shaping чуть ниже реальной скорости и используйте fq_codel или CAKE.
Если вы подбираете площадку под такие задачи, удобнее всего тестировать и внедрять shaping там, где вы полностью контролируете ОС и сетевой стек — на VDS. А для менее требовательных проектов без сложной сетевой политики часто достаточно виртуального хостинга.
Дальше можно усложнять: разделять классы для фонового трафика, помечать пакеты и строить политику под свои сервисы. Но базовый слой всегда один: честный bottleneck у вас и контроль очереди, а не ожидание, что «где-то по пути» буферы будут вести себя идеально.


