MAATRIX / Блог / Шейпинг и приоритизация на сервере: кому отдать полосу, когда её на всех не хватает

Шейпинг и приоритизация на сервере: кому отдать полосу, когда её на всех не хватает

MAATRIX

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

Почему один канал не выдерживает несколько задач одновременно

У сервера есть один физический (или виртуальный, если это VPS) исходящий канал определённой ёмкости. Всё, что уходит наружу — ответы пользователям, бэкапы, синхронизация файлов, трафик между вашими же серверами, обновления пакетов, исходящая почта — делит эту ёмкость между собой. Без явных правил приоритизации ядро Linux по умолчанию обслуживает очередь пакетов по принципу FIFO (первым пришёл — первым ушёл). Это значит: если фоновый процесс успел «занять очередь» первым, пользовательский пакет будет ждать за ним, даже если сам фоновый процесс формально не более приоритетен для бизнеса.

На практике это проявляется в двух типичных сценариях.

Сценарий первый: бэкап или синхронизация душат пользовательский трафик. Ночной rsync, дамп базы, заливка архива в S3-совместимое хранилище, синхронизация с облаком — всё это стремится выгрузить данные как можно быстрее, то есть занять весь доступный канал. Если в этот момент приходит запрос от реального пользователя, его пакеты встают в ту же очередь и ждут своей отправки наравне с бэкапным трафиком. Результат — рост задержки, иногда до нескольких секунд там, где обычно счёт идёт на десятки миллисекунд.

Сценарий второй: несколько сервисов на одном сервере конкурируют за исходящий канал. Веб-приложение, API, VPN-сервер, почтовый релей, мониторинг, который сам гоняет метрики наружу — если всё это живёт на одной машине с одним аплинком, в момент пиковой нагрузки они начинают мешать друг другу. Причём мешает не обязательно самый «тяжёлый» по объёму сервис: если у VPN много мелких пакетов (это отдельная история, разобранная в статье про лимит пакетов в секунду на виртуалке), он создаёт проблему даже при небольшом трафике в мегабитах.

В обоих случаях у вас есть три инструмента: ограничить фоновую задачу технически (traffic shaping), расставить приоритеты между типами трафика (QoS), либо развести задачи по времени, чтобы они физически не пересекались. На практике работает комбинация всех трёх.

Traffic shaping: ограничение полосы для конкретного типа трафика

Traffic shaping (шейпинг трафика) — это управление скоростью отправки пакетов определённого типа так, чтобы он не превышал заданный порог, даже если канал в моменте свободен. Идея простая: вы говорите системе «этот тип трафика — максимум N мегабит в секунду», и она искусственно придерживает лишние пакеты в очереди, отправляя их равномернее.

В Linux за это отвечает подсистема traffic control (tc) — часть стека iproute2, которая вешает на сетевой интерфейс дисциплины очередей (qdisc) вместо стандартной FIFO без приоритетов. Более сложная дисциплина умеет:

  • делить трафик на классы (например, «пользовательский», «бэкапный», «остальное»);
  • назначать каждому классу свою долю канала или потолок скорости;
  • решать, какой класс обслуживать в первую очередь, когда очередь начинает расти.

Технически это обычно связка из нескольких компонентов: tc qdisc создаёт корневую дисциплину (часто HTB — Hierarchical Token Bucket, удобна именно для деления канала на классы с гарантированной и максимальной полосой), tc class описывает сами классы с их лимитами, а tc filter (часто в паре с iptables/nftables, которые помечают пакеты через MARK) определяет, какой пакет в какой класс попадает — по порту, по адресу назначения, по типу трафика.

Здесь намеренно не привожу пошаговый рецепт с точными числами: конкретные лимиты классов, ширина токен-бакета, fwmark-метки — всё это считается от вашего реального канала и вашей нагрузки, а не копируется из чужой статьи. Общая идея, которую стоит унести:

  • Шейпинг настраивается на исходящем интерфейсе — это ограничение того, что вы отправляете, а не входящего трафика (входящий шейпить сложнее и обычно менее эффективно, поскольку пакет уже прошёл ваш канал к моменту, когда вы решаете его отбросить или задержать).
  • Разумно резервировать под фоновые задачи (бэкап, синк) не более какой-то доли канала, оставляя гарантированный запас для пользовательского трафика даже в пиковый момент. Конкретная доля — вопрос тестирования на вашей нагрузке, а не универсальная цифра.
  • Дисциплины очередей с приоритетами (например, HTB с несколькими классами, или более простая PRIO) дают предсказуемость: пользовательский класс не будет голодать, даже если бэкап пытается вылить всё разом.

