интернет шейпинг что это

Причесываем трафик — динамический шейпер на Linux

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это
Предположим у вас есть домашняя сеть (или не домашняя, а сеть небольшого офиса) с выходом в интернет через не очень скоростной канал. А пользователей — много, и каждый хочет что-то скачивать, да с максимальной скоростью. Вот тут перед нами встатет задача, как максимально эффективно распределить наш интернет-канал между пользователями так, чтобы они не мешали друг другу. В этой статье я опишу, как можно решить такую задачу с помощью Linux-сервера.

Сформулируем, что же мы хотим получить в результате:
1. Чтобы канал поровну делился между пользователями.
2. Чтобы канал зря не простаивал.
3. Чтобы онлайн-игры, ssh и telnet не «лагали» даже при полной загрузке канала, например торрентами.

Если интернетом будут одновременно пользоваться 10 пользователей — каждый получит в свое распоряжение 1/10 часть канала, если в данный момент активен только один пользователь — он будет использовать весь канал сам.
Добиться этого можно используя планировщик пакетов HTB, который входит в ядро linux начиная с версии 2.4.20.
интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это
Можно конфигурировать шейпер с помощью команды tc, но для более удобной и наглядной настройки я рекомендую скачать скрипт htb.init. Они использует для конфигурации htb набор конфигурационных файлов, именуемых так, что при сортировке по алфавиту их имена позволяют визуально представить себе дерево классов шейпера и удобно его редактировать.
Предположим, что у нас на сервере есть интерфейс eth0, через который мы подключены к интернет, и eth1, который «смотрит» в локальную сеть.

Управлять можно только исходящим из интерфейса трафиком, поэтому для eth0 будут правила для upload трафика пользователей, а для — eth1 — download трафика.

По умолчанию конфигурационные файлы htb.init находятся в /etc/htb/. Для начала напишем правила шейпинга для upload трафика, они у нас будут простые.
Создаем файл с именем eth0 (интерейс «смотрящий» в интернет), в него напищем следующие строки:
DEFAULT=20
R2Q=1

Параметр DEFAULT задает номер класса, к которому будет относиться трафик «по умолчанию» — обычно это класс с минимальным приоритетом. Параметр R2Q влияет на работу алгоритма разделения канала и зависит от ширины канала. Я подбирал его значение эмпирическим путем, для моего исходящего канала в 2 Mbit.

Далее, создадим файл eth0-2.full2MBit, для класса включающего в себя весь доступный интернет-канал. Имя файла состоит из имени интерфейса и id класса, после точки идет смысловое имя класса, используется как комментарий и системой игнорируется.
RATE=2Mbit
CEIL=2Mbit

RATE — это наша гарантированная полоса, CEIL — максимальная полоса. Так как у меня канал с гарантированной максимальной полосой в 2 Mbit, то эти параметры у меня равны.

Теперь мы создадим по одному файлу для каждого класса трафика, который у нас будет. Я у себя создал отдельные классы для ssh трафика, а так же трафика игр World Of Warcraft и Counter Strike, хотя вы можете сделать для всего высокоприоритетного трафика один класс.

Пример для ssh — создаем файл eth0-2:10.ssh. В имени файла через двоеточие указан id родительского класса 2 и id текущего класса — 10. Идентификаторы для класса вы можете выбирать произвольно.
# class for outgoing ssh
RATE=128Kbit
CEIL=2Mbit
RULE=*:22
PRIO=1
BURST=100Kb

В параметре RATE указана гарантированная полоса для этого класса, в CEIL — максимальная. Мы выделяем для ssh 128 KBit (как минимум) и разрешаем ему загрузить весь канал (я закачивать файлы по sftp). PRIO задает приоритет класса трафика (1- максимальный, чем больше число — тем меньш приоритет). BURST задает максимальный объем трафика, который будет передан на максимальной скорости перед тем, как перейти к передаче данных из дургих классов. Установив этот параметр в достаточно высокое значение мы добиваемся того, что трафик ssh будет передан с минимальными задержками.
RULE задает правило, по которому будет отбираться трафик в этот класс.
Формат — RULE=[[saddr[/prefix]][:port[/mask]],][daddr[/prefix]][:port[/mask]]
Обратите внимание на запятую! RULE=*:22 обозначает трафик, у которого порт назначения 22, а RULE=*:22, обозначает трафик, у которого исходящий порт — 22.

Создадим так же классы для других видов трафика, и класс для трафика «по умолчанию» с id 20 (мы указали вначале что именно в класс номер 20 надо направлять трафик «по умолчанию»). В нем укажем используемую дисциплину разделения канала LEAF=sfq, для того чтобы upload поровну делился между TCP сессиями разных пользователей.

Для eth1 правила будут почти такие же, только с учетом что общая ширина канала — 100 Mbit, мы ведь хотим чтобы можно было обращаться к локальным ресурсам сервера на полной скорости, для интернет-трафика выделен отдельный класс на 2 MBit, у которого как потомки добавлены классы отдельных пользователей, разделение по классам я делал по IP адресам. Для каждого пользователя можно указать максимальную и гарантированную скорость, а так же приоритет.

После правки конфигурации перезапускаем htb.init:
/etc/init.d/htb.init restart
И правила шейпинга трафика сразу же вступают в силу.

В процессе состевления правил обычно возникает необходимость как-то визуализировать трафик, в целях отладки и мониторинга, поэтому решил написать плагин для системы мониторинга серверов munin, который бы визуализировал распределение по классам HTB трафика. Выводить решил загрузку только классов-листьев дерева, так как именно они обычно несут смысловую нагрузку.
Скачать плагин вы можете из официального репозитория плагинов munin, называется он qos_, просто скопируйте его в папку плагинов munin /usr/share/munin/plugins/ и в папке используемых плагинов /etc/munin/plugins сделайте на него символическую ссылку вида qos_eth1, где eth1 — имя интерфейса, на котором нужно мониторить загрузку.
В файле конфигурации плагинов можно добавить следущее:
[qos_eth1]
env.ignore_queue1_10 yes
env.label_name1_31 Viperet
env.label_name1_32 Cornet

Параметр env.ignore_queue позволяет не отображать на графике состояние класса с указанным id, а параметр env.label_name — задать человекопонятную метку для класса на графике.

В итоге должно получиться что то такое:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Хочу заметить, что у меня несколько нетипичная ситуация, два интернет канала на 2 и 1 Mbit, и для каждого пользователя ограничение в 2 Mbit скорости загрузки, поэтому на графике видно, что если активен один пользователь — его скорость урезается на 2 Mbit, а если несколько — суммарная скорость может достигать и трех. На таком достаточно «тесном» канале работают более 20 человек, и вполне комфортно себя чувствуют, не мешая друг другу.
Эта картинка с реально действующего сервера, и она обновляется каждые 5 минут и отображает актуальную картину загрузки канала.

Источник

Базовые принципы полисеров и шейперов

Одними из инструментов обеспечения качества обслуживания в сетях передачи данных являются механизмы полисинга и шейпинга и, может быть, это самые часто используемые инструменты. Ваш Интернет провайдер, наверняка, ограничил вам скорость именно этим.

Тема качества обслуживания не самая простая для понимания, а если вы когда-нибудь интересовались именно полисерами и шейперами, то скорее всего встречали однотипные графики, отображающие зависимость скорости от времени, слышали термины «корзина», «токены» и «burst», может быть даже видели формулы для расчёта каких-то параметров. Хороший и типичный пример есть в СДСМ — глава про QoS и ограничение скорости.

В этой статье попробуем зайти чуть с другой стороны, опираясь на учебник Cisco, RFC 2697 и RFC 2698 — самые базовые понятия.

Первое в чём надо разобраться и на чём строится весь механизм управления скоростью — это понятие самой скорости. Скорость — величина производная, вычисляемая, нигде и ни в каком месте мы не видим её напрямую. Устройства оперируют только данными и их количеством. Про скорость мы говорим в контексте наблюдения и мониторинга, зная объём переданных данных за 5 минут или за 5 секунд и получая разные значения средней скорости.

Второе, количество данных пропускаемое интерфейсом за отрезок времени — константа, абсолют. Его нельзя ни уменьшить ни увеличить. Через 100Мбит/c интерфейс 90Мбит будут пропущены всегда за 0,9 секунды, а оставшуюся 0,1 секунду интерфейс будет простаивать. Но с учётом того что скорость вычисляемое значение, получим что данные были переданы со средней скоростью 90Мбит/c. Это мало похоже на дорожный трафик, у нас всегда либо 100% загруженность, либо полный простой. В контексте сетевого трафика, загруженность интерфейса — это сколько свободных промежутков времени у него остаётся из общего измеряемого интервала. Дальше продолжим употреблять размерность Мбит и секунды, для лучшего понимания, хотя это и не имеет никакого значения.

Отсюда вытекает основная задача и способ ограничения пропуска трафика — передать не больше заданного количества данных за единицу времени. Если у нас есть 100Мбит, а мы хотим ограничить скорость 50Мбит/c, то за эту секунду нам надо передать не больше 50Мбит, а оставшиеся данные передать в следующую секунду. При этом у нас есть только один способ это сделать — включить интерфейс, который всегда работает с постоянной скоростью, или выключить его. Выбор только в том, как часто мы будем включать и выключать.

Burst

Посмотрим на график скорости из учебника 5 класса по физике. Здесь показана зависимость объёма переданных данных по оси Y, от времени по оси X. Чем круче наклон прямой, тем больше скорость. Передать 50Мбит за секунду можно разными способами:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

В случае burst=25, получим 25/50 или 0,5 секунды между передачей каждых 25Мбит. С учётом скорости интерфейса на передачу 25Мбит будет затрачено 25/100=0,25 секунды и следующие 0,25 секунды интерфейс будет простаивать. В каждом случае мы 1/2 = CIR / Interface rate времени тратим на передачу и 1/2 на простой. Если увеличить CIR до 75Мбит/c, то соответственно 75/100=3/4 периода займёт передача и 1/4 пауза.

Обратите внимание что наклон прямых, показывающих объём переданных данных, всегда одинаков. Потому что скорость интерфейса константа (синие точки) и мы физически не можем передавать с другой скоростью.

В большинстве случаев при конфигурации оборудования используются именно burst, хотя могут использоваться и временные интервалы. На графике хорошо видны отличия, меньший burst даёт более строгое следование заданному ограничению — график не убегает далеко от СIR, даже на меньшем измеряемом промежутке и при этом обеспечивает короткие паузы между моментами передачи. А больший burst не ограничивает трафик на коротких отрезках. Если измерять скорость только за первые 0,5 секунды, то получилось бы 50/0,5 = 100Мбит/c. А долгая пауза после такой передачи может негативно сказаться на механизмах управления трафиком за границами нашего устройства, или привести к потере логического соединения.

Если быть ближе к реальности, сетевой трафик, как правило, не передаётся непрерывно, а имеет разную интенсивность в разные моменты времени (зелёный пунктир):

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

На этом графике видно, что за 1 секунду мы хотим передать 55Мбит при ограничении 50Мбит. То есть, реальный трафик практически не выходит за границы которые мы установили к концу измеряемого интервала. При этом механизмы ограничения приводят к тому, что передаётся меньший объём данных чем мы ожидаем. И здесь больший burst выглядит лучше, так как захватываются интервалы где трафик действительно передаётся, а меньший burst и желание строго ограничивать трафик на всём участке, выливается в большие потери.

Шейпер

Будем ещё ближе к реальности, в которой всегда имеется буфер для передачи данных. С учётом того что интерфейс у нас или занят на 100% или простаивает, а данные могут поступать одновременно из нескольких источников быстрее чем интерфейс может их передавать, то даже простейший буфер формирует очередь, позволяя данным дождаться момента передачи. Он также позволяет компенсировать те потери которые у нас могут возникнуть из-за введённых ограничений:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Policer это график Burst 5 с предыдущего изображения. Shaper тот же график Burst 5, но с учётом буфера, в котором задерживаются не успевшие передаться данные и которые могут быть переданы чуть позже.

