Сетевой стек 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

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


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

Поделиться с друзьями
-->

Комментарии (41)


  1. WST
    27.06.2017 17:55
    +3

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


  1. lexore
    27.06.2017 19:32

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


    1. a5b
      27.06.2017 19:43
      +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.


  1. LevOrdabesov
    27.06.2017 21:20

    Вах! Отлично! Спасибо!


  1. JovanottiRU
    27.06.2017 21:20
    +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'
    



    1. weirded
      27.06.2017 21:24

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


      1. какой дистрибутив linux вы используете.
      2. что выдаёт set | egrep '^(TERM|LC|LANG).*='


    1. Devd
      28.06.2017 07:22

      Мне помогло: pip install --upgrade colorama


  1. JovanottiRU
    27.06.2017 21:34

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


    1. weirded
      27.06.2017 21:37

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


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


      1. weirded
        27.06.2017 22:26

        Развернул на 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


  1. dokus
    27.06.2017 23:52

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


    1. weirded
      27.06.2017 23:54
      +1

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


  1. SergeyD
    28.06.2017 00:25
    +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'


    1. SergeyD
      28.06.2017 00:28

      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: ошибка записи: Недопустимый аргумент


      1. SergeyD
        28.06.2017 00:32

        1. weirded
          28.06.2017 09:09

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


          1. SergeyD
            28.06.2017 11:28

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


            1. weirded
              28.06.2017 12:03

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


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

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


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


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


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


  1. nick_volynkin
    28.06.2017 06:47
    +1

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


  1. 9660
    28.06.2017 09:13
    +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'



  1. Templier
    28.06.2017 10:53

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


    1. weirded
      28.06.2017 11:10

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


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

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


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


  1. undro
    28.06.2017 11:08

    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: ''


    1. weirded
      28.06.2017 20:33

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


      1. undro
        05.07.2017 09:05

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


        1. weirded
          05.07.2017 09:11

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


          1. undro
            05.07.2017 09:42

            Нет, экспериментировал на такой машине:
            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. undro
              05.07.2017 10:20
              +1

              Поглядел на одном из софтроутеров, network-top моргает портянкой в 500 вланов.
              А irq-top приятный на вид — https://ibb.co/bvXcsv


              1. weirded
                06.07.2017 22:39

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


              1. weirded
                06.07.2017 22:40

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


  1. SADKO
    28.06.2017 13:26
    +2

    О спасибо тебе добрый человече!


  1. dmitrysamsonov
    04.07.2017 10:50
    +2

    Отличные тулзы, спасибо!

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

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

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


    1. weirded
      05.07.2017 06:15

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


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


      1. dmitrysamsonov
        05.07.2017 10:02
        +1

        Для RFS достаточно включить RPS и задать ненулевое значение в файлах /sys/class/net/eth*/queues/rx-*/rps_flow_cnt. Подробнее в https://www.kernel.org/doc/Documentation/networking/scaling.txt.
        Это улучшайзер RPS, который запоминает на каком ядре находится процесс, обрабатывающий соединение.


        1. AlexeyAB
          05.07.2017 16:15

          Согласен, тем более 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 на одном ядре.


          1. weirded
            06.07.2017 23:00

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


  1. chabapok
    04.07.2017 14:09

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

    # /proc/interrupts

    CPU0 CPU1 CPU2 CPU3

    31 26 196 94 interrupts
    81 67 60 43 interrupts

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


    1. weirded
      05.07.2017 06:06

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


  1. AlexeyAB
    05.07.2017 16:31

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

    Встречались сетевые карты 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.


    1. weirded
      06.07.2017 22:44

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