Шейпер с использованием дисциплины HTB, тонкости скоростей, quantium, r2q

Автор gardarea51, 28 января 2014, 21:29:55

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

gardarea51

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

Итак, вы задались целью организовать шейпинг транзитного трафика, идущего через ваш линукс-шлюз к клиентам локальной сети. Составили схему, написали шейпер, использовали классовую дисциплину HTB и.... получили в syslog следующие непонятные предупреждения:
htb quantum of class 10001 is big. consider r2q change

Возьмем мой пример, дисциплина и классы при написании шейпера с использованием HTB могут выглядеть так:
tc qdisc add dev $lan_ifc root handle 1: htb default 90
tc class add dev $lan_if parent 1: classid 1:1 htb rate 1gbit

tc class add dev $lan_if parent 1:1 classid 1:10 htb rate 85mbit ceil 1Gbit prio 1
tc class add dev $lan_if parent 1:1 classid 1:20 htb rate ${gw_rate}kbit
tc class add dev $lan_if parent 1:1 classid 1:90 htb rate 128kbit ceil  prio 5

tc class add dev $lan_if parent 1:20 classid 1:21 htb rate $[6*$gw_rate/10]kbit ceil ${gw_rate}kbit prio 2
tc class add dev $lan_if parent 1:20 classid 1:22 htb rate $[3*$gw_rate/10]kbit ceil ${gw_rate}kbit prio 4
tc class add dev $lan_if parent 1:20 classid 1:23 htb rate $[1*$gw_rate/10]kbit ceil ${gw_rate}kbit prio 3

Боее понятно схематично:

                           root qdisc 1:
                                |
                               1:1       
                               /|\       
                              / | \
                             /  |  \
                         1:10 1:20  1:90
                               /|\       
                              / | \
                             /  |  \
                         1:21 1:22  1:23


Теперь опишу условия. Мне нужно как-то разделить и приоритезировать интернет-трафик (Internet), идущий транзитом к клиентам, но и оставить возможность качать на скорости до гигабита файлы с самого сервера

1:10 - Сервер -> Клиенты (к примеру кач фильма с шары на Сервере)
1:20 - Интернет -> Сервер -> Клиенты
1:90 - Прочий неучтенный трафик (умолчальный класс)

Канал Интернета у меня 12Мбит/с, он является основной целью шейпинга и делится на 3 подкласса:
1:21 - высокоприоритетный трафик, получающий гарантию на 7/10 общего канала из класса 1:20
1:21 - обыкновенный трафик, получающий гарантию на 3/10 общего канала из класса 1:20
1:22 - высокоприоритетный трафик, получающий гарантию на 1/10 общего канала из класса 1:20

И вот после описания классов, классификаторов (фильтров) и запуска шейпера можно получить в syslog вышеприведенную запись об ошибке. Забегая вперед скажу, что шейпер будет работать, но будет страдать его точность и скорость обработки очередей, видимо вплоть до того, что это будет заметно невооруженным взглядом.

Ошибка говорит о том, что quantum для нашего корневого класса (класс у которого будут занимать полосу его потомки) слишком большой и советует посмотреть r2q.

Параметр r2q - это глобальная переменная, которая по умолчанию устанавливается равной 10. Чтобы понять, что эта переменная делает нужно понять, что такое quantum (квант). Приведу выдержку из руководства:
ЦитироватьСейчас стоит рассмотреть понятие квантов трафика. В процессе конкурентной передачи данных нескольких классов обслуживание каждого из них начинается после того, как обработано некоторое количество байт предыдущего класса. Это количество байт называется квантом (quantum). Когда несколько классов претендуют на канал родительского, они получают части канала пропорциональные их квантам. Важно знать, что расчет распределения канала тем точнее, чем меньше размер кванта (но квант все же должен быть не меньше величины MTU). Как правило нет необходимости задавать кванты вручную, поскольку HTB рассчитывает их самостоятельно. Размер кванта класса устанавливается (при создании или изменении класса) равным запрошенной скорости деленной на глобальный параметр r2q. По умолчанию r2q равен 10, что приемлемо для скоростей выше 15 kbps (120 kbit), поскольку обычно MTU равен 1500. Для меньших скоростей величина r2q равна 1; это подходит для скоростей от 12 kbit. При желании можно установить величину кванта вручную. В этом случае параметр r2q игнорируется. Будут выведены предупреждения о том, что установленная величина не верна, но ими можно пренебречь.
А теперь попробую описать своими словами. Раз размер пакета обычно 1500 байт (бывает меньше, но больше как правило не бывает), то минимальный размер кванта нужно установить не менее чем 1500 байт, иначе пакет не будет передан на обработку (и мы получим сообщение в syslog, что квант слишком мал). Под обработкой тут видимо понимается постановка пакета в вышестоящую очередь. Логично, что чем меньше размер кванта (пусть те же 1500 байт) - тем точнее происходит "дележка" канала, при размере в 1500 байт - дележка прямо таки попакетная, но кванты берут "числом".

Дак вот к чему это все. Получается, чтобы эффективно шейпить трафик - нужно правильно установить размер кванта в байтах, а он задается статической формулой:
quantum = rate / r2q.
А как было сказано выше r2q константно равен значению 10, так как это некое оптимальное, универсальное значение для скоростей от 120kbit. Однако у размера кванта есть жестко заданное в HTB ограничение, оно равно 60 000 байт (очередь примерно из 40 пакетов). Потому что при большем размере происходит "классовое голодание" и слишком длительные задержки в обработке очередей.

И вот тут кроется проблема: для скоростей разной "точности" кванты должны быть разными, но опять таки чем они меньше (но не менее размера пакета) - тем точнее происходит "дележка". Исходя из вышеприведенной формулы можно вычислить размер кванта для скорости 1mbit. Уточню, что:
1mbit = 1000kbit = 1000000bit =  1000000/8 байт =  125000 байт Отсюда получаем размер кванта:
quantum = 125000/10 = 12500 байт (что меньше порогового 60000), это примерно 12500/1500 = 8 пакетов.

Лирическое отступление:
По умолчанию очередью на физическом интерфейсе управляет дисциплина pfifo_fast, которая имеет очередь для обработки пакетов равную 100. То есть как только 1 пакет уйдет в сеть, останется 99 - в конец очереди можно добавить еще 1 пакетик.

В то же время класс скоростью 1mbit выдает в очередь родительского класса за раз 8 пакетов. Родительский класс помещает пакеты в свою очередь и выдает выше, где они помещаются в еще более длинную очередь. И вот пока эти 8 пакетов 1mbit'ный класс не отдаст своему родителю - остальные классы его ждут.

Теперь посчитаем квант для класса в 1Gbit при дефолтном значении r2q = 10. 1Gbit Это:
1000Mbit = 1000000Kbit = 1000000000bit = 1000000000/8 байт = 125000000 байт. Размер кванта будет:
quantum = 125000000/10 = 12500000 байт (это уже сильно больше порогового 60000). В нем помещается примерно 12500000/1500 =  8334 пакета. Пока эти 8 тысяч пакетов передаются - остальные классы ждут. Возникает огромная очередь пакетов в классе и серьезная задержка плюс вывод предупреждающего сообщения в syslog.

Что можно сделать в такой ситуации?
В первую очередь постараться не смешивать слишком высокие скорости в шейпере со слишком низкими (одному 100Mbit, другому 64Kbit), а если смешивать все таки приходится то:
- Можно задать для корневой дисциплины (qdisc) соответствующий параметр r2q
- Можно задать классам соответствующие размеры квантов, тогда параметр r2q для них будет игнорироваться

К примеру если задать для классовой дисциплины большой параметр r2q равный 8000:
tc qdisc add dev $lan_ifc root handle 1: htb r2q 8000 default 90 то размеры квантов для скоростей 1mbit, 100mbit, 1Gbit будут составлять соответственно:
1mbit - 15 байт - меньше, чем размер пакета, поэтому в очередь его поставить не получится
100mbit - 1562 байта (хороший размер > размера пакета)
1Gbit - 15625 байтов (практически 10 пакетов)

Что получается в этой ситуации. Обработка классов со скоростями от 100mbit до 1Gbit будет происходить успешно, но в то же время классы со скоростями ниже 100mbit не смогут передать пакеты в очередь родительским классам, потому что размер кванта у них меньше размера пакета. Если к примеру будет созданы классы с rate 112mbit, 305mbit, 900mbit - размер квантов для каждого класса будет разным, чем больше заданный rate - тем больше квант (тем больше в нем пакетов).

Чтобы эти классы могли успешно передавать пакеты на обработку для них персонально нужно установить размеры квантов (я не уверен можно ли устанавливать размеры квантов на классы, отличные от краевых):
$class parent 1:1 classid 1:10 htb rate 85mbit ceil 500mbit quantum 15140 prio 1

Получается 3 варианта решения проблемы:
1) Ничего не делать при сообщениях о слишком больших квантах в syslog и мириться с огромными очередями.
2) Установить для корневой дисциплины большой r2q, посчитать кванты для описанных классов и явно задать нужные значения квантов для тех классов, у которых автоматически посчитанный размер кванта меньше размера пакета (1500 байт).
3) Явно задать для всех классов их персональные значения квантов. Ну и опять же я не уверен можно ли указывать кванты не для краевых классов (кажется можно, см. ps) - буду рад, если кто-то расскажет.

Мне кажется вариант 2 (комбинированный) наиболее предпочтителен. Есть мнение, что r2q нужно высчитывать по следующей формуле:
r2q = rate_класса_с_минимальным_rate / желаемый квант (не менее 1500 байт). К примеру есть такие классы:
1:1 -> 1.10 -> 1.11, скорости такие: 1Gbit, 100Mbit, 1Mbit. Можно задать r2q такой, чтобы классы 1:1 (1Gbit) и 1:10(100Mbit) работали без явного указания quantium. Почитаем r2q для 100Mbit из расчета кванта в 1 пакет (1500 байт), для 1Gbit при полученном r2q размер кванта составит 10 пакетов. После расчетов r2q получится равным примерно 8000, это те же самые расчеты, что были выше. Таким образом, если для дисциплины мы зададим r2q равное примерно 8000, то классы с заданным rate выше 100Mbit будут работать корректно. Для классов, к которых rate меньше 100Mbit нужно просто явно указать размер кванта. Вот собственно и все решение.

Надеюсь, что описание получилось понятным. Не претендую на правоту по всему вышеописанному, если есть ошибки - буду рад узнать о них от участников форума (а может я вообще тотально заблуждаюсь и все не так понял? =).


ps: судя по всему указывать кванты можно для всех классов, в т.ч. краевых. Методом теста было выяснено, что в команде просмотра состояния классов:
tc -s -d class ls dev $iface
отсутствуют параметры prio и quantium для классов, у которых есть потомки. Но в то же время в syslog может быть выведено сообщение о слишком большом кванте класса - родителя. Получается, что он все таки должен быть задан.

Чтобы не смешивать скорости, вы можете пойти другим путем и использовать PRIO: https://debianforum.ru/index.php?topic=8163.0