В результате, мы полностью обеспечили наши требования по ограничению трафика «сгладив» пики источника и не потеряв данные. Трафик по-прежнему имеет пульсирующую форму: чередующиеся периоды передачи и паузы — потому что мы не можем повлиять на скорость интерфейса и управляем только объёмом передаваемых за раз данных. Это тот самый график сравнения шейпера и полисера из СДСМ QoS, но с другой стороны:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Какой ценой мы этого достигли? Ценой буфера, который не может быть бесконечным и который вносит задержку в передаче данных. Пик на графике буфера приходится на 15Мбит, это те данные которые теряются полисером, но задерживаются шейпером. При заданном ограничении 50Мбит/c — это 15/50=300 миллисекунд, что для многих сетевых приложений уже за гранью дозволенного.

А теперь посмотрим когда эта цена играет роль, достаточно лишь чуть большей интенсивности трафика — 60Мбит/c, при ограничении 50Мбит/c:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

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

Поэтому, выбирая шейпер или полисер стоит отталкиваться от того, насколько критична дополнительная задержка, которая выше, чем больше скорость и чем интенсивнее трафик. Или стоит пожертвовать данными и потерять часть из них, учитывая что на следующем логическом уровне почти наверняка сработают механизмы восстанавливающие целостность передачи и реагирующие на заторы и потери.

Корзина

Для учёта объёма трафика переданного через интерфейс используется понятие и термин корзина. Фактически, это счётчик от максимального значения burst до 0, который уменьшается с передачей каждого кванта данных — токена. Соответственно, есть два процесса — один наполняет корзину, второй из неё забирает.

Корзина наполняется до величины burst каждый заданный интервал, при известном CIR. Для burst 5 и CIR 50, каждые 0,1 секунду, как было рассчитано чуть ранее. Но объём трафика за интервал времени может быть меньше чем заданный нами burst, так как условие ограничения — «не больше». Значит этот счётчик может не доходить до 0 и в корзине остаются токены. Тогда в следующий интервал, при заполнении корзины, неиспользуемый объём данных (токенов) будет потерян.

Такая ситуация видна на графике Policer выше, каждые 0,05 секунд мы в состоянии передать 5Мбит на скорости интерфейса, но количества данных которые у нас есть всего 3Мбит, так как скорость поступления данных всего 60Мбит/c. Именно поэтому график почти сливается с CIR, что не совсем корректно. Передача в любом случае осуществляется на скорости интерфейса и 3Мбит будут переданы за 0,03 секунды, а оставшиеся 0,02 будет пауза. Это давало бы нам характерную лесенку, которую мы видим на графике Shaper. Здесь, как раз, пример средней скорости и сглаживания точности измерения, что обычно показывают системы мониторинга оперирующие даже не секундами, а минутами.

Улучшим подход, зная что трафик спонтанен и больший burst может помочь не потерять данные. Введём ещё одну корзину, куда будем складывать неиспользованные на предыдущем интервале токены. Таким образом, в случае отсутствия трафика от источника, будет частично компенсироваться этот простой, как если бы у нас был больший burst. Для каждой корзины задаётся собственный burst, для основной — Committed Burst, CBS, Bc. А для второй — Excess Burst, EBS, Be. Таким образом максимальный объём данных который может быть непрерывно передан равен CBS+EBS.

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Shaper Exs (жёлтый) — график с учётом двух корзин, каждая объёмом burst в 5Мбит. Shaper — график с предыдущего изображения. Теперь максимальный burst=EBS+CBS=10 и первые 0,1 секунду мы используем его. Основную корзину мы пополняем каждые 5/50=0,1 секунду. Соответственно, в момент времени 0,1 опять есть возможность передавать данные и период непрерывной передачи длится 0,15 секунды. В результате длительного простоя, когда трафика с источника не было и все данные из буфера мы передали, в момент времени 0,6 секунд, добавляем неиспользуемый объём данных во вторую корзину. Таким образом, получаем возможность снова вести непрерывный пропуск трафика в течение 0,15 секунд, что позволяет вовсе не использовать буфер. В итоге, получили удачный компромисс точности нарезки полосы, в случае интенсивного трафика, и лояльности в отношении всплесков при использовании большего burst.

Сделаем ещё одно улучшение касающееся времени. Избавимся от периодического процесса пополнения корзины и заменив его на пополнение только в те моменты, в которые к нам поступают данные. В большинстве случаев, меньше чем одним, целым пакетом за раз никто не оперирует. Поэтому можно посчитать период между приходами последовательных пакетов и пополнять корзину тем объёмом данных, которые соответствуют данному периоду. Это, во первых, исключит необходимость держать отдельный таймер для временных интервалов связанных с burst периодами, а во вторых, сократит периоды между возможными пропусками трафика.

Полисер: 1 скорость, 2 корзины, 3 цвета

До этого речь шла, в основном, о шейперах, хотя понятия и термины аналогичны тем что применимы и для полисеров. Полисер, однако, как это определено в RFC 2697 это не механизм ограничения трафика, это механизм его классификации. Каждый проходящий пакет, в соответствии с заданным CIR, CBS и EBS относится к одной из категорий (цвету): conform (green), exceed (yellow), violate (red). На устройствах можно сразу настроить в каком случае трафик стоит блокировать, но в общем случае, это именно назначение метки или покраска.

Для каждого пакета происходит проверка по следующему алгоритму:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Используем те же параметры что и раньше CIR=50, CBS=5, EBS=5. Количество токенов в корзинах теперь показано отдельно: основная Bucket C (голубой) и дополнительная Bucket E (фиолетовый). Теперь у нас не непрерывный поток битовых данных, а пакеты по 5Мбит. Что не совсем реально, трафик, в общем случае, состоит из разных по размеру пакетов приходящих в разные интервалы времени, и это очень сильно может изменить картину происходящего. Но для демонстрации базовых принципов и удобства подсчёта используем такой вариант. Также, отражён процесс пополнения корзины с приходом каждого пакета.

В первые 0,05 секунд передаём пакет в 5Мбит, опустошая основную корзину. С приходом второго пакета мы пополняем её, но на величину 2,5Мбит, что соответствует заданному CIR 0,05*50. Этих токенов не хватает для передачи следующего пакета в 5Мбит, поэтому мы опустошаем вторую корзину, но пакет помечается по другому. Через 0,05 секунд опять приходит пакет, и мы опять пополняем основную корзину на 2,5Мбит и этого объёма хватает для его передачи в зелёной категории. Следующему пакету, несмотря на то что корзина пополняется, уже не хватает токенов и он попадает в красную категорию. Красный сплошной график отражает ситуацию, если отбрасываются только пакеты помеченные красным.

