- В данном случае мы публикуем очень старую заметку, которую написали несколько лет назад, но по ряду причин решили тогда не выкладывать.
- Несмотря на устаревание в частностях, некие общие сведения и приёмы в ней остались неизменными, поэтому надеемся, что даже сейчас она окажется полезной как минимум новичкам в сетевом Linux-администрировании.
- Мы не собираемся приводить её в актуальный вид, потому что по мере роста в нашем коллективе произошло неизбежное разделение таланта — технические писатели уже не разбираются в сетях достаточно глубоко, а сетевые инженеры больше не занимаются приведением накопленных наработок в популярную форму. Nuff said.
Очень кратко про дисциплины сетевых очередей:
- Queue disciplines (далее qdisc) — это часть ядра, через которую исходящие сетевые пакеты проходят непосредственно перед передачей в драйвер сетевой карты для отправки во внешний мир.
- Задача дициплин — переупорядочивать, задерживать или блокировать пакеты таким образом, чтобы никакие сетевые соединения не работали слишком медленно из-за того, что пропускная полоса потребляется другими соединениями (или слишком быстро, если речь идёт о шлюзе интернет-провайдера, отвечающем за ограничение скоростей до тарифных).
- Ядро Linux содержит несколько qdiscs и позволяет самостоятельно выбирать наиболее подходящую.
-
Ключевой характеристикой qdisc является классовость или бесклассовость:
- бесклассовая принимает все решения самостоятельно,
- классовая предназначена для вызова классов — т.е. других дисциплин, выбираемых ею по настраиваемым критериям.
- Корневая дисциплина (root qdisc) назначается на сетевой интерфейс.
- Иерархию из дисциплин называют деревом, а бесклассовые дисциплины в дереве называют листьями (leaf).
-
Также дисциплины разделяют на предназначенные для исходящего (egress) и входящего (ingress) трафика:
- здесь и далее речь идёт только об исходящем!
- ingress-дисциплин значительно меньше, и их возможности минимальны, т.к. они обрабатывают пакеты, уже поступившие в ядро, т.е. никак не могут повлиять на скорость и порядок их поступления в ядро.
Как управлять дисциплинами:
-
Назначаем дисциплину по умолчанию для новых интерфейсов (для существующих не меняется):
sysctl net.core.default_qdisc=...
-
Назначаем или меняем дисциплину для существующего интерфейса:
QDISC=sfq tc qdisc add dev eth0 root $QDISC tc qdisc replace dev eth0 root $QDISC
-
Полный список в вашем ядре (не считая pfifo):
ls -1 /lib/modules/`uname -r`/kernel/net/sched/sch_*.ko
-
Смотрим корневые дисциплины интерфейсов:
ip link list
-
Смотрим полные деревья дисциплин:
tc qdisc show
Наиболее распространённые дисциплины:
-
pfifo_fast:
- Долгое время являлась в ядре Linux дисциплиной по умолчанию.
- Ничего не делает, поэтому создаёт минимальную нагрузку на систему.
- Прекрасно работает, пока каналы быстрые, или соединений мало, исключительно по TCP, и все вопросы с задержками внутри них решает TCP congestion control (о котором речь ниже).
- Практически не работает, как только одно из этих условий нарушается (т.е. в современных реалиях — практически всегда).
-
sfq, fq и fq_codel:
- Бесклассовые дисциплины, которые поровну (fair = честный) делят пропускную способность между всеми соединениями.
- SFQ является среди них самой старой и известной, FQ_CoDel во многих современных дистрибутивах назначается по умолчанию.
-
mq:
- Данная дисциплина автоматически назначается сетевым картам с несколькими аппаратными очередями.
- mq является классовой, количество вызываемых из неё дисциплин (классов) равно количеству аппаратных очередей в сетевой карте.
- Если qdisc по умолчанию задана через sysctl net.core.default_qdisc, то при инициализации сетевого интерфейса Linux назначит её не интерфейсу в качестве корневой, а классам mq в качестве листьев.
-
Назначить mq сетевым картам, не имеющим поддержки аппаратных очередей, невозможно:
# ethtool -i enp6s0 | grep ^driver: driver: e1000e # tc qdisc add dev enp6s0 root mq RTNETLINK answers: Operation not supported
- Можно вручную заменить mq на любую другую, но делать этого не следует — многопоточная обработка превратится в однопоточную и деградация производительности в этом случае обеспечена.
TCP congestion control:
- Протокол TCP управляет на стороне отправителя (а) размером отправляемых пакетов и (б) паузами между отправкой пакетов.
- Делается это с целью повысить скорость доставки данных и при этом уменьшить нагрузку на сеть.
- Задача осложняется тем, что разные участки маршрута между отправителем и получателем не только имеют разную пропускную способность, но и меняют её непосредственно во время передачи.
- Для решения этой задачи разработано некоторое количество алгоритмов.
- Выбрать алгоритм в Linux'e можно командой "sysctl net.ipv4.tcp_congestion_control=название"
- Большинство алгоритмов собрано в виде модулей и нужный модуль должен быть предварительно загружен.
-
Список загруженных алгоритмов:
sysctl net.ipv4.tcp_available_congestion_control
— доступные для выбора черезsetsockopt()
из процессов суперпользователя,sysctl net.ipv4.tcp_allowed_congestion_control
— доступные для выбора черезsetsockopt()
из непривилегированных процессов (подмножество предыдущего списка),net.ipv4.tcp_congestion_control
— назначаемый новым соединениям по умолчанию (один из первого списка).
-
Полный список модулей с алгоритмами:
ls -l /lib/modules/`uname -r`/kernel/net/ipv4/tcp_*.ko
Что мы меняли?
- Дисциплины сетевых очередей: pfifo, sfq, fq, fq_codel.
- Алгоритмы управления TCP-соединениями: Reno, Cubic, Hybla, BBR.
- Скорость физического соединения у клиента-получателя: медленную (100 mbps) и быструю (40 gbps, как у сервера-отправителя).
Наша задача заключалась в том, чтобы найти оптимальное сочетание qdisc и tcp_congestion_control, обеспечивающее:
- максимальную скорость
- минимальную избыточность
Как мы оценивали избыточность?
- Создавали на сервере большой файл, добавляли в файрволл правило-счётчик "iptables -I OUTPUT -d ip-адрес-тестового-клиента" и запускали nginx.
- Скачивали файл на тестовую клиентскую машину.
- Сравнивали на сервере значение в счётчике файрволла с фактическим размером файла.
- Кроме тестовой копии Nginx, сервер продолжал обслуживать большое количество соединений в production-режиме. Это позволило оценить эффективность разных qdisc в реальной обстановке.
На быстрых каналах получилось так:
- Для всех алгоритмов доля ретрансмитов оказалась в пределах 1%.
- Самую высокую скорость показали BBR + FQ (в нашем случае 56 мегабайт/сек) и CUBIC + FQ (48 мегабайт/сек, т.е. на 1/7 медленнее).
- Замена дисциплины с FQ на pfifo вызвала падение скорости для всех алгоритмов: пару мегабайт для BBR и Reno, 20% для CUBIC и Hybla.
Полная таблица результатов для быстрого канала (без SFQ и FQ_Codel):
TCP congestion control | Queue discipline | Speed, megs/sec |
---|---|---|
bbr | pfifo | 54 |
bbr | fq | 56 |
reno | pfifo | 38 |
reno | fq | 35 |
cubic | pfifo | 38 |
cubic | fq | 48 |
hybla | pfifo | 19 |
hybla | fq | 25 |
На медленном канале (у сервера линк 40 гигабит, у клиента 100 мегабит):
- BBR показал 8-10% ретрансмитов с fq и 30-40% с pfifo, sfq и fq_codel.
- Cubic, Reno и Hybla показали менее 2% ретрансмитов со всеми qdisc.
- Скорость передачи данных во всех случаях составила 100 mbps, т.е. ограничивалась только пропускной способностью клиентского канала.
Выводы про BBR:
- BBR добивается немного более высокой скорости за счёт флуда (до 35%), в этом он отчасти схож со знаменитым uTP.
- Предполагаемая цель флуда — слать данные с запасом в расчёте на то, что на медленных участках пути вдруг вырастет пропускная способность.
- Большая доля ретрансмитов не приводит к уменьшению скорости. Наоборот, даже при 35% ретрансмитов скорость по сравнению со всеми другими алгоритмами оказывается выше хотя бы на доли процента.
Выводы про оптимальный вариант:
- Используйте BBR + FQ и получайте максимальную скорость, если у вас широкий канал или бесплатный трафик, при котором 10% дополнительной полосы не играют роли.
- В остальных случаях используйте CUBIC + FQ как оптимальный компромисс.