[SWARM] Дуже низька продуктивність для вхідної мережі з великою кількістю паралельних запитів # 35082

Коментарі

Копіювати посилання Цитувати відповідь

відео прокоментував 4 жовтня 2017 р

Опис

Виконання великої кількості паралельних з'єднань проти звичайних Docker і Docker Swarms призводить до 2 абсолютно різних результатів, причому Swarm є найповільнішим на 50x фактор!
Тест можна відтворити (принаймні на моїх віртуальних машинах) легко за допомогою Siege та офіційного образу Nginx, але я насправді відчуваю проблему у виробництві з нашою власною мікросервісною службою HTTP на основі Java. Я не бачу жодного очевидного повідомлення про помилку в журналах Docker або журналах ядра.

Кроки для відтворення випуску:
Запустіть контейнер nginx:

Облога контейнера, і результати хороші, понад 13 тис. Транс/с, а процесор у stresstest01 на 100% використовується процесом nginx.

Тепер давайте спробуємо з Docker Swarm (1 рой вузлів, 1 стек контейнерів)

Після першого запуску результати вже набагато гірші, ніж у звичайного Docker, але після другого - це
просто катастрофа:( Більш того, центральний процесор хоста використовується лише незначно, і лише процесом nginx. Ніяких процесів, пов’язаних з докером (dockerd, cointanerd тощо), здається, не містить CPU.

Опишіть отримані результати:
Хороші виступи з простим Докером.
Дуже погані виступи з включеним Docker Swarm.

Опишіть результати, яких ви очікували:
Подібні вистави для двох ароматів Docker на одній машині

Додаткова інформація, яку ви вважаєте важливою (наприклад, проблема трапляється лише зрідка):

Вихід версії докера:

Виведення інформації про докер:

Додаткові відомості про середовище (AWS, VirtualBox, фізичний тощо):
Це віртуальна машина KVM (під oVirt), але те саме відбувається при використанні фізичної машини.

Текст успішно оновлено, але виявлені такі помилки:

відео прокоментував 4 жовтня 2017 р

Ця проблема є загальним блокатором для мене та для розгортання Swarm у виробництві. Це графік того, як змінювався час відгуку після перемикання компонента в нашій архітектурі з Swarm на звичайний докер на тих самих точних хостах

продуктивність

Думаю, я почну переїжджати в Кубернетес
(зелена лінія - операції/сек, ліва вісь Y)

(коментар скопійовано з №35009, оскільки спочатку я думав, що це та сама проблема)

мавенуго прокоментував 4 жовтня 2017 р

@vide вхід в ройовий режим обробляється IPVS, а з’єднання надсилаються до серверних завдань через мережу входу накладання. Але оскільки це єдиний вузол, зменшення продуктивності не може відбутися через заголовки VXLAN, що використовуються в накладній мережі. Єдиною можливою причиною може бути IPVS, і це може вимагати налаштування продуктивності для вашого випадку.

Ми можемо підтвердити теорію, якщо ви можете змінити файл стека за допомогою додаткового режиму параметрів: хост у розділі портів. Це обійде IPVS і використовуватиме власне відображення портів, як це робить запуск докера. Чи можете ви, будь ласка, підтвердити ?

відео прокоментував 4 жовтня 2017 р

@mavenugo Так, IPVS теж був моїм підозрюваним номер 1, не думав про режим: хост-фокус.
Знову порівняльний аналіз із запропонованими вами налаштуваннями:

Що можна порівняти з результатами звичайного докера.

Отже, яку настройку я можу зробити на IPVS у цьому випадку? Можливо, оновлення ядра? Очевидно, що мені потрібно балансування навантаження IPVS у виробництві:)

мавенуго прокоментував 5 жовтня 2017 р

@vide дякую за підтвердження. Нам слід витратити трохи більше часу на аналіз проблеми, перш ніж розглядати IPVS як джерело проблеми з продуктивністю (хоча я про це згадав у своєму попередньому коментарі:)). Я спробую облоги і зв’яжусь з тобою.

відео прокоментував 6 жовтня 2017 р

@mavenugo Я спробував ще раз на тому самому вікні CentOS з останнім ядром 4.13 (4.13.4-1.el7.elrepo.x86_64), і результати однакові.
Крім того, я спробував встановити Ubuntu 17.04 на своєму ноутбуці, і результати тут теж погані.

відео прокоментував 10 жовтня 2017 р

@mavenugo не могли б ви відтворити його на своєму комп'ютері?

xinfengliu прокоментував 30 жовтня 2017 р

Я можу точно відтворити проблему. Тестування встановлює нове підключення до кожного запиту.
Неактивні зв’язки незабаром накопичувались у ipvs.

Якщо ви не можете дочекатися InActConn до нуля і знову запустити тестування, ви отримаєте навіть поганий результат, як описано вище.

На стороні клієнта повно "SYN_SENT".

Якщо ви хочете обійти цю проблему, встановіть connection = keep-alive у своєму файлі .siegerc (використовуйте siege.config, щоб створити шаблон .siegerc).

мавенуго прокоментовано 2 листопада 2017 р. •

@vide @xinfengliu Я міг відтворити його та звузити до стану Conntracker, що спричинило проблему. Ми бачимо набагато кращу продуктивність, завдяки чому IPVS не використовує conntracker (через --sysctl net.ipv4.vs.conntrack = 0 лише для облогового контейнера).

До речі, прошу також зауважити, що я безпосередньо використовую сервіс VIP. Використання імені служби призводить до впливу на продуктивність, оскільки Siege виконує пошук DNS для кожного запиту, що затримує процес. Використання служби VIP безпосередньо видаляє пошук DNS, і продуктивність набагато краща.

відео прокоментовано 3 листопада 2017 р. •

@mavenugo Добре, так, як мені встановити conntrack віртуального сервера на 0 у режимі Swarm? Відповідно до https://docs.docker.com/compose/compose-file/#not-supported-for-docker-stack-deploy налаштування sysctl не підтримується при розгортанні стека докерів:(

Про це є відкрите питання: moby/libentitlement # 35

відео прокоментовано 3 листопада 2017 р. •

Здається, і ця проблема пов’язана: # 31746

мавенуго прокоментував 3 листопада 2017 р

@vide idk про підтримку розгортання стека докерів. Але чи можете ви, будь ласка, підтвердити, чи запропоноване обхідне рішення працює у випадку розгортання без стека ?

BSWANG прокоментовано 4 листопада 2017 р. •

--sysctl net.ipv4.vs.conntrack = 0 не можна використовувати при вхідній сітці маршрутизації на ingress_sbox. Як ipvs буде робити SNAT після переадресації.

У службі kubenetes-проксі-сервера kubenetes. встановить ці параметри ядра:
https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/proxier.go#L88-L91
та net.netfilter.nf_conntrack_buckets, net.netfilter.nf_conntrack_max .

az-z прокоментував 15 грудня 2017 р

я на RHEL7.4 і докер 1.12. Тестування кластера з 2 вузлами за допомогою nginx: останній розгорнуто в режимі затирання. Я можу відтворити результати @vide. Але мій тест трохи відрізняється.
Замість того, щоб запускати облогу як контейнер, я запускаю її ззовні кластера, щоб перевірити завантаження пари контейнерів nginx. Я відчуваю 10-кратна деградація у часі відгуку та пропускній здатності.

проти кластера:

проти самостійного nginx:

це повноцінне блокування для будь-якого подальшого впровадження докерського рою для нас. Яке пропоноване виправлення та терміни щодо цього? спасибі.

EmarMikey прокоментував 9 січня 2018 р. •

Ми зіткнулися з цією ж проблемою із сітчастим балансуванням LVS, у нас дуже низька продуктивність.
На даний момент я працював із конфігурацією режиму хосту, але сподіваюся, це лише тимчасове рішення.
Будь-який план, щоб це виправити?

тест у режимі хоста з ab (лише 1 контейнер):

перевірка в режимі входу з ab:
netstat на клієнті:

Вивід ipvsadm у вхідному просторі імен:

ДжексонХілл прокоментував 17 січня 2018 р

@ az-z який? docker-ce 17.12 або старий 1,12 або щось інше?

sbrattla прокоментовано 1 лютого 2018 року •

@vide чи перевіряли ви, чи у вашому налаштуванні рою відбуваються повторні передачі TCP. Ми бачимо багато повторних передач для трафіку, що прямує через ingress-sbox (де обробляється IPVS). Ingress-sbox - це той, що має IP 172.18.0.2 на docker_gwbridge .

Це можна легко побачити в нашому випадку між nginx та контейнером, який перезаписано, де часто додавали 1 секунду поверх загального часу запиту - те, що сильно вказувало на повторні передачі. Захоплення 20 секунд трафіку за допомогою дротової акустики на хості показало, що насправді a багато ретрансляцій йшли через docker_gwbridge.

Ми досі не прийшли до вирішення питання № 36032, яке, я повинен сказати, є досить критичним. Ми маємо це питання в діючій виробничій системі, і ми починаємо дуже розпачуватися з цього приводу.

Ми працюємо з ubuntu 16.04 та Docker 17.09 (ми оновили оновлення до 17.12, але це багато в чому було катастрофою, тому ми знову знизили версію).

вужефанг прокоментував 31 березня 2018 р

@vide привіт, чи є якийсь прогрес у цій справі?

відео прокоментував 3 квітня 2018 р

@wuzhefang Ні, вибачте, я переїхав до Kubernetes через цю проблему

тмарті прокоментував 18 квітня 2018 р

Відповідно до цього випуску та допису від № 31746, я можу додати тут трохи інформації.

Дуже прості кроки для відтворення з одним ройовим вузлом.

ОС машини, де працює докер:

а) Встановіть рій на одному вузлі та на цьому вузлі:

служба докера створити - ім'я nginx -p 80:80 - репліки 1 nginx

б) На тій самій консолі виконайте:

watch -n 0.5 "sudo nsenter --net =/var/run/docker/netns/ingress_sbox cat/proc/net/ip_vs_conn | grep TIME_WAIT | wc -l"

Це буде контролювати вхідну мережу для з'єднань у стані TIME_WAIT і кожні півсекунди виплюватиме, скільки їх існує на той момент.

в) На іншій машині в тій же мережі використовуйте генератор навантаження (я використовував ab з apache2-utils):

(ІР моєї докерної ройової машини - 192.168.1.11)

ab -n 10000 -c 100 http://192.168.1.11/

г) Якщо ви виконуєте фрагмент з c), +/- в команді watch від b) протягом достатнього часу буде показано:

Де 10064 - це 10k з'єднання з навантажувального тесту плюс кілька додаткових з'єднань (для нас це не має значення).

e) Якщо вам вдається виконати фрагмент з c) так, щоб результат з b) отримав таке саме значення, як результат від наступної команди на ройовому вузлі:

sysctl net.ipv4.ip_local_port_range | awk ''

Починатимуться затори. Більше немає доступних вихідних портів для цієї комбінації 'джерело IP + дестинація IP + порт дестинації'.

f) Опрацьовуючи звідси, трапляється, що механізм балансування навантаження в докер-рої використовує засоби від ipvs (модуль у ядрі Linux, який сам може виконувати функцію балансування навантаження).

g) Різновидом команди в b) є:

sudo nsenter --net =/var/run/docker/netns/ingress_sbox cat/proc/net/ip_vs_conn | керівник

Якщо ви виконаєте це відразу після виконання тесту навантаження, ви побачите щось на зразок:

Що говорить нам про те, що значення часу очікування для стану TIME_WAIT на підключеннях дуже ймовірно (принаймні в моїй тестовій установці) 120 с.

h) Ви можете sysctl вузол, де працює рій, шукаючи це значення 120 (отримане з g))

sysctl -a | grep 120

i) І мережа рійок докерів для цього ж значення:

sudo nsenter --net =/var/run/docker/netns/ingress_sbox sysctl -a | grep 120

j) І це кінець

З цього моменту, жодного параметра, який я налаштовував за допомогою будь-якого

Або

sudo nsenter --net =/var/run/docker/netns/ingress_sbox sysctl -w .

вплинуло на таймаут TIME_WAIT.

Насправді не знаю, чи ipvs/netfilter (основний механізм, що використовується ipvs), насправді використовує ці sysctl-ed-значення (принаймні, коли спрацьовує докер-рій).

І з цього моменту в глухий кут.

тмарті прокоментовано 19 квітня 2018 р. •

Нарешті знайшов проблему.

Як крайній варіант, і знаючи, що рій покладається на netfilter, дозволяє зробити внутрішнє балансування навантаження для накладених мереж (як дуже простий випадок для служби в превіум-пості, яка за замовчуванням використовує накладну мережу), я завантажив Linux Ядро і трохи підробив файли.

Джерела, що цікавлять, містяться в такій папці:

[директорія джерела ядра]/net/netfilter

Час очікування TIME_WAIT жорстко закодований всередині цього файлу з файлу ip_vs модуль, всередині:

[директорія джерела ядра] /net/netfilter/ipvs/ip_vs_proto_tcp.c

Ви можете перевірити останню версію цього файлу (яка страждає від тієї ж проблеми) тут:

Усередині цього файлу ви можете побачити такий фрагмент коду:

Отже, винним у цьому великому тайм-ауті є:

Якщо попередній змінити на:

Час очікування TIME_WAIT зменшено зі 120 до 2 секунд.

Потім перекомпіляція модуля, заміна системного модуля на скомпільований, перезавантаження ройової машини, перезапуск служби та повторне тестування навантаження дають неймовірно хороші результати. Більше затоплення з'єднань у стані TIME_WAIT не спостерігається для середньо великих навантажень (2000 req/s).

Якщо перевіряється код із решти файлу, насправді не існує способу (або я його не бачу) перезавантажити ці очікування. Це tcp_timeouts Схоже, вектор використовується для ініціалізації внутрішньої таблиці часу очікування, яка буде використовуватися для управління з'єднаннями (без, очевидно, жодного способу налаштування) у цій функції:

Файл ip_vs_ctl.c, який, здається, відповідає за оновлення налаштування модуля, виставляє такі параметри systctl:

Тут нічого не схоже на тайм-аути.

Отже, немає ефективного способу оновити параметр тайм-ауту TIME_WAIT для цього модуля після його запуску (ані налаштувати його, щоб модуль зчитував налаштоване значення під час ініціювання).

Якщо хтось уявляє, як можна вирішити цю проблему, він заслуговує на великі обійми.

Наразі знову в тупику. (не дуже практично перекомпілювати модулі ядра після кожного оновлення образу ядра)

раарти прокоментовано 19 квітня 2018 р. •

Фантастична робота ! Але список розсилки ядра, здається, є наступним кроком.

таДжезта прокоментував 19 квітня 2018 р

Дякую @tmarti, це, безумовно, цікава знахідка!

ctelfer прокоментовано 20 квітня 2018 р. •

2-хвилинний тайм-аут для TIME_WAIT є дуже стандартним на практиці. Це вдвічі перевищує максимальний час життя в Інтернеті (прогнозується) для сегмента TCP, і його мета полягає у забезпеченні доставки остаточного ACK. Якщо вона загубиться, інша сторона спробує повторно надіслати FIN, і держава все одно повинна бути там, щоб інший кінець повторно відповів остаточним ACK. (див. https://en.wikipedia.org/wiki/Maximum_segment_lifetime і, звичайно, https://www.ietf.org/rfc/rfc793.txt) Ви можете встановити MSL у ядрі Linux. але це рідко щось робиться. Очевидно, IPVS навіть не дає вам можливості.

Не знав про цю проблему, але прочитає її. Більша максимальна кількість відображень IPVS цілком може вирішити проблему і, мабуть, це щось, що можна встановити. (якщо максимальних відображень було достатньо, щоб поглинути стаціонарний стан поведінки.) Яка бажана швидкість з'єднання?

тмарті прокоментовано 20 квітня 2018 р. •

Звичайно! Як безглуздо з моєї сторони.

У мене є невеличка теорія, якою я хочу поділитися з вами.

Довелося спотикатися з цим постом.

. усвідомити один дуже простий факт.

Деякі з нас втомилися час від часу перевіряти вихідні дані netsat -nolap . і щодня ми бачимо, що TCP-з'єднання визначається значеннями:

  • джерело IP
  • вихідний порт
  • IP-адреса призначення
  • порт призначення

Зазвичай у цій комбінації є 2 ступені свободи:

ip джерела: оскільки ви зазвичай приймаєте з'єднання від багатьох різних клієнтів, ви можете вважати, що це значення поширюється між різними значеннями

вихідний порт: це буде відповідати деякому ефемерному порту від клієнта (ті, що знаходяться в (ubuntu land), зазвичай варіюються від 32768 до 60999

А інші два виправлені:

  • цільовий IP: загальнодоступний IP сервера
  • цільовий порт: порт веб-сервера (у цьому випадку 80)

У чому проблема з початковим тестом навантаження від @vide? (і моя, звичайно)

Проблема цієї установки полягає в тому, що ви фактично виправляєте вихідний IP (оскільки підключення під тестом навантаження надходять з одного ПК, що є вузлом, звідки ми запускаємо тест loa), і отримуєте на один ступінь менше свободи.

Отже, для тесту навантаження можливі комбінації «ключа», який однозначно ідентифікує одне підключення, зводяться до доступної кількості ефемерних портів на клієнті (це магічне число 28231), оскільки всі інші параметри фіксовані.

Що змусило шукати цю проблему в інших місцях?

Сьогодні по обіді я спробував це сильно відгадати, щоб копатися в коді модуля ipvs. Це не так просто, як це звучить: 16 тис. Рядків коду, і він реалізує власний стек TCP з регулюванням навантаження та NAT як бонусний трек.

Хороша річ у цьому полягає в тому, що я зміг побачити, що "ключ" списку "поточних з'єднань" складається саме з адреси джерела: порт (клієнтська!) Та адреси призначення: порт (як це зроблено у функції nf_nat_used_tuple of модуль).

Отже, що відбувається, коли клієнт намагається повторно використовувати порт (пам’ятайте, що інші 3 параметри завжди однакові в цьому забрудненому тесті навантаження), які відповідають з'єднанню в стані TIME_WAIT? Ну, в кінці спроба підключення відкидається (не впевнений, що це пов’язано з невідповідністю номеру послідовності TCP у стані підключення або іншим).

То що далі?

Для того, щоб підтвердити це, не потрібні налаштування sysctl, не потрібно втручання в джерело модуля ядра, нічого настільки низького рівня насправді не потрібно.

Замість того, щоб робити тест навантаження з 2000 запит/с з одного джерела IP (який вичерпає з'єднання приблизно через 14 секунд відповідно до діапазону портів 32767-60999 та інших виправлених параметрів), просто запустіть 200 рекв/с з 10 різних IP джерел, і підтвердити, що пропускна здатність залишається стабільною.

Протягом понеділка я спробую зробити запропонований тест і повернутися сюди.

Велике спасибі @raarts та @thaJeztah за заохочення.

І велика подяка @ctelfer за ваш коментар. Я справді застряг у ідеї систематизувати модуль ipvs, і ваш коментар спочатку переповнив мене, але нарешті змусив заглянути в інші місця.