Во время простоя корзины не пополняются, как это было видно на предыдущем графике, но в момент времени 0,6, при получении следующей порции данных высчитывается интервал между пакетами: 0,6-0,3=0,3 секунды, следовательно у нас есть 0,3*50=15Мбит для того чтобы пополнить основную корзину. Максимальный её объём CBS=5Мбит, остатком пополняется вторая корзина, тоже объёмом EBS=5Мбит. Оставшиеся 5Мбит мы не используем, тем самым трафик с очень длинными паузами всё равно ограничивается, чтобы не допустить ситуации: час бездействия — час без ограничений.

В итоге, на графике 6 зелёных участков или 30Мбит переданных за секунду — средняя скорость 30Мбит/c, что соответствует использованию только одной корзины и двух цветов. 3 жёлтых участка и в сумме с первым графиком 45Мбит/c, с учётом красных участков 55Мбит/c — две корзины, три цвета.

2 скорости, 3 цвета

Существует ещё один подход RFC 2698, в котором задаётся параметр пиковой скорости PIR — Peak Information Rate. И в этом случае используются две корзины, но каждая из которых заполняется независимо от другой — одна в соответствии с CIR, другая с PIR:

Трафик, как и в предыдущем случае, классифицируется на 3 категории следующим образом:

Вспомним для чего нам вторая корзина и больший burst, чтобы компенсировать периоды простоя трафика за счёт менее строго ограничения за больший период. Подход с двумя условиями даёт нам ту же возможность. Сформируем PIR и CIR равными 50Мбит/c, размер первой корзины 5, а второй PBS 10. Почему 10? Потому что это независимое ограничение, что возвращает нас к самому первому графику показывающему разницу burst. То есть, мы хотим добиться среднего результата между burst 5 и burst 10 и задаём эти условия напрямую.

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Получили такой же график для полисера, что и при использовании предыдущего метода. При burst=10 получаем больше свободы, а вторым условием burst=5 добавляем точности. Обратите внимание как ведут себя корзины, каждая сама по себе.

Два отдельных условия, каждое из которых выполняется для одних и тех же входящих значений. Более строгое — классифицирует трафик, который гарантированно попадает под него, а менее строгое расширяет эти границы. В случае равных CIR и PIR, получаем взаимозаменяемый с предыдущим метод.

А устанавливая PIR скорость увеличиваем количество степеней свободы. Можно отдельно проверить разные burst при разных профилях трафика с разными CIR, а потом совместить всё вместе с использованием этого метода:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

CIR=50, PIR=75, CBS=5, PBS=7,5. CBS и PBS выбраны таким образом, чтобы укладываться в одинаковый интервал. Но, конечно, можно исходить из других условий, например для PIR сделать меньший burst, чтобы увеличить гарантию не выхода за обозначенные границы, а для CIR наоборот, более лояльный.

В принципе, при интенсивном трафике нет причин ставить маленький burst ни в каком случае, ни для какой из корзин. Несколько десятков секунд и несколько лишних мегабайт не сделают погоды на продолжительных временных интервалах, но частые блокировки из-за небольшого burst, могут сломать незаметные для нас механизмы регулировки потока. Для трафика 80Мбит/c, в зелёную зону уложилось 40Мбит/c. Учитывая жёлтую как раз вписались между CIR и PIR — 60МБит/c. Ещё раз, механизмы ограничения пытаются гарантировать не выход за верхние границы, про нижние они ничего не знают. И как видно в примерах, результирующий трафик всегда меньше заданных ограничений, иногда сильно меньше, даже если он сам попадал в них без посторонней помощи.

Описанные выше подходы сформировались в RFC уже больше 20 лет назад, но на свалку истории пока не торопятся, и часто применяются именно как ограничивающий инструмент ухудшающий качество, а не как классификатор, или как компенсирующий механизм. Даже в самых современных реализациях вы обязательно встретите если не эти алгоритмы, но эти термины обязательно и, конечно, сложность применения понятия скорости в сетях передачи данных. Может быть с ещё одной статьёй разобраться во всём этом станет чуть проще.

Источник

Как мы оптимизировали сетевой шейпер Linux в облаке с помощью eBPF

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Меня зовут Леонид Талалаев, я занимаюсь разработкой внутреннего облака Одноклассников one-cloud, про которое уже рассказывали на Хабре.

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

В облаке на одном физическом сервере могут одновременно работать десятки контейнеров, конкурирующих за ресурсы. Чтобы обеспечить надежную и эффективную работу, необходимо управлять распределением ресурсов между контейнерами.

Для управления сетевым трафиком до недавнего времени мы использовали решение на основе дисциплины Hierarchical Fair Service Queue из Linux Traffic Control. Сегодня пойдет речь про проблему масштабирования в Linux Traffic Control, известную как root qdisc locking. И про то, как нам удалось ее решить, переделав управление сетевым трафиком с использованием eBPF.

Приоритеты задач в облаке

В облаке one-cloud трафик можно поделить на:

prod трафик: приоритетный трафик задач с низкой задержкой – фронты, базы данных, сервисы, обрабатывающие пользовательские запросы;

nonprod трафик: весь остальной трафик – фоновые расчеты, миграция данных и др.

Гарантии для prod и nonprod трафика разные:

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

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

Среднее потребление prod-задач, как правило, намного ниже их квоты: у нас соотношение составляет в среднем 6%. Поэтому совмещение prod и nonprod задач приводит к значительной экономии ресурсов за счет овераллокации: nonprod задачи могут потреблять всю свободную пропускную способность, не использованную prod задачами.

Контейнеры и классы трафика

Один prod контейнер может генерировать разные виды трафика – трафик, относящийся к обработке пользовательских запросов и трафик, относящийся к различным фоновым процессам. Таким, как перебалансировка данных в системе хранения блобов, repair и node bootstrap в apache cassandra, и другим процессам, которые не занимаются непосредственно обработкой запросов пользователей. Такой фоновый трафик непостоянен и в пике может превышать «обычный», создаваемый пользовательской активностью. Если не разделять эти виды трафика, то фоновые операции могут начать влиять на сетевую задержку обычных запросов. Поэтому, скорость фоновых операций пришлось бы ограничивать и/или резервировать под них дополнительную пропускную способность, ухудшая утилизацию оборудования.

