- PVSM.RU - https://www.pvsm.ru -

Сравнение производительности сетевых решений для Kubernetes

Сравнение производительности сетевых решений для Kubernetes - 1

Kubernetes требует, чтобы каждый контейнер в кластере имел уникальный, маршрутизируемый IP. Kubernetes не назначает IP-адреса сам, оставляя эту задачу сторонним решениям.

Цель этого исследования — найти решение с наименьшими задержками, наибольшей пропускной способностью и самой небольшой стоимостью настройки. Поскольку наша нагрузка зависит от задержек, мы измеряем задержки высоких перцентилей [1] при достаточно активной сетевой нагрузке. В частности, мы сфокусировались на производительности в районе 30-50 процентов от максимальной нагрузки, поскольку это лучше всего отражает типовые ситуации для неперегруженных систем.

Варианты

Docker с --net=host

Наша образцовая инсталляция. Все другие варианты сравнивались с ней.

Опция --net=host [2] означает, что контейнеры наследуют IP-адреса своих хост-машин, т.е. сетевой контейнеризации нет.

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

Flannel

Flannel [3] — решение виртуальной сети, поддержкой которого занимается проект CoreOS [4]. Хорошо проверенное и готовое к production, поэтому стоимость внедрения минимальна.

Когда вы добавляете машину с flannel в кластер, flannel делает три вещи:

  1. Присваивает новой машине подсеть с помощью etcd [5].
  2. Создаёт виртуальный интерфейс моста [6] на машине (docker0 bridge).
  3. Настраивает бэкенд [7] пересылки пакетов:
    • aws-vpc — регистрирует подсеть машины в таблице экземпляров Amazon AWS. Количество записей в этой таблице ограничено 50, т.е. вы не можете иметь более 50 машин в кластере, если используете flannel с aws-vpc. Вдобавок, этот бэкенд работает только с Amazon AWS;
    • host-gw — создаёт IP-маршруты до подсетей через IP-адреса удалённой машины. Требует прямой L2-связности между хостами, на которых запущен flannel;
    • vxlan — создаёт виртуальный интерфейс VXLAN [8].

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

IPvlan

IPvlan [9] — драйвер в ядре Linux, позволяющий создавать виртуальные интерфейсы с уникальными IP-адресами без необходимости в использовании интерфейса моста.

Для назначения IP-адреса контейнеру с помощью IPvlan требуется:

  1. Создать контейнер вообще без сетевого интерфейса.
  2. Создать интерфейс ipvlan в стандартном сетевом пространстве имён.
  3. Переместить интерфейс в сетевое пространство имён контейнера.

IPvlan — относительно новое решение, поэтому пока нет готовых инструментов для автоматизации этого процесса. Таким образом, деплой IPvlan на множестве машин и контейнеров усложняется, то есть стоимость внедрения высока. Однако IPvlan не требует интерфейса моста и пересылает пакеты напрямую из NIC в виртуальный интерфейс, поэтому мы ожидали лучшей производительности, чем у flannel.

Сценарий тестирования нагрузки

Для каждого варианта мы проделали следующие шаги:

  1. Настроили сеть [10] на двух физических машинах.
  2. Запустили tcpkali [11] в контейнере на одной машине, настроив её на отправку запросов с постоянной скоростью.
  3. Запустили nginx в контейнере на другой машине, настроив его на ответ с файлом фиксированного размера.
  4. Сняли системные метрики и результаты работы tcpkali.

Мы запускали этот тест с разными количеством запросов: от 50 000 до 450 000 запросов в секунду (RPS).

Для каждого запроса nginx отвечал статическим файлом фиксированного размера: 350 байт (содержимое в 100 байт и заголовки в 250 байт) или 4 килобайта.

Результаты

  1. IPvlan продемонстрировал наименьшую задержку и наилучшую максимальную пропускную способность. Flannel с host-gw и aws-vpc следует за ним с близкими показателями, при этом host-gw проявил себя лучше под максимальной нагрузкой.
  2. Flannel с vxlan показал худшие результаты во всех тестах. Однако мы подозреваем, что его исключительно плохой перцентиль 99,999 вызван багом.
  3. Результаты для 4-килобайтного ответа похожи на случай с 350 байтами, но есть два заметных отличия:
    • максимальный показатель RPS значительно ниже, поскольку для 4-килобайтных ответов потребовалось всего ≈270 тысяч RPS для полной загрузки 10-гигабитной NIC;
    • IPvlan гораздо ближе к --net=host при приближении к лимиту пропускной способности.

Наш текущий выбор — flannel с host-gw. У него мало зависимостей (в частности, не требуется AWS или новая версия ядра Linux), он прост в установке по сравнению с IPvlan и предлагает достаточную производительностью. IPvlan — наш запасной вариант. Если в какой-то момент flannel получит поддержку IPvlan, мы перейдём на такой вариант.

Несмотря на то, что производительность aws-vpc оказалось немного лучше host-gw, ограничение в 50 машин и факт жёсткой привязки к Amazon AWS стали для нас решающими факторами.

50 000 RPS, 350 байт

Сравнение производительности сетевых решений для Kubernetes - 2
На 50 000 запросах в секунду все кандидаты продемонстрировали приемлемую производительность. Уже можно заметить основной тренд: IPvlan показывает лучшие результаты, host-gw и aws-vpc следуют за ним, а vxlan — худший.

150 000 RPS, 350 байт

Сравнение производительности сетевых решений для Kubernetes - 3

Перцентили задержки на 150 000 RPS (≈30 % от максимального RPS), мс
Сравнение производительности сетевых решений для Kubernetes - 4

IPvlan немного лучше, чем host-gw и aws-vpc, однако у него худший перцентиль 99,99. У host-gw немного лучшая производительность, чем у aws-vpc.

250 000 RPS, 350 байт

Сравнение производительности сетевых решений для Kubernetes - 5

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

Перцентили задержки на 250 000 RPS (≈50 % от максимального RPS), мс
Сравнение производительности сетевых решений для Kubernetes - 6

IPvlan снова показывает лучшую производительность, но у aws-vpc лучший результат в перцентилях 99,99 и 99,999. host-gw превосходит aws-vpc в перцентилях 95 и 99.

350 000 RPS, 350 байт

Сравнение производительности сетевых решений для Kubernetes - 7

В большинстве случаев задержка близка к результатам для 250 000 RPS (350 байт), однако она стремительно растёт после перцентиля 99,5, что означает приближение к максимальному RPS.

450 000 RPS, 350 байт

Сравнение производительности сетевых решений для Kubernetes - 8
Сравнение производительности сетевых решений для Kubernetes - 9

Интересно, что host-gw показывает гораздо лучшую производительность, чем aws-vpc:
Сравнение производительности сетевых решений для Kubernetes - 10

500 000 RPS, 350 байт

При нагрузке в 500 000 RPS только IPvlan всё ещё работает и даже превосходит --net=host, однако задержка столь высока, что мы не можем назвать её допустимой для приложений, которые чувствительны к задержкам.
Сравнение производительности сетевых решений для Kubernetes - 11

50 000 RPS, 4 килобайта

Сравнение производительности сетевых решений для Kubernetes - 12

Большие результаты запросов (4 килобайта против тестируемых ранее 350 байт) приводят к большей сетевой нагрузке, однако список лидеров практически не меняется:

Перцентили задержки на 50 000 RPS (≈20 % от максимального RPS), мс
Сравнение производительности сетевых решений для Kubernetes - 13

150 000 RPS, 4 килобайта

Сравнение производительности сетевых решений для Kubernetes - 14

У host-gw на удивление плохой перцентиль 99,999, однако он всё ещё показывает хорошие результаты для меньших перцентилей.

Перцентили задержки на 150 000 RPS (≈60 % от максимального RPS), мс
Сравнение производительности сетевых решений для Kubernetes - 15

250 000 RPS, 4 килобайта

Сравнение производительности сетевых решений для Kubernetes - 16

Это максимальный RPS с большим ответом (4 Кб). aws-vpc значительно превосходит host-gw в отличие от случая с небольшим ответом (350 байт).

Vxlan был снова исключён из графика.

Окружение для тестирования

Основы

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

В этих статьях содержится полезная информация по этой теме:

Машины

  • Мы использовали два экземпляра c4.8xlarge [15] в Amazon AWS EC2 с CentOS 7.
  • На обоих машинах включена enhanced networking [16].
  • Каждая машина — NUMA [17] с 2 процессорами, у каждого процессора по 9 ядер, у каждого ядра по 2 потока (hyperthreads), что обеспечивает эффективный запуск 36 потоков на каждой машине.
  • У каждой машины по сетевой карте 10Gbps (NIC) и 60 Гб оперативной памяти.
  • Для поддержки enhanced networking и IPvlan мы установили ядро Linux 4.3.0 [18] с драйвером Intel ixgbevf.

Конфигурация

Современные NIC используют Receive Side Scaling (RSS) [19] через многочисленные линии запроса прерывания (IRQ [20]). EC2 предлагает только две такие линии в виртуализированном окружении, поэтому мы протестировали несколько конфигураций с RSS и Receive Packet Steering (RPS) [21] и пришли к следующим настройкам, отчасти рекомендованным документацией по ядру Linux:

  • IRQ. Первое ядро каждого из двух узлов NUMA настроено на получение прерываний из NIC. Чтобы сопоставить CPU с узлом NUMA, используется lscpu:
    $ lscpu | grep NUMA
    NUMA node(s):          2
    NUMA node0 CPU(s):     0-8,18-26
    NUMA node1 CPU(s):     9-17,27-35

    Эта настройка выполняется записью 0 и 9 в /proc/irq/<num>/smp_affinity_list, где номера IRQ получаются через grep eth0 /proc/interrupts:

    $ echo 0 > /proc/irq/265/smp_affinity_list
    $ echo 9 > /proc/irq/266/smp_affinity_list
  • Receive Packet Steering (RPS). Было протестировано несколько комбинаций для RPS. Чтобы уменьшить задержку, мы разгрузили процессоры от обработки IRQ, используя только CPU под номерами 1–8 и 10–17. В отличие от smp_affinity в IRQ, у sysfs-файла rps_cpus нет постфикса _list, поэтому для перечисления CPU, которым RPS может направлять трафик, используются битовые маски (подробнее см. Linux kernel documentation: RPS Configuration [14]):
    $ echo "00000000,0003fdfe" > /sys/class/net/eth0/queues/rx-0/rps_cpus
    $ echo "00000000,0003fdfe" > /sys/class/net/eth0/queues/rx-1/rps_cpus
  • Transmit Packet Steering (XPS). Все процессоры NUMA 0 (включая HyperThreading, т.е. CPU под номерами 0—8, 18—26) были настроены на tx-0, а процессоры NUMA 1 (9—17, 27—37) — на tx-1 (подробнее см. Linux kernel documentation: XPS Configuration [14]):
    $ echo "00000000,07fc01ff" > /sys/class/net/eth0/queues/tx-0/xps_cpus
    $ echo "0000000f,f803fe00" > /sys/class/net/eth0/queues/tx-1/xps_cpus
  • Receive Flow Steering (RFS). Мы планировали использовать 60 тысяч постоянных подключений, а официальная документация рекомендует округлить это количество до ближайшей степени двойки:
    $ echo 65536 > /proc/sys/net/core/rps_sock_flow_entries
    $ echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
    $ echo 32768 > /sys/class/net/eth0/queues/rx-1/rps_flow_cnt
  • Nginx. Nginx использовал 18 рабочих процессов, у каждого — свой CPU (0—17). Это настраивается с помощью worker_cpu_affinity [22]:
    workers 18;
    worker_cpu_affinity 1 10 100 1000 10000 ...;
  • Tcpkali [11]. У Tcpkali нет встроенной поддержки привязки к конкретным CPU. Чтобы использовать RFS, мы запускали tcpkali в taskset и настроили планировщик для редкого переназначения потоков:
    $ echo 10000000 > /proc/sys/kernel/sched_migration_cost_ns
    $ taskset -ac 0-17 tcpkali --threads 18 ...

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

Ядра 0 и 9 обслуживают только прерывания сетевых карт (NIC) и не работают с пакетами, но они остаются самыми занятыми:

Сравнение производительности сетевых решений для Kubernetes - 17

Также был использовал tuned [23] от Red Hat с включённым профилем network-latency.

Для минимизации влияния nf_conntrack были добавлены правила NOTRACK [24].

Конфигурация sysctl была настроена на поддержку большого количества TCP-подключений:

fs.file-max = 1024000
net.ipv4.ip_local_port_range = "2000 65535"
net.ipv4.tcp_max_tw_buckets = 2000000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_low_latency = 1

От переводчика: Большое спасибо коллегам из Machine Zone, Inc за проведённое тестирование! Оно помогло нам, поэтому мы захотели поделиться им и с другими.

P.S. Возможно, вас также заинтересует наша статья «Container Networking Interface (CNI) — сетевой интерфейс и стандарт для Linux-контейнеров [25]».

Автор: zuzzas

Источник [26]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/proizvoditel-nost/259728

Ссылки в тексте:

[1] перцентилей: https://ru.wikipedia.org/wiki/%D0%9A%D0%B2%D0%B0%D0%BD%D1%82%D0%B8%D0%BB%D1%8C#.D0.9F.D0.B5.D1.80.D1.86.D0.B5.D0.BD.D1.82.D0.B8.D0.BB.D1.8C

[2] --net=host: https://docs.docker.com/engine/reference/run/#network-settings

[3] Flannel: https://github.com/coreos/flannel

[4] CoreOS: https://coreos.com/

[5] etcd: https://github.com/coreos/etcd

[6] интерфейс моста: http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge

[7] бэкенд: https://github.com/coreos/flannel#backends

[8] интерфейс VXLAN: https://en.wikipedia.org/wiki/Virtual_Extensible_LAN

[9] IPvlan: https://www.kernel.org/doc/Documentation/networking/ipvlan.txt

[10] Настроили сеть: http://machinezone.github.io/research/networking-solutions-for-kubernetes/#environment

[11] tcpkali: https://github.com/MachineZone/tcpkali

[12] How to receive a million packets per second: https://blog.cloudflare.com/how-to-receive-a-million-packets/

[13] How to achieve low latency with 10Gbps Ethernet: https://blog.cloudflare.com/how-to-achieve-low-latency/

[14] Scaling in the Linux Networking Stack: https://www.kernel.org/doc/Documentation/networking/scaling.txt

[15] c4.8xlarge: https://aws.amazon.com/ec2/instance-types/#c4

[16] enhanced networking: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking.html

[17] NUMA: https://en.wikipedia.org/wiki/Non-uniform_memory_access

[18] Linux 4.3.0: http://elrepo.org/linux/kernel/el7/x86_64/RPMS/

[19] Receive Side Scaling (RSS): https://msdn.microsoft.com/en-us/library/windows/hardware/ff556942(v=vs.85).aspx

[20] IRQ: https://en.wikipedia.org/wiki/Interrupt_request_(PC_architecture)

[21] Receive Packet Steering (RPS): https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/network-rps.html

[22] worker_cpu_affinity: http://nginx.org/en/docs/ngx_core_module.html#worker_cpu_affinity

[23] tuned: https://fedorahosted.org/tuned/

[24] правила NOTRACK: http://serverfault.com/a/536303/99164

[25] Container Networking Interface (CNI) — сетевой интерфейс и стандарт для Linux-контейнеров: https://habrahabr.ru/company/flant/blog/329830/

[26] Источник: https://habrahabr.ru/post/332432/