Ключевые слова:traffic, bandwidth, shaper, qos, htb, (найти похожие документы)
From: Andy Gorev <gorev@mail333.com>
Date: Mon, 13 Mar 2003 13:01:37 +0000 (UTC)
Subject: Сага по ограничению трафика в Linux при помощи htb.
Оригинал: http://www.atmsk.ru/index.php?option=articles&task=viewarticle&artid=28
Сага по Hierarchical Token Bucket.
В статье Advanced Routing & QoS
(http://www.atmsk.ru/index.php?option=articles&task=viewarticle&artid=27)
я упоминал полноклассовую дисциплину HTB. Так как она в последнее время
набирает обороты, хочу поделиться некоторой информацией, которая может
оказаться полезной при ее использовании.
Начну с краткого описания HTB. Я думаю, есть хотя-бы приблизительное
представление о том, как работает classfull шэйпинг, поэтому в
описание терминологии вдаваться не нужно. Кроме того, все
заинтересованные прочитали статью про QoS.
Итак, что дает нам HTB:
1) Последняя реализация так-же быстра, как CBQ и гораздо точнее ее.
2) Это более понятно и легко конфигурируемо, чем CBQ.
3) HTB работает похоже на CBQ, но не расчитывает на временнЫе
параметры в канале в момент простоев (idle time) для разграничения
полос. Вместо этого, он работает как полноклассовый Token Bucket
Filter. У НТВ небольшое число параметров, которые неплохо
документированы.
4) Вытекает из предыдущего - классы уже работают как TBF-шэйперы и
мы можем выбирать qdisc, который будет не резать, а распределять
трафик. У CBQ.init в листьях был только TBF.
5) Решение на основе НТВ более расширяемо. Нужно мало действий,
чтобы создать минимальную конфигурацию. Про CBQ так не скажешь.
6) HTB может наследовать неиспользуемую полосу пропускания как по
горизонтали, так и по вертикали в дереве классов.
7) Наследование происходит на основе приоритетов классов и
фильтров, а так-же установленного rate.
8) Вводится понятие CIR(знакомое по frame-relay), т.е.
минимально-допустимой полосы пропускания.
9) В HTB работают все настройки, в отличие от параметров bounded
совместно с isolated в CBQ.
10) Разработка этого добра продолжается, тогда как про CBQ
Кузнецов почему-то забыл. Приходится даже патчить его утилиту tc
на предмет совместимости с HTB. В ядре HTB начиная с 2.4.20.
11) Наконец существует довольно оживленный список рассылки по
вопросам конфигурирования и функционирования HTB.
12) И последнее - при описании дерева классов, не обязательно
точно описывать родительский RATE.
Несколько генеральных ссылок по HTB:
1) http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm
2) http://www.docum.org/stef.coene/qos/faq/cache/
3) http://tldp.org/HOWTO/ADSL-Bandwidth-Management-HOWTO/index.html
Краткие правила при использовании HTB:
1) Напоминаю, что управлять можно только исходящим трафиком.
2) kbps = kilobyte/s; kb = kilobyte; kbit = kilobit/s or kilobit -
это нужно для понимания вывода статистики по сконфигурированным
классам и дальнейших рассуждений.
3) Не нужно добавлять htb/cbq/tbf qdiscs в классы - это вносит
задержки и жрет CPU. Мы будем использовать sfq и pbfifo.
4) Чем больше prio в классе, тем ниже приоритет при наследовании
полосы. Допустимый диапазон - 0-7.
5) rate <= ceil в классе.
6) sum (rate of childs) <= parent rate & ceil; в идеальном случае
sum (rate of childs) = parent rate. Если правило не соблюдается,
то на parent параметры HTB кладет (см. п.12 выше). Это допустимо
но не желательно. Реальный пример - имеем около 80 классов,
которые имеют rate=32kbit. При их суммировании, имеем почти в три
раза большее значение, чем имеющийся у нас мегабит исходящего
канала, который описан как rate родительского класса. Таким
образом HTB сохраняет свою целостность (в отличие от CBQ, у
которого съезжает крыша, и он начинает ошибаться и врать в
статистике). В случае, если предположить, что все классы будут
забиты трафиком (почти невероятно), то никто не получит свой CIR,
но будет очень близок к нему тот класс, у которого выше prio и
больше quantum.
7) Если ceil не указан, то он = rate.
8) Если класс промежуточный (не leaf и не root), в него _не_ нужно
вводить qdisc и фильтры(rules) соответственно. Приоритеты в таких
классах так-же не работают, они нужны только для leafs.
9) Приоритет класса описывает то, какие классы в каком порядке
наследуют полосу. Приоритет фильтра описывает порядок просмотра
списка фильтров при классификации.
10) HTB обычно подразумевается как класс, но так-же существует и
как qdisc, который присоединяется _только_ непосредственно к
интерфейсу (до root-class)
11) Численное значение любого класса лежит в пределах 2-ffff и
должно быть уникально в пределах интерфейса.
Теперь сообщу приятную новость. Есть-таки скрипт htb.init, который был
переписан с cbq.init и работает очень похоже. Найти его можно по
адресу http://freshmeat.net/projects/htb.init Ниже я расскажу о
нюансах в примерной реализации дерева классов на его основе. А сейчас
рассмотрим параметры НТВ, которые, кстати, вместе с примером описаны в
этом скрипте. Все слова написанные ЗАГЛАВНЫМИ буквами распознаются
htb.init как параметры. Однако, никто не запрещает писать plain-script
с вызовами tc, и для него эти параметры такие-же.
1) Для HTB qdisc устройства мы описываем DEFAULT класс по умолчанию, в
который попадает трафик не подпавший ни под одно rule, обычно это
самый низко-приоритетный трафик. Если DEFAULT не указывать, то
весь не склассифицированный трафик пойдет напрямую с наивысшим 0
приоритетом.
2) Второй параметр R2Q используется для квантования трафика в
классах. В случае малых скоростей рекомендуется значение = 1, что
позволяет шейпать с точностью до 4kbit. Если не указан, то =10,
что для скоростей >120Kbit.
3) В скриптах допустим параметр QUANTUM, но он используется только
как параметр для sfq, а не htb. При использовании htb.init для
htb-классов вычисляется автоматически, исходя из глобального
параметра R2Q и rate класса. На что влияет quantum в HTB, можно
попробовать понять из
http://www.docum.org/stef.coene/qos/faq/cache/31.html
На что влияет QUANTUM(как параметр в скриптах) для sfq - читать
http://lartc.org/howto/lartc.qdisc.classless.html#LARTC.SFQ.
4) CEIL используется для ограничения полосы в leaf(оконечном)
классе, точно как RATE в cbq.init. RATE в HTB используется для
управления тем, как полоса разбивается между дочерними классами, и
гарантирует минимальную полосу, если есть у кого наследовать. То
есть своего рода CIR.
5) Про PRIO я уже писал. Обычно у классов хостов он где-то в
середине, у интерактивного трафика - повыше, у остального (или по
умолчанию) - пониже.
6) Классификация трафика происходит так-же как и в cbq.init с
помощью параметра RULE, которых допустимо несколько. Пакет
попавший под любое rule будет отправлен в этот класс. Обращать
внимание на запятые! rule=addres, - это source; rule=addres - это
destination. Допустимы звездочки, при описании портов. При
введении rules, классификация происходит с использованием u32
классификатора, про который читать
http://lartc.org/howto/lartc.adv-filter.html#LARTC.ADV-FILTER.U32
7) Классификация например в интерактивный класс может происходить
так-же при помощи параметра MARK. Тут в дело вступает
классификатор fw, который выбирает fwmarked пакеты. Отмечаем
пакеты в фаерволе. Приоритет при просмотре фильтров у fw в скрипте
ниже, что IMHO не правильно. Для того, чтобы ловить сначала мелкие
пакеты (ACKs), а только потом по адресам-портам, надо подправить
приоритеты фильтрам прямо в htb.init. При генерации
plain-script-а, htb.init назначит всем фильтрам один приоритет,
который отличется численно для u32, fw и route классификаторов.
8) Параметры листьев: для sfq - QUANTUM и PERTURB, можно оставить
по умолчанию. Для pfifo - LIMIT. О смысле расскажу ниже в
реализации. Других qdiscs, кроме как еще bfifo, для HTB не
предусмотрено (RED не рассматриваем, да он и не поддерживается
скриптом).
9) BURST и CBURST можно не описывать, они вычисляются
автоматически. Смысл - как и PEAK в CBQ (который вы тоже скорее
всего не описывали).
Наконец примерная реализация.
В двух словах, htb.init парсит каталог /etc/sysconfig/htb, генерит
plain-script, состоящий из множества вызовов tc и запускает его. Точно
также как и cbq.init. Одно из существенных изменений - именование
файлов классов. Оно детально расписано внутри самого скрипта, так-что
приведу только пример:
eth0-2:20:0101:A101.client_out
1) Здесь до минуса - интерфейс, в его соответствующем файле описываем
параметры htb qdisc (R2Q и DEFAULT).
2) Далее id корнвого класса, в его файле только rate, для eth = 10/100Mbit.
3) Далее id=20 Это класс, объединяющий всех клиентов. Про него ниже.
4) Далее 0101 - parent класс клиента (только в случае запроса на
prio, и соответственно разделение трафика по подклассам), обычно
это просто листовой класс клиента. Чтобы разобраться, зачем это
нужно, смотрите
http://luxik.cdi.cz/%7Edevik/qos/htb/manual/userg.htm#hsharing
5) A101 - leaf класс клиента, в котором описывается qdisc.
6) Все, что после точки - смысловое описание класса.
В нашей примерной реализации я создал три класса сразу под корневым -
10: 20: и 30: Их функции таковы:
1) 10: - интерактивный трафик. Сюда попадает ssh трафик, SMB в офисе,
http на превилегированный хост - чтобы интранет работал без
ограничений. С помощью марки в таблице mangle в этот-же класс
завернуты пакеты < 64 байт, в основном это ACK-и. Нужно для
обеспечения клиентам upload-а во время download-а. А так-же
завернуты udp запросы к DNS - они маленькие, а пользы прибавят.
ICMP в этот класс не попадает, для возможности актуальной проверки
канала на загрузку. Вы конечно можете это делать по своему.
Подчеркну, что клиентский трафик, попавший в этот класс, не
шейпается его классом (если не забыли сделать fw фильтру бОльший
приоритет, чем u32 в htb.init, см. п.7 параметров), и более
приоритетен, чем его остальной трафик. Правда он достаточно мал,
чтобы слишком выбиваться на картинках клинтского трафика. Служит
для того, чтобы клиент не "убивал" одной закачкой весь свой канал.
К классу прикреплена дисциплина sfq, так как в него попадают
пакеты с разных адресов. SFQ работает по принципу русской рулетки
и не дает одному tcp виртуальному каналу все время владеть
полосой. Подробнее я писал в статье по QoS. (http://www.atmsk.ru/index.php?option=articles&task=viewarticle&artid=27) Параметр QUANTUM
оставлен по умолчанию, параметр PERTURB, так-же по умолчанию и
равен 10 сек. Это время перестройки хэша "русской рулетки", т.е.
частота рандомизации хэша. Подробно про SFQ читать - см. п.3 в
параметрах HTB. Полоса класса например 10Мбит, ceil = 100Mbit.
2) 20: - клиентский трафик. Ниже лежат все классы-листья клиентов.
Ничего особого - rate(который может быть overlimit) согласно
нашему внешнему каналу, ceil = 100Mbit(как-раз для таких случаев).
Особо остановлюсь на клиентских классах. В общем ничего мудреного,
все согласно правилам выше. Про прио я уже упоминал. В качестве
qdisc в листьях используется pfifo (packet фифо), который еще
проще, чем pfifo_fast, используемый в случае отсутствия
какого-либо qdisc. Напомню, что pfifo_fast трехполосный, и
распределение по полосам плохенько, но производилось с помощью
TOS. TOS умеют выставлять некоторые "умные" приложения, типа
openssh.
Используемый pfifo имеет одну полосу, поэтому весь трафик
в ней эквивалентен, однако в отличие от pfifo_fast имеет механизм
статистики. Единственный параметр LIMIT указывает на размер
очереди для pfifo в пакетах, для bfifo в байтах. Стандартная
очередь pfifo{_fast} = 100. Чем больше очередь, тем беспрерывнее и
"быстрее" работает одна tcp сессия, например закачка. Но и не дает
никому другому кислорода, поэтому больше задержка в канале. При
значениях по умолчанию qlen*MTU/sec=100*1500=150kbps, то есть к
примеру на идеально забитом канале в 128kbit мы можем ожидать
ответа например на пинг теоретически 150/16kbps больше 9 секунд!
Исходя из этих соображений выбирайте значения поменьше, если
хотите обеспечить интерактив.
3) 30: - класс по умолчанию, он же "опущенный". Сюда попадает весь
не описанный фильтрами в классах трафик. Qdisc = SFQ, т.к.
разномастный трафик от разных адресов.
Так-как рулеса в скриптах работают только с tcp и портами, udp и
мелкие ACK-и придется маркировать в iptables. Для этого создаем
соответствующую цепочку в mangle. Любопытных милости просим сходить по
треттей ссылке из генеральных.
Напоследок замечу, что это действительно работает :-)