В one-cloud фоновый трафик prod контейнеров может помечаться как nonprod и шейпиться независимо от остального трафика контейнера. Это позволяет нам не включать такой трафик в квоту контейнера. При этом мы можем не ограничивать скорость фоновых операций, позволяя им использовать всю свободную пропускную способность сети. Помимо трафика фоновых операций, мы приоритизируем также CPU и дисковый ввод-вывод с помощью шедулеров CFS и BFQ – об этом мы расскажем в следующих статьях.

Итого каждому prod контейнеру соответствует два класса трафика – prod и nonprod. Также есть отдельный nonprod класс для трафика, не относящегося к контейнерам (загрузка образов из реестра, например). Поэтому правильнее было бы говорить не о трафике контейнеров, а о классах трафика.

Но чтобы не усложнять изложение излишними деталями, далее по тексту будем подразумевать, что контейнер = задача = один класс трафика.

Требования к сетевому шейперу

Мы хотим, чтобы задержка prod задачи как можно меньше зависела от наличия других задач на том же хосте – prod или nonprod.

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

Приоритизация – prod получает ресурс сетевой карты до nonprod.

Квотирование (rate limit) – трафик каждой prod задачи можно ограничить сверху. Это означает, что на сервере с 10 Гбит/с свободной полосы можно разместить 10 prod задач с квотой 1 Гбит/с так, чтобы они не мешали друг другу.

Разделение полосы (link sharing) – у каждой nonprod задачи есть вес, который определяет в какой пропорции она получает свободную полосу трафика, не использованную prod задачами.

Дисциплины и фильтры в Linux Traffic Control

Перечисленные выше требования можно удовлетворить с помощью Linux Traffic Control – это часть сетевой подсистемы Linux, которая позволяет настраивать на сетевом интерфейсе различные дисциплины очередей (queueing discipline, сокращенно – qdisc). Исходящие пакеты сначала попадают в дисциплину, а только потом на сетевой интерфейс. Дисциплина, в зависимости от её алгоритма, может делать с пакетами различные действия: складывать в очереди, приоритизировать, модифицировать, в некоторых случаях – дропать.

Classfull дисциплины распределяют пакеты по классам, для каждого из которых настраивается дочерний qdisc со своей логикой. В отличие от простых (classless) дисциплин, они оперируют классами, а не пакетами. Т.е. их алгоритмы определяют выбор класса, из которого в данный момент отправлять пакеты. Приоритизация пакетов внутри классов определяется дочерними qdisc.

Задавать распределение пакетов по классам в classfull дисциплинах можно с помощью фильтров. Фильтр содержит условие и значение класса (flowid), который назначается пакетам, удовлетворяющим условию.

Возможности фильтров шире, чем просто классификация пакетов для qdisc. К ним можно привязывать действия, которые будут применены к пакетам, попадающим под фильтр. Например, изменить пакет, вызвать BPF программу, перенаправить на другой сетевой интерфейс или дропнуть.

Существуют две специальные псевдо-дисциплины – ingress и clsact. «Псевдо» – потому что они не являются дисциплинами в чистом виде, т.е. не реализуют никакой логики по обработке пакетов. Их можно настраивать на сетевом интерфейсе параллельно с основной дисциплиной. Единственное их назначение – возможность вешать фильтры на входящие (ingress) или исходящие (egress) пакеты. Дисциплина clsact включает в себя ingress и позволяет вешать фильтры на оба направления.

С помощью clsact можно добавлять фильтры для исходящих пакетов в тех случаях, когда основная дисциплина не поддерживает добавление фильтров напрямую (classless и multiqueue дисциплины, про последние речь пойдет чуть позже).

В предыдущем решении для управления трафиком мы использовали дисциплину hfsc (Hierarchical Fair Service Curve) с дочерними дисциплинами fq. Подробности настройки этого решения можно найти в этой статье. Для понимания текущей статьи детали настроек не имеют принципиального значения. Проблема, о которой пойдет речь, актуальна не только для hfsc, но и любой другой дисциплины (про исключения мы тоже поговорим).

Проблема блокировок Linux Traffic Control

За несколько лет производительность используемых нами серверов существенно выросла: с 8-и ядерных с 128-и Гб памяти и сетью 2 Гбит/с до 128-и ядерных с 1 Тб памяти и 20 Гбит/с сетью. Соответственно, выросло число контейнеров и общая нагрузка, которая приходится на отдельный сервер. Все чаще мы стали сталкиваться с парадоксальной ситуацией: после переноса контейнера на более мощный сервер, он иногда начинал работать хуже. При увеличении числа сетевых пакетов примерно свыше 200К/сек начинался заметный рост сетевой задержки prod задач. При нагрузке 600K пакетов/сек – потребление CPU вырастало в 1.5–2 раза, сетевая задержка задач вырастала в 5–6 раз. Чем мощнее сервер, тем чаще проявлялась данная проблема.

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что этоРост сетевой задержки сервиса при сетевой активности соседнего контейнера

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Оранжевым цветом отмечены фреймы, относящиеся к ядру Linux. Как легко догадаться из названия, метод native_queued_spin_lock_slowpath – это ожидание получения блокировки spinlock.

Чтобы разобраться, в какой момент берется блокировка, посмотрим на верхушку flamegraph:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

Метод __dev_queue_xmit относится к сетевому стеку – через него проходят все пакеты, отправляемые с сетевого интерфейса. По коду __dev_queue_xmit можно понять, что блокировка берется внутри __dev_xmit_skb (который является inline методом, поэтому отсутствует в стеке вызовов):

spinlock_t *root_lock = qdisc_lock(q);
.
spin_lock(root_lock)
. //обработка пакета
spin_unlock(root_lock)

В данном коде q – это корневая сетевая дисциплина, настроенная для сетевого устройства, а qdisc_lock(q) – объект блокировки, связанный с ней.

Проблема масштабирования Linux Traffic Control на больших нагрузках известна как root qdisc locking. Например, вот слайд про нее из доклада Jesper Dangaard Brouer, Principal Kernel Engineer из Red Hat:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что это