Если вы не готовы разбираться с tc на уровне ядра, для многих сценариев хватает более простого подхода — ограничить скорость не на уровне сети, а на уровне конкретной программы, которая генерирует фоновый трафик. Об этом — в следующем разделе.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Подобрать сервер под нагрузку

QoS-приоритизация: не просто ограничить, а расставить очередь

Traffic shaping отвечает на вопрос «сколько максимум»; QoS (Quality of Service) — на вопрос «кого обслуживать первым, когда всем сразу не хватает». Это разные, хотя и связанные задачи. Шейпинг можно настроить и без приоритизации (просто урезать бэкап до фиксированной скорости всегда), а можно совместить оба подхода: дать бэкапу мягкий лимит и при этом явно объявить, что пользовательский трафик обслуживается первым, если оба типа трафика претендуют на канал одновременно.

Смысл QoS-приоритизации на сервере обычно сводится к трём уровням важности:

УровеньТипичный трафикПоведение при перегрузке канала
Высокий приоритетОтветы пользователям, API, интерактивные сессии (SSH, VoIP, видеозвонки)Обслуживается первым, минимальная задержка
Средний приоритетОбычная фоновая работа: обновления, синхронизация небольших файлов, мониторингЖдёт, если канал занят высоким приоритетом, но не блокируется полностью
Низкий приоритетМассовые бэкапы, полная синхронизация больших объёмов, batch-выгрузкиЗанимает канал только когда он свободен от более приоритетного трафика

Технически такое деление реализуется той же связкой tc + маркировка пакетов, либо на уровне приложения, если оно умеет расставлять DSCP-метки (Differentiated Services Code Point — поле в заголовке IP-пакета с желаемым приоритетом). Важный нюанс: DSCP-метки играют роль, только пока трафик остаётся в инфраструктуре, которая их уважает. Как только пакет уходит в сеть провайдера, тот с большой вероятностью эти метки игнорирует или сбрасывает — приоритизация внутри вашего сервера решает конкуренцию ваших же сервисов друг с другом, но не гарантирует приоритет во внешней сети.

Практический вывод: если у вас несколько сервисов на одной машине, стоит явно решить, какой из них «жизненно важен» (обычно — то, что напрямую видит пользователь: веб, API, интерактивные протоколы), и настраивать shaping так, чтобы именно этот трафик первым получал доступ к каналу, а всё остальное — по остаточному принципу.

Простой приём: ограничить сам бэкап-скрипт, а не канал целиком

Настройка tc даёт гибкий и точный контроль, но для многих ситуаций избыточна. Если проблема конкретна — «вот этот cron-скрипт с rsync забивает канал по ночам» — часто быстрее и надёжнее ограничить скорость самого инструмента, а не городить дисциплины очередей на интерфейсе.

У большинства утилит синхронизации и передачи файлов уже есть встроенный флаг ограничения скорости:

# rsync: ограничение полосы примерно до N килобайт/сек
rsync -avz --bwlimit=RATE /path/to/data/ user@remote:/backup/

# curl: ограничение скорости загрузки/выгрузки
curl --limit-rate RATE -T archive.tar.gz https://storage.example.com/upload

# scp (через ssh) тоже поддерживает ограничение полосы:
scp -l RATE_IN_KBIT archive.tar.gz user@remote:/backup/

Здесь намеренно не подставлено конкретное число вместо RATE — оно зависит от вашего канала, времени, за которое должен успеть отработать бэкап, и того, сколько запаса вы готовы оставить пользовательскому трафику. Подход к выбору цифры простой: возьмите ёмкость канала, вычтите разумный запас под пиковую пользовательскую нагрузку (например, наблюдаемый максимум за последние недели плюс запас), оставшееся отдайте фоновой задаче. Дальше — проверка на практике: запустить бэкап с этим лимитом одновременно с синтетической пользовательской нагрузкой и посмотреть, что происходит с задержкой ответов.

Похожая логика применима и к диску — если бэкап параллельно упирается в дисковый ввод-вывод, там работает похожий инструмент (ionice), подробно разобранный в статье про приоритет ввода-вывода и то, почему ночной бэкап душит сайт — стоит прочитать, если после ограничения сетевой скорости сайт всё равно тормозит именно в момент бэкапа.

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

Перенос тяжёлых задач в непиковые часы — простая альтернатива техническому шейпингу

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

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

Практические соображения при переносе задач по времени:

  • Если у сервера пользователи в разных часовых поясах, «ночь по вашему времени» может оказаться пиком для другого региона — смотрите на реальные графики нагрузки, а не на локальные часы.
  • Разные фоновые задачи (бэкап, обновление индексов, ротация логов, синхронизация с внешним хранилищем) стоит развести и между собой — иначе вы просто переносите конкуренцию за канал с «день против ночи» на «час ночи против часа ночи».
  • Планировщик (cron, systemd-таймеры) не гарантирует, что задача стартует именно тогда, когда вы рассчитывали, если предыдущий запуск ещё не завершился — проверяйте, что задачи не накладываются друг на друга.
  • Перенос по времени и шейпинг не исключают друг друга: можно развести бэкап на тихие часы и на всякий случай оставить ему --bwlimit — на случай разового всплеска или активного пользователя из другого пояса.

Шейпинг на вашей стороне против шейпинга у провайдера

Важно разделять два вида ограничения полосы: то, что настраиваете вы сами на своём сервере, и то, что где-то по пути делает сеть провайдера (хостера, магистрального оператора, вашего ISP) без вашего участия и часто без предупреждения.

Шейпинг на своей стороне — это всё, что описано выше: tc, --bwlimit, ionice, перенос задач по времени. Здесь вы полностью контролируете причину и следствие: знаете, какое правило действует и на какой трафик, и можете изменить его в любой момент. Если что-то работает не так — можно посмотреть конфигурацию tc qdisc show, счётчики классов, логи cron — и разобраться.

Шейпинг на стороне провайдера — это ограничение, которое накладывает внешняя сеть: по порту, по протоколу, по объёму трафика после превышения квоты, иногда избирательно для конкретных сервисов. Здесь у вас нет доступа к конфигурации — вы видите только результат: необъяснимо низкую скорость для одного типа трафика при нормальной скорости для остального. Отличить это от собственной проблемы (перегруженный диск, неверно настроенный ваш же шейпинг, исчерпанный лимит виртуалки по PPS) бывает непросто, и тут помогает системный подход к диагностике, а не догадки.

Практический ориентир: если проблема воспроизводится только для конкретного протокола или сервиса, а остальной трафик с того же сервера идёт нормально — это больше похоже на избирательное ограничение где-то на сети (при вашем собственном шейпинге вы обычно точно знаете, что и почему ограничили). Если же тормозит всё сразу и коррелирует по времени с вашим же бэкапом — вопрос, скорее всего, к тому, что происходит на вашей стороне.

Смежный случай — когда дело не в шейпинге по типу трафика, а в том, что канал общий (shared), а не выделенный, и в моменты чужой нагрузки на том же физическом канале страдаете и вы. Разница между форматами и что вы на самом деле покупаете под словом «гарантированный» в тарифе — в статье общий канал против выделенного: что вы покупаете.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Подобрать сервер под нагрузку

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

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

С чего начать, если сайт тормозит именно во время бэкапа?

Проверьте самый дешёвый вариант — ограничить бэкап-инструмент через --bwlimit (rsync) или аналогичный флаг. Если это снимает симптом — можно остановиться на этом или позже донастроить перенос задачи на тихие часы. Если не снимает полностью, проверьте, не упираетесь ли вы параллельно в диск (тогда нужен ionice) или в лимит пакетов в секунду виртуалки.

Обязательно ли настраивать tc, или можно обойтись флагами утилит?

Для одной-двух явно проблемных задач обычно достаточно ограничить сам инструмент. tc имеет смысл настраивать, когда источников фонового трафика много и разных, либо когда нужно гарантировать полосу пользовательскому трафику единым правилом на уровне интерфейса, а не разбросанными флагами по десятку скриптов.

Как понять, что тормозит не мой сервер, а провайдер режет конкретный сервис?

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

Можно ли ограничить входящий трафик так же просто, как исходящий?

Технически можно, но эффективность ниже: пакет уже занял часть канала на пути к серверу к моменту, когда вы решаете его задержать. Для входящего трафика чаще используют ограничение на уровне приложения (лимиты соединений, троттлинг запросов), а не шейпинг.

Что будет, если поставить --bwlimit слишком низким?

Бэкап станет безопаснее для остального трафика, но будет идти дольше — вплоть до риска не успеть до следующего окна. Лимит стоит подбирать не «на глаз поменьше», а от реального объёма данных и доступного окна: посчитайте время на задачу и от него выводите минимально достаточную скорость.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →