Тюнинг сетевого стека Linux для ленивых

  • Tutorial

Сетевой стек Linux по умолчанию замечательно работает на десктопах. На серверах с нагрузкой чуть выше средней уже приходится разбираться как всё нужно правильно настраивать. На моей текущей работе этим приходится заниматься едва ли не в промышленных масштабах, так что без автоматизации никуда – объяснять каждому коллеге что и как устроено долго, а заставлять людей читать ≈300 страниц английского текста, перемешанного с кодом на C… Можно и нужно, но результаты будут не через час и не через день. Поэтому я попробовал накидать набор утилит для тюнинга сетевого стека и руководство по их использованию, не уходящее в специфические детали определённых задач, которое при этом остаётся достаточно компактным для того, чтобы его можно было прочитать меньше чем за час и вынести из него хоть какую-то пользу.


Чего нужно добиться?


Главная задача при тюнинге сетевого стека (не важно, какую роль выполняет сервер — роутер, анализатор трафика, веб-сервер, принимающий большие объёмы трафика) – равномерно распределить нагрузку по обработке пакетов между ядрами процессора. Желательно с учётом принадлежности CPU и сетевой карты к одной NUMA-ноде, а также не создавая при этом лишних перекидываний пакета между ядрами.


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


Рекомендации по подбору железа


  • Двухпроцессорный сервер не будет полезен, если трафик приходит только на одну сетевую карту.
  • Отдельные NUMA-ноды не будут полезны, если трафик приходит в порты одной сетевой карты.
  • Нет смысла покупать сервер с числом ядер большим, чем суммарное число очередей сетевых карт.
  • В случае с сетевыми картами с одной очередью распределить нагрузку между ядрами можно с помощью RPS, но потери при копировании пакетов в память это не устранит.
  • Hyper-Threading не приносит пользы и выключается в BIOS (тем более в Skylake и Kaby Lake с ним проблемы)
  • Выбирайте процессор с частотой ядер не менее 2.5GHz и большим объёмом L3 и остальных кэшей.
  • Используйте DDR4-память.
  • Выбирайте сетевые карты, поддерживающие размер RX-буферов до 2048 и более.

Таким образом, если дано 2+ источника объёма трафика больше 2 Гбит/сек, то стоит задуматься о сервере с числом процессоров и NUMA-нод, а также числу сетевых карт (не портов), равных числу этих источников.


"Господи, я не хочу в этом разбираться!"


И не нужно. Я уже разобрался и, чтобы не тратить время на то, чтобы объяснять это коллегам, написал набор утилит — netutils-linux. Написаны на Python, проверены на версиях 2.6, 2.7, 3.4, 3.6.


network-top


network-top


Эта утилита нужна для оценки применённых настроек и отображает равномерность распределения нагрузки (прерывания, softirqs, число пакетов в секунду на ядро процессора) на ресурсы сервера, всевозможные ошибки обработки пакетов. Значения, превышающие пороговые подсвечиваются.


rss-ladder


# rss-ladder eth1 0
- distributing interrupts of eth1 (-TxRx) on socket 0:"
  - eth1: irq 67 eth1-TxRx-0 -> 0
  - eth1: irq 68 eth1-TxRx-1 -> 1
  - eth1: irq 69 eth1-TxRx-2 -> 2
  - eth1: irq 70 eth1-TxRx-3 -> 3
  - eth1: irq 71 eth1-TxRx-4 -> 8
  - eth1: irq 72 eth1-TxRx-5 -> 9
  - eth1: irq 73 eth1-TxRx-6 -> 10
  - eth1: irq 74 eth1-TxRx-7 -> 11

Эта утилита распределяет прерывания сетевой карты на ядра выбранного физического процессора (по умолчанию на нулевой).


autorps


# autorps eth0
Using mask 'fc0' for eth0-rx-0.

Эта утилита позволяет настроить распределение обработки пакетов между ядрами выбранного физического процессора (по умолчанию на нулевой). Если вы используете RSS, скорее всего вам эта утилита не потребуется. Типичный сценарий использования — многоядерный процессор и сетевые карты с одной очередью.


server-info


# server-info rate
cpu:
  BogoMIPS: 7
  CPU MHz: 7
  CPU(s): 1
  Core(s) per socket: 1
  L3 cache: 1
  Socket(s): 10
  Thread(s) per core: 10
  Vendor ID: 10
 disk:
   vda:
     size: 1
     type: 1
 memory:
   MemTotal: 1
   SwapTotal: 10
 net:
   eth1:
     buffers:
       cur: 5
       max: 10
     driver: 1
     queues: 1
 system:
   Hypervisor vendor: 1
   Virtualization type: 1

Данная утилита позволяет сделать две вещи:


  1. server-info show: посмотреть, что за железо вообще установлено на сервере. В целом похоже на велосипед, повторяющий lshw, но с акцентом на интересующие нас параметры.
  2. server-info rate: найти узкие места в аппаратном обеспечении сервера. В целом похоже на индекс производительности Windows, но опять же с акцентом на интересующие нас параметры. Оценка производится по шкале от 1 до 10.

Прочие утилиты


  • rx-buffers-increase автоматически увеличивает буфер выбранной сетевой карты до оптимального значения.
  • maximize-cpu-freq отключает плавающую частоту процессора. Энергопотребление будет повышенным, но это не ноутбук без зарядного устройства, а сервер, который обрабатывает гигабиты трафика.

Господи, я хочу в этом разбираться!


Прочитайте статьи про:



Эти статьи вдохновили меня на написание этих утилит .


Также хорошую статью написали в блоге одноклассников 2 года назад.


Обычные кейсы


Но руководство по запуску утилит само по себе мало что говорит о том, как именно их нужно применять в зависимости от ситуации. Приведём несколько примеров.


Пример 1. Максимально простой.


Дано:


  • один процессор с 4 ядрами
  • одна 1 Гбит/сек сетевая карта (eth0) с 4 combined очередями
  • входящий объём трафика 600 Мбит/сек, исходящего нет.
  • все очереди висят на CPU0, суммарно на нём ≈55000 прерываний и 350000 пакетов в секунду, из них около 200 пакетов/сек теряются сетевой картой. Остальные 3 ядра простаивают

Решение:


  • распределяем очереди между ядрами командой rss-ladder eth0
  • увеличиваем ей буфер командой rx-buffers-increase eth0

Пример 2. Чуть сложнее.


Дано:


  • два процессора с 8 ядрами
  • две NUMA-ноды
  • Две двухпортовые 10 Гбит/сек сетевые карты (eth0, eth1, eth2, eth3), у каждого порта 16 очередей, все привязаны к node0, входящий объём трафика: 3 Гбит/сек на каждую
  • 1 х 1 Гбит/сек сетевая карта, 4 очереди, привязана к node0, исходящий объём трафика: 100 Мбит/сек.

Решение:


1 Переткнуть одну из 10 Гбит/сек сетевых карт в другой PCI-слот, привязанный к NUMA node1.


2 Уменьшить число combined очередей для 10гбитных портов до числа ядер одного физического процессора:


for dev in eth0 eth1 eth2 eth3; do
  ethtool -L $dev combined 8
done

3 Распределить прерывания портов eth0, eth1 на ядра процессора, попадающие в NUMA node0, а портов eth2, eth3 на ядра процессора, попадающие в NUMA node1:


rss-ladder eth0 0
rss-ladder eth1 0
rss-ladder eth2 1
rss-ladder eth3 1

4 Увеличить eth0, eth1, eth2, eth3 RX-буферы:


for dev in eth0 eth1 eth2 eth3; do
  rx-buffers-increase $dev
done

Необычные кейсы


Не всегда всё идёт идеально:


  • Встречались сетевые карты, теряющие пакеты (missed) в случае использования RSS на несколько ядер в одной NUMA-ноде. Решение странное, но рабочее — 6 RX-очередей привязаны к CPU0, в rps_cpus каждой очереди записана маска процессоров 111110, потери пропали.
  • Встречались сетевые карты mellanox и intel (X710) продолжающие работать при прекратившемся росте счётчиков прерываний. Трафик в tcpdump имелся, нагрузка, создаваемая сетевыми картами висела на CPU0. Нормальная работа восстановилась после включения и выключения RPS. Почему — неизвестно.
  • Некоторые SFP-модули для Intel 82599ES при обновлении драйвера (сборка ixgbe из исходников с sourceforge) "пропадают" из списка сетевых карт. При этом в lspci этот порт отображается, второй аналогичный порт работает, а в dmesg на оба порта одинаковые warning'и. Помогает флаг unsupported_sfp=1,1 при загрузке модуля ixgbe. По хорошему, однако, стоит купить supported sfp.
  • Некоторые драйверы сетевых карт подстраивают число очередей только под равные степени двойки значения (что обидно на 6-ядерных процессорах).



Update: после публикации автор осознал, что люди используют не только RHEL-based дистрибутивы для сетевых задач, а тесты в debian на наборах данных, собранных в RHEL-based системах, не отлавливают кучу багов. Большое спасибо всем сообщившим о том, что что-то не работает в Ubuntu/Debian/Altlinux! Все баги исправлены в релизе 2.0.10


Update2. в комментариях упомянули то, что RPS всё же часто бывает полезен людям и я его недооцениваю. В принципе это так, поэтому в релизе 2.2.0 появилась значительно улучшенная версия утилиты autorps.


Update3: Релиз 2.5.0

Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 45
  • +3
    Пользуясь внезапно подвернувшимся случаем, хочу ещё раз поблагодарить вас за ваши продукты :)
    • 0

      Как расшифровывается RPS в данном конткесте, если не секрет?

      • +1

        RPS: Receive Packet Steering, программный аналог аппаратного Receive-Side Scaling (RSS). Обе технологии позволяют распределять входящие пакеты по очередям в зависимости от хеша нескольких байтов заголовка (фильтра).


        https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/network-rps.html
        https://www.kernel.org/doc/Documentation/networking/scaling.txt


        RSS: Receive Side Scaling

        Contemporary NICs support multiple receive and transmit descriptor queues (multi-queue). On reception, a NIC can send different packets to different queues to distribute processing among CPUs. The NIC distributes packets by
        applying a filter to each packet that assigns it to one of a small number of logical flows. Packets for each flow are steered to a separate receive queue, which in turn can be processed by separate CPUs. This mechanism is
        generally known as “Receive-side Scaling” (RSS). The goal of RSS and the other scaling techniques is to increase performance uniformly.


        Receive Packet Steering (RPS) is logically a software implementation of RSS. Being in software, it is necessarily called later in the datapath. Whereas RSS selects the queue and hence CPU that will run the hardware
        interrupt handler, RPS selects the CPU to perform protocol processing above the interrupt handler. This is accomplished by placing the packet on the desired CPU’s backlog queue and waking up the CPU for processing.
        RPS has some advantages over RSS:
        1) it can be used with any NIC,
        2) software filters can easily be added to hash over new protocols,
        3) it does not increase hardware device interrupt rate (although it does introduce inter-processor interrupts (IPIs)).

        RPS is called during bottom half of the receive interrupt handler, when a driver sends a packet up the network stack with netif_rx() or netif_receive_skb(). These call the get_rps_cpu() function, which selects the queue that should process a packet.
      • 0
        Вах! Отлично! Спасибо!
        • +1
          При запуске выдает следующее:
          /usr/local/bin/network-top
          Traceback (most recent call last):
            File "/usr/local/bin/network-top", line 3, in <module>
              from netutils_linux_monitoring import NetworkTop
            File "/usr/local/lib/python2.7/dist-packages/netutils_linux_monitoring/__init__.py", line 1, in <module>
              from netutils_linux_monitoring.irqtop import IrqTop
            File "/usr/local/lib/python2.7/dist-packages/netutils_linux_monitoring/irqtop.py", line 7, in <module>
              from netutils_linux_monitoring.base_top import BaseTop
            File "/usr/local/lib/python2.7/dist-packages/netutils_linux_monitoring/base_top.py", line 7, in <module>
              from netutils_linux_monitoring.colors import wrap
            File "/usr/local/lib/python2.7/dist-packages/netutils_linux_monitoring/colors.py", line 7, in <module>
              2: Fore.LIGHTYELLOW_EX,
          AttributeError: 'AnsiCodes' object has no attribute 'LIGHTYELLOW_EX'
          


          • 0

            Подскажите пожалуйста:


            1. какой дистрибутив linux вы используете.
            2. что выдаёт set | egrep '^(TERM|LC|LANG).*='
            • 0
              Мне помогло: pip install --upgrade colorama
            • 0
              Ubuntu 14.04.4 LTS
              LANG=en_US.UTF-8
              LANGUAGE=en_US:en
              TERM=xterm
              • 0

                Спасибо, создал issue на эту тему, попробую завтра вечером починить.


                https://github.com/strizhechenko/netutils-linux/issues/95

                • 0

                  Развернул на digitalocean виртуалку:


                  Ubuntu 14.04.5
                  LANG=en_US.UTF-8
                  LANGUAGE=en_US:en
                  TERM=xterm

                  Работает прекрасно. На всякий случай посмотрел исходники colorama, про все эти LIGHT..._EX:


                  These are fairly well supported, but not part of the standard.

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


                  Обновиться можно с помощью pip install --upgrade netutils-linux, билд в котором это поправлено: 2.0.4

              • 0
                выглядит интересно. А измения перманетны или после перезагрузки нужно повторять процедуру?
                • +1

                  Нет, эти изменения действуют до перезагрузки.
                  Как вариант запихать их в /etc/rc.local или куда сейчас там модно :)

                • +1

                  weirded
                  Ubuntu 12.04
                  TERM=screen-bce
                  LANG=en_US.UTF-8
                  LANGUAGE=en_US:en


                  # rss-ladder eth1 0
                  - distribute interrupts of eth1 (-rx-) on socket 0
                  lscpu: unrecognized option '--extended=CPU,SOCKET'
                  • 0

                    Ubuntu 16.04.2
                    TERM=xterm (xfce-terminal)
                    LANG=ru_RU.UTF-8
                    LANGUAGE=ru_RU:ru:en


                    # rss-ladder eth1 0
                    - distribute interrupts of eth1 (-rx-) on socket 0
                      - eth1: irq 128 1572865-edge -> eth1-rx-0
                    /usr/local/bin/rss-ladder: строка 80: echo: ошибка записи: Недопустимый аргумент
                      • 0

                        С 12.04 все в порядке в 2.0.5?

                        • 0

                          Не совсем, оно как-будто 2-х ядер не видит. Всё на первое вешает.
                          Написал в issue.

                          • 0

                            А, понял. Там логика для раздельных RX и TX очередей следующая:


                            1. Размазываем лесенкой все RX очереди по ядрам.
                            2. Размазываем лесенкой все TX очереди по ядрам.

                            Собственно это он и делает, а поскольку RX очередь одна и TX очередь одна — обе очереди падают на 0 ядро.


                            В феврале ещё думал как лучше поступать в этом кейсе:


                            https://github.com/strizhechenko/netutils-linux/issues/8


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

                  • +1

                    Заголовки в статье восхитительны.

                    • +1
                      В debian и производных нет /etc/sysconfig/network-scripts/
                      Поэтому
                      rx-buffers-increase eth0
                      Traceback (most recent call last):
                      File "/usr/local/bin/rx-buffers-increase", line 24, in main()
                      File "/usr/local/bin/rx-buffers-increase", line 20, in main
                      RxBuffersIncreaser(dev, upper_bound).apply()
                      File "/usr/local/lib/python2.7/dist-packages/netutils_linux_tuning/rx_buffers.py", line 64, in apply
                      self.investigate()
                      File "/usr/local/lib/python2.7/dist-packages/netutils_linux_tuning/rx_buffers.py", line 33, in investigate
                      with open(path.join(ns, 'ifcfg-' + self.dev)) as config:
                      IOError: [Errno 2] No such file or directory: '/etc/sysconfig/network-scripts/ifcfg-eth0'

                    • 0
                      Актуально ли это для виртуальных серверов, куда проброшен зеркальный трафик?
                      • 0

                        Отчасти да, но это нужно делать:


                        1. На виртуалке.
                        2. На хосте.
                        3. Ковырять дополнительно прямой проброс железа (ядер и сетёвок) в виртуалку.

                        и всё равно хорошей производительности в итоге не добиться.


                        Но если идеальная работа не преследуется, то вполне сойдёт :)

                      • 0
                        gentoo:
                        network-top
                        Traceback (most recent call last):
                        File "/usr/bin/network-top", line 7, in NetworkTop().run()
                        File "/usr/lib64/python2.7/site-packages/netutils_linux_monitoring/network_top.py", line 24, in __init__
                        self.parse_options()
                        File "/usr/lib64/python2.7/site-packages/netutils_linux_monitoring/network_top.py", line 117, in parse_options
                        top.post_optparse()
                        File "/usr/lib64/python2.7/site-packages/netutils_linux_monitoring/link_rate.py", line 152, in post_optparse
                        self.numa = Numa(self.options.devices, self.options.random)
                        File "/usr/lib64/python2.7/site-packages/netutils_linux_monitoring/numa.py", line 34, in __init__
                        self.detect_layouts()
                        File "/usr/lib64/python2.7/site-packages/netutils_linux_monitoring/numa.py", line 77, in detect_layouts
                        layouts = [list(map(int, row.split(b',')[2:4])) for row in rows]
                        ValueError: invalid literal for int() with base 10: ''
                        • 0

                          Если в 2.0.8 повторяется, можете скинуть вывод lscpu -p?

                          • 0
                            В 2.0.10 было так же.
                            Сейчас накатил 2.1.1. Работает :)
                            • 0

                              32 битная система, полагаю?

                              • 0
                                Нет, экспериментировал на такой машине:
                                undro netutils-linux # uname -a
                                Linux undro 4.4.21-gentoo #1 SMP Wed Feb 15 05:32:25 VLAT 2017 x86_64 Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz GenuineIntel GNU/Linux
                                undro netutils-linux # lscpu -p
                                # The following is the parsable format, which can be fed to other
                                # programs. Each different item in every column has an unique ID
                                # starting from zero.
                                # CPU,Core,Socket,Node,,L1d,L1i,L2,L3
                                0,0,0,,,0,0,0,0
                                1,1,0,,,1,1,1,0
                                2,2,0,,,2,2,2,0
                                3,3,0,,,3,3,3,0
                                • +1
                                  Поглядел на одном из софтроутеров, network-top моргает портянкой в 500 вланов.
                                  А irq-top приятный на вид — https://ibb.co/bvXcsv
                                  • 0

                                    --device-regex вам в помощь, чтобы от vlan'ов с ума не сходить :)

                                    • 0
                                      Да, спасибо, увидел :)
                                    • 0

                                      Кстати, что за магия такая, что у вас все сетёвки генерят абсолютно одинаковое число прерываний?

                                      • +1
                                        Кол-во генерируемых прерываний фиксируется параметром InterruptThrottleRate для модуля ixgbe.
                                        • 0

                                          А, помню такое. 4к прерываний на ядро вроде как слишком уж жёсткий лимит выходит в итоге.

                          • +2
                            О спасибо тебе добрый человече!
                            • +2
                              Отличные тулзы, спасибо!
                              Нет смысла покупать сервер с числом ядер большим, чем суммарное число очередей сетевых карт.

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

                              Ещё, пользователи тулзов наверняка оценили бы возможность настраивать ещё и RFS и XPS.
                              • 0

                                Не, вы правы конечно, если прибить запуск юзерспэйс приложений на ядра не занимающиеся обработкой пакетов — профит будет, но в случае если это тупая молотилка трафика (в основном такими занимаюсь, небольшая профессиональная деформация) — то вряд ли.


                                Про RFS — тут опять же надо вклиниваться в процесс запуска приложения, которое должно определенный сорт трафика потреблять, а не только выполнить правильную команду для ethtool. Что-то универсальное для этого запилить сложновато. Хотя, если есть идеи — welcome to the github issues, я бы с удовольствием сделал.

                                • +1
                                  Для RFS достаточно включить RPS и задать ненулевое значение в файлах /sys/class/net/eth*/queues/rx-*/rps_flow_cnt. Подробнее в https://www.kernel.org/doc/Documentation/networking/scaling.txt.
                                  Это улучшайзер RPS, который запоминает на каком ядре находится процесс, обрабатывающий соединение.
                                  • 0
                                    Согласен, тем более RFS умеет избегать «out of order packets» при миграции потоков приложения по ядрам, т.е. даже если потоки не привязаны к ядрам — это не критично: https://www.kernel.org/doc/Documentation/networking/scaling.txt
                                    When the scheduler moves a thread to a new CPU while it has outstanding
                                    receive packets on the old CPU, packets may arrive out of order. To
                                    avoid this, RFS uses a second flow table
                                    to track outstanding packets
                                    for each flow

                                    (привязка приложения к нодам во время запуска приложения (numactl, taskset) или потоков к ядрам на уровне приложения (sched_setaffinity, pthread_setaffinity_np) — очень полезная, но отдельная задача)

                                    При RPS один и тот же пакет обрабатывается в kernel-space TCP/IP-stack на одном CPU-Core, и затем в user-space вашего приложения в потоке работающем на другом CPU-Core, это добавляет лишний обмен между ядрами процессора.
                                    При RFS все пакеты одного соединения обрабатываются в kernel-space и user-space на одном ядре.
                                    • 0

                                      Пардон, попутал с Receive Flow Hash Indirectiction.
                                      Да, RFS уместен, в случае если кто-то в userspace пакетики получает, но в случае форварда это, как мне кажется только лишний оверхед (прыжок пакета в Flow Table).

                                • 0

                                  Про XPS: добавил тулзу autoxps в версии 2.2.5

                                • 0
                                  Хм. У меня network-top показывает 2 строки:

                                  # /proc/interrupts

                                  CPU0 CPU1 CPU2 CPU3

                                  31 26 196 94 interrupts
                                  81 67 60 43 interrupts

                                  Столбцы — ясно. Но что означают строки — непонятно.
                                  • 0

                                    По дефолту он скрывает строки, в которых все прерывания подрастают меньше, чем на 80 в секунду. Его цель показать то, что несет большую часть нагрузки на ядра процессора/выявлять шторм прерываний. Можете использовать: network-top -l 0

                                  • 0
                                    Хорошая статья. Готовый код всегда лучше общих рассуждений.
                                    Встречались сетевые карты mellanox ...

                                    Используете библиотеку VMA для обмена по LAN если на обоих серверах установлены Mellanox Ethernet-карты, или в ваших кейсах почти весь трафик внешний?
                                    http://www.mellanox.com/page/software_vma?mtag=vma
                                    The result is a reduction in latency by as much as 300% and an increase in application throughput by as much as 200% per server as compared to applications running on standard Ethernet or InfiniBand interconnect networks.
                                    • 0

                                      Спасибо!
                                      Увы, не использовал, трафик обычно — зеркало со свитчей.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.