Порівняння рішень по балансуванню високонавантажених систем

І знову ми публікуємо розшифровки виступів з конференції HighLoad++, яка пройшла в підмосковному Сколково 7-8 листопада 2016 року. Сьогодні Євген Півень знайомить з рішеннями балансування в хмарах.

Мене звати Женя, я працюю в компанії IPONWEB. Сьогодні ми поговоримо про розвиток наших рішень в балансуванні високонавантажених систем.

Спочатку я пробегусь за поняттями, якими буду оперувати. Почнемо з того, чим ми займаємося: RTB, Real Time Bidding — показ реклами з аукціоном в реальному часі. Дуже спрощена схема того, що відбувається, коли ви заходите на сайт:



Для того, щоб показати рекламу, йде запит на RTB-сервер, який запитує у рекламних серверів їх ставки і потім вирішує, яку рекламу вам показати.

Особливості IPONWEB
У нас вся інфраструктура в хмарах. Ми дуже активно користуємося Amazon і GCE, у нас кілька тисяч серверів. Головна причина, по якій ми живемо в хмарах, — це скалируемость, тобто у нас реально часто потрібно додавати/видаляти инстансы, іноді дуже багато.

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

Для нас не актуально кешування. Ми не підтримуємо CDN. Якщо нашим клієнтам потрібні CDN, то вони самі займаються цими рішеннями. У нас є дуже сильні добові і подієві коливання. Вони проявляються під час свят, спортивних подій, якихось сезонних знижок і т. д. Добові можна добре подивитися на такому графіку:



Червоний графік — звичайний стандартний графік в європейській країні, а синій графік — це Японія. На цьому графіку ми бачимо, що кожен день приблизно в дванадцять у нас є різкий стрибок, і приблизно за годину трафік різко падає. Пов'язано це з тим, що люди йдуть на обід, і як дуже порядні японські громадяни вони користуються інтернетом активно більше всього на обіді. Таке дуже добре видно на цьому графіку.

У нас є великий розкид користувачів по всьому світу. Це — друга велика причина, чому ми користуємося хмарами. Нам дуже важливо віддати відповідь швидко, і якщо сервери знаходяться в якихось інших регіонах від користувачів, то часто це неприйнятно для RTB-реалій.

І у нас є чітке розмежування серверного та користувацького трафіку. Повернемося до тієї схеми, яку я показав на першому слайді. Для простоти все те, що приходить від користувача до первинного RTB-сервера — це користувальницький трафік, все те, що відбувається за ним — це серверний трафік.



Навіщо балансувати?
Дві головні причини — це масштабованість і доступність сервісів.

Масштабованість — це коли ми додаємо нові сервера, коли ми з одного сервера, який вже не може впоратися, виростаємо як мінімум до двох, і між ними треба якось розкидати запити. Доступність — це коли ми боїмося, що з цим одним сервером щось трапиться. Знову ж потрібно додати другий, щоб між ними як-то все це балансувати і розподіляти запити тільки з тих серверів, які можуть на них відповісти.

Що ще часто вимагається від балансировщиков? Ці функції, природно, для цілей кожної програми можуть бути різними. Для нас найбільше актуально SSL Offload. Як це працює, показано тут.



Від користувача до балансувальника йде трафік, який зашифрований. Балансувальник розшифровує його, розкидає по бэкендам вже розшифрований HTTP-трафік. Потім балансувальник назад зашифровує його і віддає користувачеві знову в зашифрованому вигляді.

Ще нам дуже важлива така жарт, як Sticky-балансування, яку часто називають session affinity.



Чому для нас це актуально? Коли ми бачимо кілька слотів для реклами на сторінці, ми хочемо, щоб при відкритті цієї сторінки всі запити прийшли відразу на один бекенд. Чому це важливо? Є така особливість як roadblock. Вона означає, що якщо ми показали в одному з слотів банер, наприклад, Pepsi, то в іншому ми не можемо показати банер тієї ж самої Pepsi чи Coca-Cola, тому що це конфліктуючі банери.

Нам потрібно розуміти, які банери ми показуємо у даний момент користувачеві. При балансуванні нам потрібно впевнитися що користувач прийшов на один бекенд. На цьому бэкенде у нас створюється якусь подобу сесії, і ми розуміємо які саме реклами можна показати користувачеві.

Також у нас є Fallback. На прикладі зверху видно банер, на якому він не працює, а праворуч — банер, на якому він працює.



Fallback — це ситуація, коли з якихось причин бекенд не справляється, і щоб не зламати сторінку користувачеві, ми віддаємо йому зазвичай зовсім порожній піксель. Тут я його намалював великим зеленим квадратом для розуміння, але насправді зазвичай це просто маленька гифка, двохсотий HTTP Response і правильний набір header'ів, щоб у нас не зламалося нічого у верстці.

Так у нас виглядає нормальна балансування.



Синя жирна лінія — це сума всіх запитів. Ці маленькі лінії, багато-багато, — це йдуть запити на кожен інстанси. Як ми бачимо, тут балансування досить хороша: практично всі вони йдуть майже однаково, зливаючись в одну лінію.

Це — балансування курця. Тут щось пішло не так.



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

Варто сказати, що метрики — це добре. Тут Amazon досі не вірить нам, що у нас відбувається щось погане. Вони бачать тільки ось цей загальний графік, на якому видно лише суму запитів, але не видно, скільки запитів приходить з инстансам. Досі з ними боремося, намагаємося їм довести, що з їх балансировщиком йде не так.

Балансування DNS
Отже, про балансування я розповім на прикладі одного з наших проектів. Почнемо із самого початку. Проект був молодий, ми приблизно близько початку цього проекту.



Велика частина трафіку в цьому проекті — серверний. Особливість роботи з серверним трафіком: ми можемо легко вирішувати деякі проблеми. Якщо у нас є специфіка у одного з наших клієнтів, то ми можемо якось домовитися з ним, щоб вони щось змінили на своєму кінці, як-то оновили свої системи, що ще зробили, щоб у нас краще працювало з ними. Можна виділити їх в окремий пул: ми можемо просто взяти одного клієнта, у якого є якась проблема, прив'язати його до іншого пулу і вирішити проблему локально. Або, зовсім важких випадках, можна навіть його забанити.

Перше, чим ми почали користуватися, — це була звичайна DNS-балансування.



Користуємося Round-robin DNS-пулами.



Кожен раз при запиті на DNS, пул ротується, і зверху виявляється новий IP-адресу. Таким чином працює балансування.

Проблеми звичайного Round-Robin DNS:

  • У нього немає ніяких перевірок статусу. Ми не можемо зрозуміти, що з бэкэндом сталося щось не те і не відсилати на нього запити.
  • У нас немає розуміння геолокації клієнта.
  • Коли запити йдуть з достатньо маленького кількості айпишников, що актуально для серверного трафіку, то балансування може бути не дуже ідеальною.
Балансування gdnsd
На допомогу приходить gdnsd – це DNS сервер, який багато хто, напевно, знають, яким ми активно користуємося і зараз.

  • Головна фіча gdnsd, якою ми користуємося, — це DYNA-запису. Це такі записи, які видають на кожен запит, використовуючи якийсь плагін, динамічно небудь одну A-запис, або набір A-записів. Там всередині вони можуть використовувати Round-robin.
  • gdnsd вміє підтримувати бази даних geoIP.
  • У нього є перевірка статусу. Він може по TCP якісь запити надсилати хосту, HTTP дивитися Response і викидати з пулу ті сервери, які не використовуються, в яких у даний момент є якась проблема.
Щоб підтримувати динамічність цих записів, нам потрібно підтримувати досить низький TTL. Це сильно збільшує трафік на наші DNS-сервера: досить часто клієнтам доводиться перезапрашивать ці пули, і тому DNS-серверів, відповідно, доводиться мати більше.

Через якийсь проміжок часу ми стикаємося з проблемою 512 байт.



Проблема 512 байт — це проблема практично всіх DNS-серверів. Спочатку, коли DNS тільки проектувався, максимальний MTU у модемів був 576 байт. Це 512 байт + 64 довжини заголовка. Пакети від DNS історично не посилають більше, ніж 576 байт по UDP.
Відповідно, якщо у нас пул довше, ніж 512 байт, то ми відсилаємо тільки частину пулу, включаємо в ньому прапор truncated. Потім вже від клієнта приходить запит по TCP, перепитуючи нас знову це пул. І тоді ми надсилаємо йому повний пул, тепер вже по TCP.

Ця проблема була лише у частини наших клієнтів, приблизно у 15%. Ми змогли виділити їх окремо в пул і використовувати для них weighted-пули в gdnsd.

Бонус weighted-пулів у цьому випадку — їх можна розбивати. Якщо у нас, скажімо, 100 серверів, ми розбиваємо їх на 5 частин. Ми віддаємо на кожен запит один з цих маленьких подпулов, в яких всього 20 серверів. І Round-robin проходить за цим маленьким пулами, кожен раз він видає новий пул. Всередині самого пулу теж використовується Round-Robin: він шаффлит ці айпишники і кожен раз видає нові.

Ваги gdnsd можна використовувати крім цього, для, наприклад, staging серверів. Якщо у вас є більш слабкий інстанси, ви на нього можете спочатку відсилати набагато менше трафіку і перевіряти, що щось там зламалося, тільки на ньому, відсилаючи на нього досить маленький набір трафіку. Або якщо у вас є різні типи инстансов, або ви використовуєте різні сервера. (Я часто кажу «инстансы» тому що у нас все в хмарах, але для вашого конкретного випадку це може бути не так.) Тобто ви можете використовувати різні типи серверів і з допомогою gdnsd слати на них більше або менше трафіку.

Тут у нас теж виникає проблема — DNS-кешування. Часто, коли йде запит цього пулу, ми віддаємо тільки маленький пул, і цей пул кешується. З цим пулом у нас продовжує жити якийсь клієнт, не перепитуючи наш DNS. Таке трапляється, коли DNS-клієнт погано себе веде, не дотримується TTL і працює тільки з маленьким обмеженим набором IP-адрес, не оновлюючи його. Якщо він отримав спочатку повний лист по TCP, це нормально. Але якщо він отримав тільки маленький пул, який weighted, то це може бути проблемою.

Через якийсь час ми стикаємося з новою проблемою.



Ті, що залишилися 85% серверного трафіку все ще користуються звичайними мультифопулами, як це називається в gdnsd. З деякими з них починаються проблеми.

Ми зрозуміли, що проблема відбувається тільки у амазоновских DNS. Тобто у тих наших клієнтів, які самі хостятся в Amazon, при ресолве пулу, в якому знаходиться більше 253 хостів, їм просто приходить помилка NXDOMAIN, і вони повністю не ресолвят цей цілий пул.

Це сталося тоді, коли ми додали близько 20 хостів, та у нас їх стало 270. Ми локалізували число до 253, ми зрозуміли, що на цьому кількості стає проблемно. Зараз цю проблему вже полагодили. Але на той момент ми зрозуміли, що тупцюємо на місці, і треба якось цю проблему вирішувати далі.

Так як ми знаходимося в хмарах, перше, про що ми подумали, — спробувати вертикальне масштабування. Воно спрацювало, відповідно, ми скоротили кількість инстансов. Але знову ж, це тимчасове рішення проблеми.

ELB
Ми вирішили спробувати щось ще, тоді вибір упав на ELB.



ELB – це Elastic Load Balancing, рішення від Amazon, яка балансує трафік. Як це працює?

Вони надають вам CNAME, в даному випадку це ось ця страшна рядок під
www.site.com
: elb, цифри, регіон і так далі. І цей CNAME ресолвится на кілька внутрішніх айпишников инстансов, які балансують на наші бэкенды. В такому разі нам потрібно всього один раз прив'язати їх CNAME в нашому DNS до нашого пулу. Потім ми вже додаємо в групу сервери, на які розкидають балансировщики.

ELB вміє SSL Offload, до нього можна причепити сертифікат. Також він вміє HTTP status checks, щоб розуміти, наскільки живі у нас инстансы.

Ми практично відразу стали стикатися з проблемами з ELB. Є такий жарт як прогрів балансировщиков ELB. Це потрібно, коли ви хочете пускати більше 20-30 тисяч запитів в секунду. Перш ніж перевести весь ваш трафік на ELB, вам потрібно написати листа в Amazon, сказати, що ми хочемо пустити багато трафіку. Вам надсилають лист з купою страшних питань про характеристики вашого трафіку, скільки, коли, як довго ви збираєтеся це все підтримувати. Потім вони додають нових инстансов в свій пул і готові до напливу трафіку.

І навіть при попередньому прогріві ми зіткнулися з проблемою. Коли ми у них запросили 40 тисяч запитів в секунду, приблизно на 30 тисячах у них все зламалося. Нам довелося все це справа швидко відкочувати.

Ще у них є балансування по швидкості відповідей. Це алгоритм роботи амазоновского балансувальника. Він дивиться наскільки швидко ваші бэкенды відповідають. Якщо він бачить, що швидко, то шле туди більше трафіку.

Проблема тут в чому? Якщо ваш бекенд відчайдушно пятисотит [видає код стану HTTP 5XX, що говорить про помилку сервера] і не справляється, то балансувальник думає, що бекенда дуже швидко віддає відповіді, і починає слати йому ще більше трафіку, загинаючи ваш бекенда ще сильніше. В наших реаліях це ще проблемний, тому що ми, як я вже розповідав, зазвичай відсилаємо 200-ий відповідь, навіть якщо все погано. Користувач не повинен бачити помилки — просто посилаємо порожній піксель. Тобто для нас цю проблему вирішити ще складніше.

На останній конференції Amazon вони розповідали, що якщо у вас щось погане відбувається, то в exception'и завертайте які-небудь таймаут по 100-200 мс, штучно сповільнюючи 500-ті відповіді, щоб амазоновской балансувальник розумів, що ваш бекенд не справляється. Але взагалі, по-хорошому треба робити правильні status checks. Тоді ваш бекенд розумів би, що є проблеми, і віддавав би на status checks перевірки проблему, і його просто викидало б з пулу.

Тепер у Amazon з'явилося нове рішення: Application Load Balancer (ALB). Це досить цікаве рішення, але нам воно не дуже актуально, тому що воно для нас нічого не вирішує і швидше за все буде коштувати набагато більше. Їх система з хостами стала складніше.

Але ALB підтримує Path-based routing: це означає, що якщо у вас, наприклад, користувач приходить на
/video
, то ви можете направляти запит на один набір инстансов, якщо на
/static
, то на інший, і так далі.

Є підтримка WebSocket, HTTP/2 і контейнерів. Якщо у вас Docker всередині одного инстанса, то він може розподіляти між ними.

GLB
В Google ми користуємося GLB. Це досить цікаве рішення. Порівняно з Amazon, у нього є багато переваг.

Перше — у нас всього 1 IP. Коли ви створюєте балансувальник в Google, вам дається єдиний айпішнік, який ви можете прив'язати до свого сайту. Це означає, що ви можете прив'язати його навіть до «голому» домену. CNAME ж можна прив'язати до домену другого рівня.



Вам потрібно створити лише один балансувальник по всіх регіонах. Тобто в Amazon нам потрібно в кожному регіоні створювати балансировщику, щоб балансувати між инстансами всередині цього регіону, а Google — всього один балансувальник, всього один IP, і він балансує між усіма вашими инстансами по різних регіонах.

Гуглівський балансувальник вміє Sticky і по IP, і по cookie. Я розповідав, навіщо потрібен Sticky — нам потрібно одного користувача переслати на один бекенда. Амазонівські балансировщики вміють тільки за cookie, тобто вони самі на рівні балансувальника видають cookie. Потім його перевіряють, і якщо видно, що у користувача cookie, відповідний одному з инстансов, його відсилають на той самий. Гуглівський вміє IP, що нам набагато краще, хоч і не завжди вирішує всі проблеми.

У балансувальника Google Instant warm-up: його не треба ніяк прогрівати, на нього відразу можна відсилати до мільйона запитів. Мільйон запитів — це те, що вони обіцяють самі точно, і те, що я сам перевіряв. Думаю, далі вони ростуть як-то самі всередині.

Але при цьому у них є проблема з різким зміною числа бэкендов. В якийсь момент ми додали близько 100 нових хостів. На цьому етапі гуглівський балансувальник загнувся. Коли ми почали спілкуватися з інженером з Google, вони сказали: додавайте по одному в хвилину, тоді буде вам щастя.

Також нещодавно в їхньому HTTP балансировщике підрізали порти, якими ви можете користуватися при створенні балансувальника. Раніше було звичайне поле для вводу, зараз можна вибрати тільки між 80 і 8080 портами. Для когось це може бути проблемою.

Ні у одного з цих хмарних балансировщиков немає підтримки SNI. Якщо вам потрібно підтримувати декілька доменів і декілька сертифікатів, то вам потрібно на кожний сертифікат створювати за окремим балансировщику і до нього прив'язувати. Це розв'язувана проблема, але така ситуація може бути незручною.


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.