На слайде root_lock – это глобальная блокировка корневой qdisc дисциплины, разделяемая всеми процессами. И на каждый пакет эту блокировку нужно взять дважды. Проблема усугубляется тем, что это spinlock – во время ожидания процессорное время тратится впустую. Что в случае prod задач может вести к превышению квоты по cpu и, как следствие, к троттлингу.

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что этоПроблема root qdisc locking

Чем мощнее сервер, тем больше контейнеров там можно разместить, тем больше они создадут потоков и тем больше будет конкуренция за root qdisc lock. И рано или поздно наличие блокировки станет узким местом. У нас наиболее заметно проблема стала проявляться на серверах со 128 ядрами и полосой 20 Гбит/с – настолько, что нам пришлось отказаться от их эксплуатации до решения данной проблемы.

Lockless и multiqueue дисциплины

Из всех сетевых дисциплин Linux Traffic Control не имеют глобальной блокировки следующие (в последней версии ядра, на момент написания статьи – 5.13):

noqueue – отсутствие qdisc

lockless qdiscs, которые используют lock-free алгоритмы вместо блокировки. Единственная такая дисциплина – pfifo_fast, которая умеет приоритизировать, но не ограничивать скорость или делать link sharing. Возможно, в будущем появятся lockless реализации других дисциплин. Например, предпринимались попытки сделать lockless реализацию HTB.

multiqueue qdisc: mq и mqprio (mq не надо путать с multiq, у которой есть глобальная блокировка).

Поскольку нам нужно уметь ограничивать скорость контейнеров, варианты 1 и 2 нам не подходят. Остаются только варианты с multiqueue qdisc.

На данный момент практически все сетевые карты серверного уровня имеют несколько очередей, которые позволяют распределить нагрузку по обработке пакетов между несколькими ядрами процессора. Дисциплина mq позволяет на каждую очередь сетевой карты назначать свой дочерний qdisc.

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что этоСхема работы дисциплины mq

Классификация пакета (определение в какой дочерний qdisc он попадет) происходит неблокирующим образом. В дисциплине mq очередь выбирается как хэш flowid пакета по модулю числа очередей. Где flowid – набор из protocol, src ip, dst ip, src port, dst port.

Использование flowid позволяет избежать изменения порядка пакетов (reordering) из одного соединения вследствие отправки пакетов через разные очереди сетевой карты. Для TCP и многих UDP протоколов reordering может привести к ненужным пересылкам пакетов и снижению производительности.

У каждой очереди сетевой карты свой qdisc, и они друг с другом никак не взаимодействуют. Обработка пакетов в дочерних qdisc происходит параллельно без глобальной синхронизации, что увеличивает производительность, но ограничивает возможности по управлению трафиком рамками одной подочереди.

Если трафик контейнера будет распределяться по разным очередям, то его нельзя будет ограничить. Поэтому, чтобы использовать mq с ограничением скорости трафика контейнеров, трафик каждого контейнера нужно загнать в одну очередь сетевой карты. Для очереди настроить qdisc, лимитирующий скорость этого контейнера.

Переопределить выбор очереди для mq (и других дисциплин) можно, устанавливая у пакета атрибут queue_mapping до того, как он попадет в qdisc. Один из вариантов, как это сделать – фильтр пакетов с действием skbedit. Дисциплина mq будет использовать значение queue_mapping как номер очереди вместо хэша от flowid.

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что этоСхема один qdisc на контейнер (мы так делать не будем)

Хотя такое решение будет работать, в нем есть несколько недостатков. Во-первых, оно приведёт к деградации производительности, так как обработка пакетов одного контейнера будет производиться одним ядром процессора. Во-вторых, число контейнеров будет ограничено числом очередей сетевой карты, что не всегда приемлемо (часть карт в наших ДЦ имеют всего по 8 очередей).

В-третьих, вместо глобальной блокировки мы получили бы блокировку на каждый контейнер, что конечно сильно лучше, но не исключит проблему для контейнеров, генерирующих много трафика. Поэтому мы стали искать другое решение.

Earliest Departure Time и дисциплина fq

В Linux Kernel было внесено изменение (оно присутствует в ядрах 4.20 и выше), которое позволило задавать для каждого исходящего пакета самое ранее время отправки (earliest departure time или EDT). Это время затем учитывается дисциплиной fq как время, раньше которого пакет нельзя отправлять на сетевую карту.

Дисциплина fq хранит неотправленные пакеты в виде набора FIFO очередей. Одна FIFO-очередь соответствует одному flow. Как и в случае с mq, flow определяется набором из protocol, src ip, dst ip, src port, dst port. Пакеты одного flow отсылаются всегда в порядке поступления. Предполагается, что в рамках одного flow EDT пакетов не уменьшается – в противном случае соблюдение EDT не будет гарантировано. Но это требование вполне логичное, так как с точки зрения приоритизации сетевого трафика пакеты одного соединения относятся к одному классу и нет необходимости менять их порядок.

Те flow, время отправки пакетов в которых еще не наступило, хранятся в дереве ожидающих отправки flows. В нем flows отсортированы по EDT первого пакета в flow. Как только EDT наступает, flow переносится в связанный список готовых к отправке flows. В этом списке flow обрабатываются в порядке round-robin по циклам (dequeue rounds). За каждый такой цикл из одного flow отправляется пакетов суммарной длиной не более quantum байт (задается параметром fq). После чего цикл переходит к следующему flow. Если очередной пакет из flow имеет EDT в будущем, то этот flow переносится в дерево ожидающих отправки flows.

Независимо от того, в какую очередь и, соответственно, дочернюю дисциплину fq попал пакет, он будет отправлен в соответствии с установленным в нем EDT. Т.е. больше нет требования иметь ровно одну дисциплину fq на контейнер. Например, можно распределять пакеты дисциплиной mq по дочерним fq и получить очень хороший параллелизм – у 10G карт Intel серий X500 и X700, которые мы используем, по 64 очереди, а на некоторых серверах стоит несколько таких карт.

Поделив длину пакета на скорость, мы получим минимальное время, через которое мы можем отправить следующий пакет, не превышая заданную скорость. Таким образом, получаем следующую формулу для расчета EDT следующего пакета:

EDT = время + длина_пакета / скорость

Остается вопрос – как проставлять EDT?

BPF спешит на помощь

