Выберите продукт

Linux tc netem: эмуляция задержек, потерь и джиттера для тестов сети

tc netem позволяет воспроизводимо «портить» сеть в Linux: добавлять задержку и jitter, имитировать packet loss, reorder и duplicate. Ниже — готовые команды для интерфейса и для выбранного трафика, способы проверки и безопасный откат.
Linux tc netem: эмуляция задержек, потерь и джиттера для тестов сети

Иногда проблема в проде выглядит как «плавающие таймауты», «рандомные 502», «странные ретраи», а в лаборатории всё идеально. В таких случаях выручает network emulation: вы искусственно ухудшаете канал и проверяете, как ведёт себя приложение, балансировщик, TCP-стек и логика ретраев клиента.

В Linux для этого используется tc (traffic control) и qdisc netem. Он умеет эмулировать delay, jitter, packet loss, reorder и duplicate. А для контроля очередей и борьбы с bufferbloat полезно комбинировать его с fq_codel (или fq/cake в отдельных сценариях).

Дальше — практичный набор приёмов: как быстро включить tc netem на интерфейсе, как ограничить влияние только нужным портом/хостом, как проверить эффект и как безопасно откатить настройки.

Что такое qdisc и где живёт netem

tc управляет дисциплинами очереди (qdisc) на сетевом интерфейсе. Есть корневая qdisc (root) и дочерние. Фильтры (классификаторы) могут направлять часть трафика в отдельную «ветку» с нужными правилами.

netem — это qdisc, которая искусственно меняет судьбу пакетов: задерживает, теряет, переставляет местами или дублирует. Обычно её ставят либо root-qdisc для всего интерфейса (быстро, но грубо), либо как дочернюю — чтобы «портить» только выбранный трафик.

Важно: применяя tc к интерфейсу, вы влияете на трафик, который выходит через этот интерфейс. Входящий (ingress) трафик нельзя просто «задержать» без дополнительных приёмов (например, IFB). Проще всего эмулировать условия на стороне клиента или на промежуточном хосте.

Подготовка: что проверить перед стартом

1) Убедитесь, что qdisc уже не используется

Посмотрите текущие дисциплины очередей и список интерфейсов:

tc qdisc show
ip -br link

Часто по умолчанию вы увидите fq_codel или fq (зависит от дистрибутива/ядра). Если вы поставите netem как root, вы замените текущую root-qdisc для интерфейса.

2) Выберите интерфейс

Например, eth0, ens3, enp1s0. В примерах ниже используется eth0 — подставьте свой.

3) План отката (обязательно)

Самый безопасный откат — удалить root-qdisc на интерфейсе (ядро вернёт дефолтную):

tc qdisc del dev eth0 root

Держите эту команду под рукой в отдельной SSH-сессии. Если вы тестируете на удалённом сервере, начинайте с мягких значений (например, 20–50 мс), чтобы не «отрезать» себе доступ.

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

Вывод tc qdisc show и tc -s для проверки netem на интерфейсе

Быстрый старт: задержка и jitter

Добавим фиксированную задержку 100 мс на исходящий трафик интерфейса:

tc qdisc add dev eth0 root netem delay 100ms

Проверим счётчики и RTT:

tc -s qdisc show dev eth0
ping -c 5 1.1.1.1

Теперь добавим jitter: 100 мс базовой задержки и ±20 мс разброса:

tc qdisc change dev eth0 root netem delay 100ms 20ms

Для более реалистичной модели часто используют нормальное распределение:

tc qdisc change dev eth0 root netem delay 100ms 20ms distribution normal

Если нужно воспроизвести «мобильность», одной вариативности задержки может быть мало: добавьте небольшой loss или reorder, иначе TCP может вести себя слишком «идеально».

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Эмуляция packet loss: постоянная и коррелированная

Packet loss — основа для тестов ретраев, таймаутов и поведения протоколов поверх TCP/QUIC.

Постоянная потеря

Например, 1% потерь:

tc qdisc change dev eth0 root netem loss 1%

Комбинация delay + loss:

tc qdisc change dev eth0 root netem delay 80ms 15ms distribution normal loss 0.5%

Коррелированная потеря (burst loss)

Реальные каналы часто теряют пакеты пачками. В netem можно задать корреляцию (условно: вероятность того, что следующий пакет тоже потеряется):

tc qdisc change dev eth0 root netem loss 1% 25%

Здесь 1% — базовая вероятность, 25% — корреляция. Чем выше корреляция, тем более «кучными» становятся потери.

Reorder и duplicate: редкие, но показательные тесты

Reorder и duplicate полезны для тестов приложений и библиотек, которые неверно предполагают «идеальный» порядок/доставку на уровне выше TCP. Особенно заметно это в UDP-протоколах, телеметрии, туннелях и при работе через некоторые middlebox.

Reorder (переупорядочивание)

Пример: 0.2% пакетов будут приходить «не на месте». Обычно reorder требует наличия задержки, иначе переставлять нечего:

tc qdisc change dev eth0 root netem delay 50ms 10ms reorder 0.2% 50%

Где 0.2% — доля пакетов, которые будут переупорядочены, 50% — корреляция эффекта.

Duplicate (дублирование пакетов)

Например, 0.1% пакетов будут продублированы:

tc qdisc change dev eth0 root netem duplicate 0.1%

В сочетании с задержкой и небольшим loss можно получить «живую» картину для тестов идемпотентности запросов и дедупликации на уровне приложения (там, где это применимо).

Как комбинировать netem и fq_codel (и зачем)

Частая ошибка — повесить только netem и удивляться странным задержкам под нагрузкой. netem сам по себе не всегда решает проблему очередей; при высокой скорости отправки может появляться bufferbloat, который «смазывает» ваш сценарий теста.

Практичный подход: разделять ответственность. Netem задаёт «качество канала», а fq_codel — «как ведёт себя очередь под нагрузкой». В зависимости от цели теста вы либо ставите netem как root (быстро), либо строите дерево qdisc и направляете в netem только нужный поток.

Если хотите тестировать узкий канал (например, 20–50 Мбит/с), одного netem недостаточно: нужна дисциплина, ограничивающая скорость (например, tbf или классы в htb). Тогда сценарий будет ближе к реальности, особенно для очередей и latency under load.

Точечное ухудшение: только один порт/адрес, а не весь интерфейс

Глобально «портить» интерфейс удобно, но часто опасно: вы заденете SSH, мониторинг, репликации. Надёжнее сделать классификацию: выделить нужный трафик фильтром и направить его в отдельную qdisc с netem.

Пример: эмулируем плохую сеть только для исходящих соединений к IP 203.0.113.10 на TCP порт 443. Для классификации используем prio и u32:

tc qdisc add dev eth0 root handle 1: prio
tc qdisc add dev eth0 parent 1:1 handle 10: fq_codel
tc qdisc add dev eth0 parent 1:2 handle 20: netem delay 120ms 30ms loss 1%

tc filter add dev eth0 protocol ip parent 1: prio 2 u32 match ip dst 203.0.113.10/32 match ip dport 443 0xffff flowid 1:2

Что здесь важно:

  • prio создаёт несколько band. Band 1 (1:1) — «нормальный» с fq_codel, band 2 (1:2) — «плохой» с netem.
  • Фильтр отправляет только совпавший трафик в band 2.
  • SSH (например, порт 22) не пострадает, если вы не добавите под него отдельный фильтр.

Проверить, что фильтр матчится, можно по счётчикам:

tc -s qdisc show dev eth0
tc -s filter show dev eth0 parent 1:

Если вы делаете такие эксперименты на боевых серверах, сначала приведите в порядок базовую сетевую гигиену (SSH, фаервол, ограничение доступа). Подробный чеклист есть в статье про защиту VDS: SSH и firewall.

Схема: prio qdisc с нормальной веткой и веткой netem для выбранного трафика

Проверка результата: как понять, что эмуляция реально работает

1) ping для RTT и потерь

ping -c 20 203.0.113.10

Даже простой ping даст ощущение изменения RTT и джиттера.

2) Нагрузка (очередь проявляется на трафике)

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

3) Счётчики tc

tc -s показывает количество пакетов/байт, дропы и состояние очередей:

tc -s qdisc show dev eth0

Если вы включили loss, но дропов не видно — вероятно, трафик не попадает в нужную qdisc (ошибка в фильтре) или нет нагрузки.

Типовые профили для тестов (готовые рецепты)

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

Профиль 1: умеренная WAN-задержка

tc qdisc replace dev eth0 root netem delay 60ms 10ms distribution normal

Профиль 2: мобильная сеть или нестабильный Wi‑Fi

tc qdisc replace dev eth0 root netem delay 120ms 40ms distribution normal loss 1% 25% duplicate 0.05%

Профиль 3: случайные таймауты из-за потерь

tc qdisc replace dev eth0 root netem loss 3% 40%

Профиль 4: переупорядочивание

tc qdisc replace dev eth0 root netem delay 40ms 10ms reorder 0.5% 50%

Откат и уборка: как вернуть всё как было

Самый простой способ — удалить root-qdisc:

tc qdisc del dev eth0 root

Если вы строили дерево (prio + дочерние qdisc + фильтры), удаление root обычно убирает всё дерево целиком.

Полезная привычка: перед экспериментом сохранить текущее состояние:

tc qdisc show dev eth0
tc filter show dev eth0

А после — снова проверить, что всё чисто.

Частые ошибки и практические советы

  • Портите весь интерфейс и теряете SSH. Делайте точечные фильтры или тестируйте на отдельной машине/namespace.
  • Пытаетесь эмулировать ingress обычным netem. Для входящего трафика обычно используют IFB и перенаправление ingress в egress IFB.
  • Нет нагрузки — нет эффекта. Для проверки очередей нужен поток; измеряйте задержки параллельно.
  • Слишком большие значения jitter/loss. Начинайте с малого: 20–50 мс и 0.1–0.5% потерь, затем усиливайте.
  • Забыли про очереди. Для реалистичных тестов под нагрузкой комбинируйте netem с fq_codel и, при необходимости, с шейпингом скорости.

Мини-чеклист перед тем, как использовать tc netem в стенде

  1. Определите, где эмулировать (клиент, сервер, промежуточный хоп) и какой именно трафик ухудшать.
  2. Сначала примените настройки на «неопасном» интерфейсе или через фильтры.
  3. Замерьте базовую линию (RTT, пропускная способность, ретраи приложения).
  4. Включите профиль (delay/jitter/loss/reorder/duplicate) и зафиксируйте результат.
  5. Откатите qdisc и убедитесь, что состояние вернулось.

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

Поделиться статьей

Вам будет интересно

Linux 6.x: UDP GRO/GSO и USO (tx-udp-segmentation) для ускорения VPN OpenAI Статья написана AI (GPT 5)

Linux 6.x: UDP GRO/GSO и USO (tx-udp-segmentation) для ускорения VPN

UDP GRO/GSO и USO в Linux 6.x могут заметно поднять throughput в WireGuard и других UDP‑VPN, снижая нагрузку на CPU и softirq. В с ...
Linux IRQ/softirq и ksoftirqd: как убрать CPU jitter и настроить RPS/RFS, irqbalance и ethtool OpenAI Статья написана AI (GPT 5)

Linux IRQ/softirq и ksoftirqd: как убрать CPU jitter и настроить RPS/RFS, irqbalance и ethtool

Если ksoftirqd грузит CPU, растёт softirq и «пилит» задержка, обычно виноваты сетевые IRQ и распределение очередей. Разберём /proc ...
Linux: eBPF для сетевого troubleshooting — где теряются пакеты (tc/qdisc/conntrack) OpenAI Статья написана AI (GPT 5)

Linux: eBPF для сетевого troubleshooting — где теряются пакеты (tc/qdisc/conntrack)

Когда «тормозит сеть», виноваты не всегда приложение и не всегда канал. Ниже — практичный eBPF-плейбук: как поймать TCP retransmit ...