Linux QOS:Тестирование различных вариантов управленя траффиком: различия между версиями
Sirmax (обсуждение | вклад) |
Sirmax (обсуждение | вклад) |
||
Строка 388: | Строка 388: | ||
<BR>То есть, для шейпинга на скорсоти около 30 000 мегабит у нас интервал r2q лежит в районе 700-3000. Ограничения - нельзя делать делать меньше mtu и нельзя делать уж очень большим. |
<BR>То есть, для шейпинга на скорсоти около 30 000 мегабит у нас интервал r2q лежит в районе 700-3000. Ограничения - нельзя делать делать меньше mtu и нельзя делать уж очень большим. |
||
+ | <BR> |
||
+ | <span style="color:#FF0000"> Вот этот момент не очень понятен - при больших скоростях r2q надо брать достаточно большим что бы получался большой квантум - но как вычислили значения - не понятно |
||
+ | <BR> Пример с форума НАГа: |
||
+ | <PRE> |
||
+ | для примера на канал 900 мегабит |
||
+ | $TC->("qdisc add dev $dev root handle 1: htb r2q 37500"); |
||
+ | автоматом будет посчитан quantum = 3000 |
||
+ | ровно таким же макаром нужно считать для дочек |
||
− | |||
+ | </PRE> |
||
+ | Число ПОХОЖЕ на правду но я не смог понять как было получено именно такое - в каких единицах надо брать rate? |
||
+ | В примере для 900Мбит и квантум=3000 r2q=37500 но rate=quantum*r2q отсюда rate=37500*3000=112500000 что близко но все же не соответвует |
||
+ | </span> |
||
<PRE> |
<PRE> |
Версия 12:54, 19 июня 2015
Linux Shaper
Эта тема всегда вызывала у меня некоторые затруднения и потому я решил систематизировать свои знания.
Описание лаборатории
Для тестирования я использую лабу из 3 виртуальных машин, виртуалбокс. Базовая ОС - Мак Ос
|-------| |--------| |-------| |node-1 |-eth0-----eth1-|router0 |-eth2-----eth0-|node-2 | |-------| |--------| |-------| | eth0 | HOST
node-1
- 192.168.1.2
- Алиасами добавлены адреса 192.168.1.3-100
node-1
- 192.168.2.2
- Алиасами добавлены адреса 192.168.2.3-100
Router0
- eth1: 192.168.1.1
- eth2: 192.168.2.1
eth0 - служит для доступа к лабе и никак в тестах не участвует.
Все политики клсассы и прочее - только на транзитных интерфейсах eth1 и eth2
Базовое тестирование-ограничение скорости
Так как планируется большое число тестов и большое число данных, то первым делом нужно подготовить автоматизацию. Для генерации трафика я буду использовать iperf с переменным числом потоков, для сбора статистики - скрипт на питоне. Для визуализации - GnuPlot
Как тестируем
Тест проходит следующим образом:
- на node-1 запущены множество экземпляров iperf в режиме сервера
BASE_PORT=5000 for I in `seq 2 100` do let PORT=BASE_PORT+I echo ${PORT} iperf -s -l 32k -w 512k -u -p ${PORT} -D iperf -s -l 32k -w 512k -p ${PORT} -D done
- на node-2 запускается iperf в 30 потоков, каждый - на другой порт и на другой IP, для того что бы в дальнейших тестах проверять классы трафика и
для более равномерного распределения. sleep 30 - нужен для того что бы успеть запустить счетчики на ноде 1
В случае когда условия тестирования отличаются - я буду указывать отдельно
#!/bin/bash sleep 30 BASE_PORT=5000 for I in `seq 2 ${1}` do . IP=192.168.1.${I} let PORT=BASE_PORT+I #echo iperf -c ${IP} -b 100000M -i 1 -l 60K -t 120 -p ${PORT} -D iperf -c ${IP} -b 100000M -i 1 -l 60K -t 120 -p ${PORT} -D iperf -c ${IP} -b 100000M -i 1 -l 60K -t 120 -p ${PORT} -u -D done
- В процессе работы собираем траффик наколенном скриптом на питоне и складываем в лог, потом рисуем график используя GnuPLOT
import time import datetime as dt import sys stop_time=time.time()+300 iface='eth0' sleep_time=0.5 tx_data_file = open('/sys/class/net/'+iface+'/statistics/tx_bytes', 'r') rx_data_file = open('/sys/class/net/'+iface+'/statistics/rx_bytes', 'r') res_file = open(str(sys.argv[1]),'w') for l in rx_data_file: in_bytes_prev=int(l) for l in tx_data_file: out_bytes_prev=int(l) while True: try: rx_data_file.seek(0) tx_data_file.seek(0) for l in rx_data_file: in_bytes=int(l) for l in tx_data_file: out_bytes=int(l) in_bytes_diff=in_bytes-in_bytes_prev out_bytes_diff=out_bytes-out_bytes_prev in_bytes_prev=in_bytes out_bytes_prev=out_bytes time.sleep(sleep_time) res_file.write(str(time.time())+" "+str((in_bytes_diff)*8/sleep_time)+" "+str((out_bytes_diff)*8/sleep_time)+"\n") print str(time.time())+" "+str(in_bytes_prev)+" "+str(in_bytes)+" "+str((in_bytes_diff)*8)+" "+str(out_bytes_prev)+" "+str(out_bytes)+" "+str(out_bytes_diff)+" IN="+str((in_bytes_diff)*8/1024/1024/sleep_time)+" OUT="+str((out_bytes_diff)*8/1024/1024/sleep_time) if time.time()>stop_time: break except: break res_file.close()
Скрипт для GnuPlot - заготовка из кототорой на лету формируем awk то что надо - подставляя входной файл, имя графика и т.д.
#!/usr/bin/gnuplot -persist set terminal png size 1400,600 # Размер и формат графика. set output "/root/no_shaper.png" set title "no_shaper" # Заголовок set nokey # не знаю. Уточнить. set key top left # Расположение подписи set key box # Оформление (в рамке) подписи к графикам set xlabel "Date" # Метка по оси Х set xdata time # Описать что по оси Х время (формат ниже) set timefmt "%s" # Формат даты соответвует формату gnu date # В моем случае был удобен такой формат. set ylabel "Traffic" #set format y '%.0s%cB' set format y '%s' plot \ "/root/no_shaper.log" using 1:2 with lines title "IN, bit/s" smooth bezier, \ "/root/no_shaper.log" using 1:2 with lines title "IN, bit/s" , \ "/root/no_shaper.log" using 1:3 with lines title "OUT, bit/s" smooth bezier, \ "/root/no_shaper.log" using 1:3 with lines title "OUT, bit/s" [root@node-1 ~]# cat plot.sh #!/usr/bin/gnuplot -persist set terminal png size 1400,600 # Размер и формат графика. set output "___OUTPUT_FILE___" set title "___TITLE_OF_PLOT___" # Заголовок set nokey # не знаю. Уточнить. set key top left # Расположение подписи set key box # Оформление (в рамке) подписи к графикам set xlabel "Date" # Метка по оси Х set xdata time # Описать что по оси Х время (формат ниже) set timefmt "%s" # Формат даты соответвует формату gnu date # В моем случае был удобен такой формат. set ylabel "Traffic" #set format y '%.0s%cB' set format y '%s' plot \ "___INPUT_FILE___" using 1:2 with lines title "IN, bit/s" smooth bezier, \ "___INPUT_FILE___" using 1:2 with lines title "IN, bit/s" , \ "___INPUT_FILE___" using 1:3 with lines title "OUT, bit/s" smooth bezier, \ "___INPUT_FILE___" using 1:3 with lines title "OUT, bit/s"
Точка отсчета - без шейпера
Для того что бы получить некоторую точку отсчета (учитывая что лаба - это ВМки) делаю первый прогон без ограничения трафика.
Бесклассовые дисциплины
Я не буду рассматривать в этом разделе дисциплины без возможности ограничения трафика - по той причине что на неогруженных интерфейсах это не имеет практического смысла
RED
Отличная документация: "Подробнее с RED можно ознакомится в исходниках ядра"
TBF
Про TBF много пишут но я никогда ее не использовал. Соответственно - нужно тестировать.
Как и все бесклассовые дисциплины крепиться или к листу (leaf) или к интерфейсу.
Об этом напишу подробнее ниже.
Напомню что eth1 - интерфейс в сторону node-1
Удаляем текущую дисциплину
tc qdis del dev eth1 root
Убеждаемся что удалена (т е стоит по умолчанию pfifo_fast)
tc qdisc s dev eth1 qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 qdisc ingress ffff: parent ffff:fff1 ---------------- qdisc ingress ffff: parent ffff:fff1 ----------------
Второй раз удалить дисциплину по-умолчанию нельзя:
tc qdis del dev eth1 root RTNETLINK answers: No such file or directory
Добавляем дисциплину tbf:
tc qdisc add dev eth1 root tbf rate 180kbit latency 20ms buffer 1540
Проверяем:
tc qdisc s dev eth1 qdisc tbf 8002: root refcnt 2 rate 180000bit burst 1539b lat 20.0ms qdisc ingress ffff: parent ffff:fff1 ----------------
Описание параметров
После длительного гугления я так и не нашел внятного описания параметров. Потому попробую процитировать
- rate — ограничение скорости
- latency — максимальный "возраст"пакета в очереди
- burst — размер буфера. В байтах. (burst/buffer/maxburst - альтернативные названия что вносит некоторую путаницу)
Вот такое описание я нашел на Хабре, красным - мои вопросы
Дисциплина ТBF для своей работы использует механизм токенов. Токены генерируются системой с постоянной какой??? скоростью и помещаются в буфер(bucket). За каждый токен, вышедший из буфера с интерфейса уходит IP-пакет.А как насчет размера пакета? скорость передачи зависит от размера пакета
Если скорости передачи пакетов и генерации токенов совпадает, процесс передачи данных идет без задержки.
Если скорость передачи пакетов меньше чем скорость токенов, последние начинают накапливаться в буфере и затем могут использоваться для кратковременной передачи данных на скорости выше пороговой.
Если скорость передачи пакетов выше — токенов начинает не хватать. Пакеты данных ожидают новых токенов некоторое время Какое? думаю - пока не заполнится очередь заданая limit/latency, а затем начинают отбрасываться.
Прямым следствием из этого я вижу следующее - если задать бурст ОЧЕНЬ большим то можно ожидать всплеска траффика в начале теста.
Впрочем немного погуглив - найдено другое описание алгоритма
http://linux.die.net/man/8/tc-tbf
Результаты
Результаты (для меня) достаточно неожиданные
- Таки да - достаточно четко выставлена нужная скорость
- Явных потерь пингов я не наблюдаю
- Рост задержек явно не соответствует обещанным 20мс но и не запредельный
64 bytes from node-2 (192.168.2.2): icmp_seq=19 ttl=63 time=84.5 ms 64 bytes from node-2 (192.168.2.2): icmp_seq=20 ttl=63 time=84.6 ms 64 bytes from node-2 (192.168.2.2): icmp_seq=21 ttl=63 time=82.2 ms 64 bytes from node-2 (192.168.2.2): icmp_seq=28 ttl=63 time=86.0 ms 64 bytes from node-2 (192.168.2.2): icmp_seq=30 ttl=63 time=84.3 ms 64 bytes from node-2 (192.168.2.2): icmp_seq=37 ttl=63 time=84.1 ms
Epic Fail
tc qdisc replace dev eth1 root tbf rate 40Mbit burst 159900000000000000 latency 10s
Kernel panic
Судя по моим подсчетам - burst 159900000000000000 (который пересчитывается в байты при создании правила) израсходовал 1100Мб при том что на ВМке всего1 гиг - предположительная причина - перерасход памяти
Проверка предположения про burst
Как я писал выше, предположение что установка огромного burst вызовет всплеск трафика в начале теста требует подтверждения: настраиваю следующим образор
tc qdisc replace dev eth1 root tbf rate 40Mbit burst 159900000 latency 10s
Как можно видеть по графику - предположение полностью подтверждается, наблюдаем явный пик трафика и потом переход к "нормальной скорости"
Проверка влияния burst и latency на поведение алгоритма
Попробуем собрать более-менее полную статистику по burst и latency. Для этого я в цикле прогоняю тесты при этом в каждом следующем тесте я делаю burst=burst*N что бы обеспечить более-менее приемлемую скорость перебора. Аналогично - для latency. Все параметры теста вынесены в заголовок графика.
Выводы
Судя по тому что я вижу - TBF неплохо подходит для простых случаев; хорошо масштабируемый до скоростей порядка 100Мбит.
Задирание буферов до запредельных значений смысла не имеет как и увеличение latency.
Насколько я могу судить алгоритм достаточно точный - снимаемые с ноды значения соответвуют выставляемым на роутере.
Классовые дисциплины
Для простых случаев можно использовать и классовые дисциплины - при этом конструкции будут достаточно простые и понятные.
HTB
По-поводу HTB написано очень много - но вопросы с пониманием возникают постоянно. Потому я попробую собрать наиболее подробное и понятное описание из разных источников - такая компиляция.
Вот неплохое описание:
The Hierarchical Token Bucket (HTB) is an improved version of TBF that introduces the notion of classes. Each class is, in fact, a TBF-like qdisc, and classes are linked together as a tree, with a root and leaves. HTB introduces a number of features to improved the management of bandwidth, such as a the priority between classes, a way to borrow bandwidth from another class, or the possibility to plug another qdisc as an exit point (a SFQ for example).
Т.е. для начала считаем что HTB - это расширенная TBF с возможностью займа полосы и подключения к выходу других дисциплин.
Пробная настройка
У HTB много малопонятных параметров - я постараюсь описать их по возможности подробно.
Первым делом создаем корневую дисциплину - у нее по сути есть всего 2 параметра
# tc qdics add dev eth1 root handle 1: htb default 20
- default 20 — задаем класс по-умолчанию. В нем будут обрабатываться пакеты, не попавшие в другие классы дисциплины htb.
Если его не указать, то будет назначен “default 0” и весь не классифицированный (не попавший под фильтры) трафик будет отправляться со скоростью интерфейса. В моем случае когда надо ограничить весь трафик (базовый случай - аналог примера выше) это будет единственный класс и никаких фильтров применяться не будет. (примеры с фильтрами, приоритетами и деревьями классов - ниже, в другом разделе этого документа)
- r2q DRR quantums are computed as rate in Bps/r2q {10}
Есть несколько описаний но насколько я смог понять - это кванты трафика с которым работает HTB, очевидно что делать мелкие кванты при нынешних скоростях смысла нет. Приведу несколько описаний (автор - не я)
- Второй параметр R2Q используется для квантования трафика в классах. В случае малых скоростей рекомендуется значение = 1, что позволяет шейпать с точностью до 4kbit. Если не указан, то =10, что для скоростей >120Kbit.
- Сейчас стоит рассмотреть понятие квантов трафика. В процессе конкурентной передачи данных нескольких классов обслуживание каждого из них начинается после того, как обработано некоторое количество байт предыдущего класса. Это количество байт называется квантом (quantum). Когда несколько классов претендуют на канал родительского, они получают части канала пропорциональные их квантам. Важно знать, что расчёт распределения канала тем точнее, чем меньше размер кванта (но квант всё же должен быть не меньше величины MTU). Как правило нет необходимости задавать кванты вручную, поскольку HTB рассчитывает их самостоятельно. Размер кванта класса устанавливается (при создании или изменении класса) равным запрошенной скорости делённой на глобальный параметр r2q. По умолчанию r2q равен 10, что приемлемо для скоростей выше 15 kbps (120 kbit), поскольку обычно MTU равен 1500. Для меньших скоростей величина r2q равна 1; это подходит для скоростей от 12 kbit.При желании можно установить величину кванта вручную. В этом случае параметр r2q игнорируется. Будут выведены предупреждения о том, что установленная величина не верна, но ими можно пренебречь.
http://forum.nag.ru/forum/index.php?showtopic=48277&st=0&p=609502&#entry609502
- Еще одно хорошее описание:
Уверен, многие сталкивались с проблемами:
HTB: quantum of class 10001 is big. Consider r2q change или HTB: quantum of class 10001 is small. Consider r2q change.
Что делать вообще и как жить? На удивление информации нереально мало.
Но на форму nag.ru нашелся чудесный челвоек technolab, который дал следующие разъяснения:
MTU - максимальный размера блока в байтах который может быть передан.
Quantum - число байт, которые может передать поток перед переходом к следующему классу, по умолчанию = MTU интерфейса. Расчет распределения канала тем точнее, чем меньше это значение.
R2Q - глобальный параметр, по умолчанию R2Q = 10, если Quantum указан, то R2Q игнорируется.
Немного констант:
quantum = rate / r2q
mtu ≤ quantum ≤ 60000
Соответственно:
quantum is small => RATE / R2Q < MTU
quantum is big => RATE / R2Q > 60000
То есть, для шейпинга на скорсоти около 30 000 мегабит у нас интервал r2q лежит в районе 700-3000. Ограничения - нельзя делать делать меньше mtu и нельзя делать уж очень большим.
Вот этот момент не очень понятен - при больших скоростях r2q надо брать достаточно большим что бы получался большой квантум - но как вычислили значения - не понятно
Пример с форума НАГа:
для примера на канал 900 мегабит $TC->("qdisc add dev $dev root handle 1: htb r2q 37500"); автоматом будет посчитан quantum = 3000 ровно таким же макаром нужно считать для дочек
Число ПОХОЖЕ на правду но я не смог понять как было получено именно такое - в каких единицах надо брать rate? В примере для 900Мбит и квантум=3000 r2q=37500 но rate=quantum*r2q отсюда rate=37500*3000=112500000 что близко но все же не соответвует
# tc class add dev eth1 parent 1: classid 1:20 htb rate 90kbps ceil 90kbps
прикрепляем к root qdisc класс с идентификатором 1:1. Тем самым ограничиваем скорость на интерфейсе до 90Кбайт/c.
classid 1:1 — идентификатор класса. <PRE> * rate 90kbps — устанавливаем нижний порог пропускной способности для класса. * ceil 90kbps — устанавливаем верхний порог пропускной способности для класса. <PRE> # tc class add dev eth0 parent 1:1 classid 1:10 htb rate 20kbps ceil 70kbps
создаем класс 1:10, дочерний классу 1:1. Затем в него фильтром будет направляться исходящий почтовый трафик.
rate 20kbps — устанавливаем гарантированный нижний порог пропускной способности для класса.
ceil 70kbps — устанавливаем верхний порог пропускной способности для класса. В случае если у родительского класса будет свободна полоса пропускания (наличие “лишних” токенов), class 1:10 сможет временно поднять скорость передачи данных, вплоть до указанного предела в 70Кбайт/c.
# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 70kbps ceil 90kbps
Создаем класс по умолчанию. В него будет попадать весь остальной трафик. Точно также, параметрами rate и ceil, задаем расширение пропускной способности в случае отсутствия уже почтового трафика.
- tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 25 0xffff flowid 1:10
фильтр на базе u32, направляющий пакеты исходящие на 25й порт в класс 1:10.
Кстати, в документации указано, что по факту в HTB шейпинг трафика происходит только в краевых классах, в нашем случае 1:10 и 1:20. Указание параметров ограничения полосы пропускания в остальных классах HTB нужно лишь для функционирования системы заимствования между классами.
При добавлении класса также возможно указать параметр prio. Он задает приоритет класса (0 — макс.приоритет). Классы с меньшим приоритетом не обрабатываются пока есть данные в более приоритетных классах.
HFSC
- http://ace-host.stuart.id.au/russell/files/tc/doc/sch_hfsc.txt
- http://linux-ip.net/articles/hfsc.en/
Источники
- http://www.opennet.ru/base/net/linux_traffic_qos.txt.html
- http://www.opennet.ru/base/net/htb_manual.txt.html
- http://wiki.linuxwall.info/doku.php/en:ressources:dossiers:networking:traffic_control#tbf_-_token_bucket_filter
- http://xgu.ru/wiki/QoS_%D0%B2_Linux