Тут на помощь приходит технология eBPF (или просто BPF). Тем, кто не знаком с ней, рекомендую почитать вводную статью на Хабре. Программа на BPF типа BPF_PROG_TYPE_SCHED_CLS может перехватывать все исходящие пакеты до того, как они поступят в qdisc. Программа получает на вход указатель на структуру __sk_buff, описывающую пакет, которая содержит в том числе:

wire_len : длина пакета в байтах

tstamp : собственно, Earliest Departure Time

Получается, можно реализовать BPF шейпер, который будет проставлять пакетам EDT. Далее они будут поступать в mq и затем в соответствующий fq qdisc, который будет отправлять пакет в заданное время:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что этоЛимитирование трафика на основе BPF, EDT и fq

Данный подход применяет Google для шейпинга исходящего во внешний мир трафика. Также он реализован как часть Cilium – сетевого data-plane для Kubernetes на базе BPF.

Упрощенный пример расчета EDT внутри BPF кода:

Здесь now – текущее время, t_next – EDT в наносекундах следующего пакета, которое мы запоминаем в состоянии программы, upper_limit_bps – скорость в байтах/секунду.

Защита от переполнения очереди

Bursting

Сила BPF — в возможности реализовывать произвольную логику, а не ограничиваться возможностями встроенных сетевых дисциплин. Например, можно реализовать bursting – разрешать превышать лимит в течение короткого интервала времени. Поскольку bursting не возникает одновременно на всех контейнерах, это можно сделать достаточно безопасно. Мы нашли оптимальным включение burst в течение 5мс для prod задач – это позволило снизить сетевую задержку для некоторых сервисов до 30%. Добавить bursting в наш BPF шейпер очень просто:

Доводим BPF шейпер до ума

Для реализации полноценного шейпера на BPF предстоит сделать еще несколько вещей:

Control plane. Заполнением перечисленных выше BPF maps занимается user space демон, который у нас называется «миньон». Он написан на Java, для доступа к возможностям BPF из Java мы используем библиотеку one-nio.

Статистика. В BPF мы считаем число отправленных/дропнутых пакетов, их длину в байтах, гистограмму задержки EDT. Статистика складывается в BPF_MAP_TYPE_PERCPU_ARRAY и читается раз в секунду из user space демона, который пересылает ее в агрегированном виде в подсистему статистики.

Пока наш шейпер умеет только ограничивать скорость, но нам нужно удовлетворить два других требования – приоритизацию и link sharing.

Приоритизация дисциплиной mqprio

Требование приоритизации означает, что все пакеты prod контейнеров должны быть отправлены на сетевую карту до того, как начнётся отправка nonprod пакетов (при условии, что их EDT уже наступило). В этом нам может помочь дисциплина mqprio.

Дисциплина mqprio расширяет mq возможностью определить набор классов (traffic class), а также и каждому из них назначить диапазон очередей сетевой карты. При отправке через дисциплину пакеты делятся сначала по классам, затем – по соответствующим этому классу очередям на основании хэша от flowid пакета.

Нам нужно настроить в mqprio два traffic class – для prod и для nonprod трафика, а также поделить очереди сетевой карты между ними. Классификация пакетов происходит на основе поля priority сетевого пакета, которое может принимать значения 0–15. При настройке mqprio передается параметр map, в котором перечисляются traffic class для каждого возможного значения priority.

После классификации пакета BPF программа выставляет skb->priority = TC_PRIO_INTERACTIVE для prod трафика и TC_PRIO_BESTEFFORT для nonprod. Дисциплина mqprio на основе приоритета определяет traffic class и направляет пакет в соответствующую очередь:

интернет шейпинг что это. Смотреть фото интернет шейпинг что это. Смотреть картинку интернет шейпинг что это. Картинка про интернет шейпинг что это. Фото интернет шейпинг что этоЛимитирование и приоритизация трафика с помощью BPF, mqprio, EDT и fq

Приоритизация на сетевой карте

Дисциплина mqprio также умеет настраивать поддержку приоритизации со стороны сетевой карты (hardware QoS), которая включается параметром hw 1. Это позволяет добиться практически идеальной приоритизации – по результатам тестов влияние nonprod на prod полностью отсутствовало.

Однако с этим возникли сложности:

Во-первых, часть карт старых моделей не поддерживало данную возможность.

Во-вторых, на картах Intel X700 возник конфликт настроек.

Подробности под катом

для поддержки HW QoS нужно было включить DCB в firmware карты, который включался только вместе со встроенным в карту LLDP, а он в свою очередь мешал работе SW реализации LLDP, необходимой нам для интеграции с другими системами.

В-третьих, на Intel картах поддержку hardware QoS нельзя включить одновременно с XDP native mode, который мы планируем использовать для других задач (балансировщика нагрузки).

Получалось, что включение hardware QoS в датацентрах потребовало бы заменить сетевые карты на 30% машин и отказаться от возможности в будущем использовать XDP.

Забегая немного вперёд – помимо исходящего трафика нам нужно шейпить также и входящий. А на виртуальном интерфейсе ifb поддержка hardware приоритизации не работает в принципе.

Поэтому мы стали искать способ приоритизации, который сможет работать без hardware QoS.

Приоритизация без поддержки со стороны карты

Заметим, что даже без hardware QoS сам факт разделения nonprod и prod трафика по разным очередям сетевой карты снижает влияние nonprod на prod. В случае, если они не разделены по очередям (как было в нашем старом решении), после того как пакеты отправлены в сетевую карту, там могут возникнуть свои задержки из-за кратковременной заполненности полосы или по другим причинам. В результате получается ситуация, когда в одной очереди карты перемешаны пакеты разных приоритетов, а отправка их происходит в том порядке, в котором они были получены, т.к. повторной приоритизации в этой очереди нет, что влечёт рост задержки prod задач.

В итоге мы пришли к следующему решению: делить очереди для prod и nonprod трафика в пропорции 7:1. Т.е. даем prod трафику в 7 раз больше очередей. Например, если на сетевой карте восемь очередей, даем семь prod и одну – nonprod трафику. Это позволяет снизить вероятность конкуренции prod и nonprod пакетов за физическую полосу карты, т.к. отправкой prod пакетов занимается в 7 раз больше очередей. В такой конфигурации в нагрузочных тестах с использованием iperf максимальное влияние nonprod трафика на задержку prod задач составило не более 15%.

Дополнительно мы ограничиваем лимит nonprod задач так, чтобы общая утилизация сетевой карты не превышала 80%, потому что согласно нашим наблюдениям, при превышении этого порога начинается деградация latency даже на серверах без шейпинга. Это позволило ещё сильнее уменьшить влияние – на реальных задачах оно не превышает 5%, что нас вполне устраивает. И это решение оставляет возможность включить hardware QoS, если это когда-то будет необходимо.

Настройка сетевой подсистемы

Для полноты приведем команды для настройки BPF шейпера. Для этого нам понадобятся утилиты ethtool и tc.

Определим число очередей на сетевой карте с помощью ethtool:

Channel parameters for eth1:
Pre-set maximums:
RX: 0
TX: 0
Other: 1
Combined: 63
Current hardware settings:
RX: 0
TX: 0
Other: 1
Combined: 56

На карте настроено 56 очередей (смотрим значение Combined). Настраиваем mqprio, отдав 49 очередей prod трафику, 7 – nonprod:

Заменяем дочерние qdisc (которые по умолчанию – pfifo_fast) на fq. Поднимаем лимиты числа пакетов, так как защита от переполнения очереди уже есть в BPF:

Добавляем clsact qdisc для возможности вешать tc фильтры на исходящие пакеты:

Загружаем скомпилированную в ELF файл BPF программу как фильтр исходящих пакетов:

Параметр da (direct-action) нужен для возможности дропать пакеты: результат BPF программы интерпретируется как действие над пакетом.

Разделение полосы nonprod

Контейнеры с уровнем изоляции nonprod могут использовать всю свободную полосу вплоть до 80% пропускной способности сетевой карты за вычетом среднего потребления prod задач. В случае, если nonprod контейнеров несколько, для разделения полосы между ними мы используем алгоритм max-min fair share.

Идея алгоритма следующая. Представим, что нам нужно поделить между тремя задачами – A, B и C полосу 900 Мбит/с. Самое простое – поделить поровну: получаем разбиение 300+300+300. Теперь представим, что нам известна желаемая доля трафика каждой задачи, и ей нет смысла давать больше. Допустим, желаемая доля A – 200 Мбит/с. Нам нужно уменьшить долю задачи A с 300 до 200, поделив разницу в 100 Мбит/с между B и C – получаем разбиение 200+350+350. Продолжаем процесс перераспределения долей, пока доля каждой задачи не будет меньше или равна ее желаемой доле. Если по окончанию у нас осталась неиспользованная доля трафика (т.е. сумма желаемых долей меньше полосы), то делим остаток между всеми задачами поровну.

Теперь немного усложним этот алгоритм – добавим для каждой задачи вес и будем делить трафик пропорционально весам. Весом для nonprod задачи мы считаем её минимальный трафик, задаваемый в манифесте.

Алгоритм выполняется в user space демоне и рассчитывает лимит каждой nonprod задачи на следующий квант времени (1 секунда). В качестве желаемой доли трафика задачи берется ее среднее потребление за предыдущий квант времени.

Отложенное применение лимита nonprod

У задач с низким потреблением трафика рассчитанная по алгоритму доля будет низкой. Но, если в следующую секунду задача захочет потребить больше, то упрется в лимит, и мы не узнаем ее реальное желаемое потребление. Получается проблема «курицы и яйца»: лимит зависит от потребления задачи, которое ограничено лимитом. Чтобы разорвать этот круг, мы не применяем рассчитанный лимит к задаче, пока её утилизация ниже 80% от лимита. Таким образом, задачи со скачкообразным изменением трафика могут сразу получить свою честную долю.

С другой стороны, это означает, что суммарное потребление всех задач может кратковременно превысить порог в 80% от пропускной способности сетевой карты, которого мы хотим придерживаться. Но поскольку мы разделили prod и nonprod трафик по разным очередям, существенного влияния на задержку prod задач это не оказывает.

Шейпинг входящего трафика

До этого речь была только про исходящий трафик. Но входящий трафик тоже нужно шейпить. Для входящего трафика мы используем точно такой же подход, как для исходящего. Отличие состоит лишь в том, что сетевые дисциплины и фильтр с BPF программой настраиваются не на сетевом интерфейсе, а на виртуальном интерфейсе ifbX, создаваемом модулем ifb, на который переадресуются входящие пакеты.

Входящий и исходящий трафик шейпятся отдельными BPF программами, алгоритм max-min fair share и статистика по ним считаются независимо.

Не обошлось без подводных камней – после включения шейпинга входящего трафика перестала работать синхронизация времени через ntpd. Как выяснилось, реализация протокола NTP использует поле skb->tstamp и не очень хорошо относится к его модификации. В BPF программу была добавлена проверка, чтобы не менять tstamp пакетов, относящихся к NTP (UDP пакеты с src и dst портом 123). После чего синхронизация снова заработала.

Заключение

Новый шейпер развернут в наших дата-центрах на более чем 5 тыс. серверов. Через него проходит трафик всех основных сервисов Одноклассников. Полный переход на новое решение занял примерно месяц и был завершен 3 месяца назад.

Проблем деградации производительности при росте числа сетевых пакетов больше не наблюдалось. На серверах с 10-и гигабитными картами при нагрузке 2 млн пакетов/сек рост задержки prod задач остается в пределах 15% (на предыдущей версии шейпера деградация свыше 15% начиналась после 200К пакетов/сек). Также улучшилось latency prod задач – у отдельных задач до 30%.

Идею использовать EDT+fq для ограничения скорости мы взяли из статьи от Google и доработали для возможности приоритизации трафика и разделения полосы.

Разработка заняла 4 месяца силами одного человека вместе с исследованиями и экспериментами.

Ссылки

Статья, видео и слайды доклада про оптимизацию шейпера в Google: Replacing HTB with EDT and BPF

Введение в BPF и XDP на сайте Cilium: BPF and XDP Reference Guide

Статья про использование BPF для классификации пакетов: On getting tc classifier fully programmable with cls bpf

Еще одна статья про особенности использования eBPF для сетевых задач: Creating Complex Network Services with eBPF: Experience and Lessons Learned

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *