Як Badoo досяг можливості віддавати 200k фото в секунду

Як Badoo досяг можливості віддавати 200k фото в секунду

Сучасний веб практично немислимий без медіаконтенту: смартфони є практично у кожної нашої бабусі, всі сидять у соцмережах, і простої в обслуговуванні дорого коштують компаніям. До вашої уваги розшифровка оповідання компанії Badoo про те, як вона організувала віддачу фотографій за допомогою апаратного рішення, з якими проблемами продуктивності зіткнулася в процесі, чим вони були викликані, та як ці проблеми були вирішені за допомогою софтового рішення на основі Nginx, забезпечивши при цьому відмовостійкість на всіх рівнях (відео). Дякуємо авторам оповідання Олега Sannis Єфімова та Олександра Димова, які поділилися своїм досвідом на конференції Uptime day 4.

— Почнемо з невеликого введення про те, як ми зберігаємо та кешуємо фотографії. У нас є шар, на якому ми їх зберігаємо, та шар, де ми фотографії кешуємо. При цьому, якщо ми хочемо домагатися великого хітрейту та знижувати навантаження на стораджі, нам важливо, щоб кожна фотографія окремого користувача лежала на одному сервері, що кешує. Інакше нам довелося б ставити в стільки разів більше дисків, скільки у нас більше серверів. Хітрейт у нас у районі 99%, тобто ми у 100 разів знижуємо навантаження на наші storage, і для того, щоб це зробити, ще 10 років тому, коли все це будувалося, ми мали 50 серверів. Відповідно, для того, щоб ці фотографії віддавати нам потрібно було по суті 50 зовнішніх доменів, які ці сервери обслуговують.

Звичайно, відразу постало питання: а якщо у нас один сервер впаде, буде недоступний, яку частину трафіку ми втрачаємо? Ми подивилися, що є на ринку і вирішили купити залізницю, щоб вона вирішила всі наші проблеми. Вибір припав на рішення компанії F5-network (яка, до речі, недавно купила NGINX, Inc): BIG-IP Local Traffic Manager.

Як Badoo досяг можливості віддавати 200k фото в секунду

Що ця залізниця (LTM) робить: це залізний роутер, який робить залізне redundancy своїх зовнішніх портів і дозволяє роутити трафік, ґрунтуючись на топології мережі, на якихось налаштуваннях, робить health-check'і. Нам було важливо те, що цю залізницю можна програмувати. Відповідно ми могли описати логіку того, як фотографії певного користувача віддавалися з якогось конкретного кеша. Як це виглядає? Є залізниця, яка дивиться в інтернет по одному домену, одному ip, робить ssl offload, розбирає http-запити, з IRule вибирає номер кеша, куди йти, і пускає туди трафік. При цьому вона робить health-cheсk'і, і в разі недоступності якоїсь машини ми зробили на той момент так, щоб трафік йшов на один резервний сервер. З точки зору конфігурування є, звичайно, деякі нюанси, але в цілому все досить просто: ми прописуємо карту, відповідність якогось числа нашому IP в мережі, говоримо, що ми будемо слухати на 80-му і 443-му портах, говоримо, що, якщо сервер недоступний, потрібно пускати трафік на резервний, у разі 35-й, і описуємо купу логіки, як цю архітектуру треба розбирати. Єдина проблема була в тому, що мова, якою програмується залізниця, це мова Tcl. Якщо хтось взагалі такий пам'ятає… мова ця більше write-only, ніж мова, зручна для програмування:

Як Badoo досяг можливості віддавати 200k фото в секунду

Що ми здобули? Ми отримали залізницю, яка забезпечує високу доступність нашої інфраструктури, роутит весь наш трафік, забезпечує health-cheсk'і та просто працює. До того ж працює досить довго: за останні 10 років до неї не було жодних нарікань. На початок 2018 року ми вже віддавали близько 80k фотографій на секунду. Це приблизно 80 гігабіт трафіку з обох наших дата-центрів.

Однак ...

На початку 2018 року ми на графіках побачили негарну картину: час віддачі фотографій виріс. І вона перестала нас влаштовувати. Проблема в тому, що така поведінка була видна лише в самий пік трафіку — для нашої компанії це ніч із неділі на понеділок. Але решта часу система поводилася як завжди, ніяких ознак поломки.

Як Badoo досяг можливості віддавати 200k фото в секунду

Проте проблему треба було вирішувати. Ми визначили можливі вузькі місця та почали їх ліквідувати. В першу чергу, звичайно, ми розширили зовнішні uplinks, провели повну ревізію внутрішніх uplinks, знайшли всі можливі вузькі місця. Але очевидного результату це не дало, проблема не зникла.

Ще одним можливим вузьким місцем була продуктивність самих фотокешів. І ми вирішили, що, можливо, проблема впирається в них. Що ж, розширили продуктивність — переважно мережеві порти на фотокешах. Але знову жодного явного покращення не бачили. Зрештою, звернули увагу на продуктивність самого LTM-а, і ось тут побачили на графіках сумну картину: завантаження всіх CPU починає йти плавно, але потім різко впирається в полицю. При цьому LTM перестає адекватно реагувати на health-check і uplink і починає їх випадковим чином вимикати, що веде до серйозної деградації продуктивності.

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

Як Badoo досяг можливості віддавати 200k фото в секунду

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

Так як завдання звучало як «зробити щось максимально швидко і використовуючи те залізо, яке у нас є», перше, що ми подумали, — це просто зняти з фронту якісь не найпотужніші машини, поставити туди Nginx, з яким ми вміємо працювати, і спробувати реалізувати всю ту саму логіку, яку раніше робила залізниця. Тобто фактично ми залишали нашу залізницю, ставили ще 4 сервери, які мали сконфігурувати, робили для них зовнішні домени за аналогією з тим, як це було 10 років тому… Ми трохи втрачали доступність у разі падіння цих машин, але, тим не менше менш, вирішували локально проблему наших користувачів.

Відповідно, логіка залишається такою ж: ми ставимо Nginx, він вміє робити SSL-offload, ми можемо на конфігах якось спрограмувати логіку роутингу, health-check'і і просто продублювати ту логіку, яка в нас була до цього.

Сідаємо написати конфіги. Спершу здавалося, що все було дуже просто, але, на жаль, під кожну задачу знайти мануали дуже складно. Тому не радимо просто гуглити «як налаштувати Nginx для фотографій»: краще звернутися до офіційної документації, яка покаже, які настройки варто чіпати. Але конкретний параметр краще вибирати самим. Ну, а далі все просто: описуємо сервери, які ми маємо, описуємо сертифікати… Але найцікавіше — це, власне, сама логіка роутингу.

Спочатку нам здавалося, що ми просто описуємо наш локейшн, матчимо в ньому номер нашого фотокешу, руками або генератором описуємо, скільки нам потрібно апстрімів, у кожному апстрімі вказуємо сервер, на який повинен йти трафік, і бекапний сервер у випадку, якщо основний сервер недоступний:

Як Badoo досяг можливості віддавати 200k фото в секунду

Але, напевно, якби все було б так просто, ми просто розійшлися б по домівках і нічого не розповідали. На жаль, з дефолтними налаштуваннями Nginx, які, загалом, зроблені за довгі роки розробки і не зовсім під цей кейс ... конфіг виглядає так: у випадку, якщо у якогось апстрім-сервера відбувається помилка запиту або тайм, Nginx завжди перемикає трафік наступного. При цьому після першого фейлу протягом 10 секунд сервер також буде вимкнений, причому і помилково, і по таймууту - це навіть не можна конфігурувати. Тобто якщо ми приберемо або скинемо в апстрім-директиві опцію таймаут, то, хоча Nginx і не оброблятиме цей запит і відповість якоюсь не дуже гарною помилкою, сервер вимикатиметься.

Як Badoo досяг можливості віддавати 200k фото в секунду

Щоб цього уникнути, ми зробили дві речі:

а) заборонили робити це Nginx'у руками – і на жаль, єдиний спосіб зробити це – просто задати налаштування max fails.

б) згадали, що ми в інших проектах використовуємо модуль, який дозволяє робити фонові health-check'і — відповідно, ми зробили досить часті health-check'и, щоб простий у разі аварії ми мали мінімальний.

На жаль, це теж не все, тому що буквально перші два тижні роботи цієї схеми показали, що TCP health-check теж штука ненадійна: на апстрім-сервері може бути піднятий не Nginx, або Nginx в D-state, і в цьому випадку ядро прийматиме з'єднання, health-check проходитиме, а працювати не буде. Тому ми відразу ж замінили це на health-check http'шний, зробили певний, який якщо вже віддає 200, то все працює у цьому скрипті. Можна робити додаткову логіку — наприклад, у разі серверів кеші перевіряти, що правильно змонтована файлова система:

Як Badoo досяг можливості віддавати 200k фото в секунду

І нас би це влаштувало, за винятком того, що на даний момент схема повністю повторювала те, що робила залізниця. Але ми хотіли зробити краще. Раніше у нас був один резервний сервер, і, напевно, це не дуже добре, тому що якщо серверів у вас сто, то коли падає відразу кілька, один резервний сервер навряд чи впорається з навантаженням. Тому ми вирішили резервування розподілити по всіх серверах: зробили просто ще один окремий апстрім, записали туди всі сервери з певними параметрами відповідно до того, яке навантаження вони можуть обслуговувати, додали ті самі health-check'и, які у нас були до цього :

Як Badoo досяг можливості віддавати 200k фото в секунду

Так як всередині одного апстріму не можна ходити в інший апстрім, потрібно було зробити так, щоб у разі недоступності основного апстріму, в якому просто записували правильний фотокеш, ми просто через error_page йшли на fallback, звідки йшли на резервний апрстрім:

Як Badoo досяг можливості віддавати 200k фото в секунду

І буквально додавши чотири сервери, ми ось що отримали: замінили частину навантаження — зняли з LTM на ці сервери, реалізували там ту ж логіку, використовуючи стандартне залізо та софт, одразу отримали бонусом, що ці сервери можна масштабувати, тому що їх можна просто поставити стільки, скільки потрібно. Ну і єдиний мінус – ми втратили високу доступність зовнішніх користувачів. Але на той момент довелося пожертвувати цим, бо треба було негайно вирішити проблему. Отже, частину навантаження ми зняли, це близько 40% на той момент, LTM стало добре, а буквально через два тижні після початку проблеми ми стали віддавати не 45k запитів в секунду, а 55k. По суті ми зросли на 20% — це явно той трафік, який ми недовіддавали користувачеві. І після цього почали думати, як вирішити проблему, що залишилася — забезпечити високу зовнішню доступність.

Як Badoo досяг можливості віддавати 200k фото в секунду

У нас була деяка пауза, під час якої ми обговорювали, яке рішення ми використовуватимемо. Були пропозиції забезпечувати надійність за допомогою DNS, за допомогою якихось самописних скриптів, протоколів динамічної маршрутизації… варіантів було багато, але вже стало зрозуміло, що для справді надійної віддачі фотографій потрібно ввести ще один шар, який слідкуватиме за цим. Ми назвали ці машини photo directors. Як програмне забезпечення, на яке ми спиралися, вибрав Keepalived:

Як Badoo досяг можливості віддавати 200k фото в секунду

Для початку – з чого Keepalived складається. Перше - це протокол VRRP, широко відомий мережевим, розташований на мережному обладнанні, що забезпечує відмовостійкість зовнішньої IP-адреси, на яку з'єднуються клієнти. Друга частина - IPVS, IP virtual server, для балансування між фото-роутерами і забезпечення відмовостійкості на цьому рівні. І третє - health-check'і.

Почнемо з першої частини: VRRP – як це виглядає? Є якийсь віртуальний IP, на який є запис в dns badoocdn.com, куди підключаються клієнти. У якийсь момент часу у нас IP-адреса присутня на якомусь одному сервері. Між серверами бігають по протоколу VRRP keepalived-пакети, і якщо майстер пропадає з радарів - сервер перезавантажився або ще щось, то бекапний сервер автоматично піднімає цю IP адресу у себе - не треба робити ніяких ручних дій. Відрізняються майстер та бекап, в основному priority: чим воно вище, тим більше шансів, що машина стане майстер. Дуже велика перевага, що не потрібно конфігурувати IP-адреси на самому сервері, досить описати їх в конфізі, і якщо при цьому IP-адресам необхідні якісь кастомні правила маршрутизації, це описується прямо в конфізі, тим же синтаксисом, як це описується у пакеті VRRP. Жодних незнайомих речей вам не зустрінеться.

Як Badoo досяг можливості віддавати 200k фото в секунду

Як це виглядає практично? Що відбувається, якщо один із серверів йде у відмову? Як тільки майстер пропадає, у нас бекап припиняє отримувати адвертисменти та автоматично стає майстром. Через якийсь час ми відремонтували майстер, перезавантажили, підняли Keepalived - приходять адвертисменти з більшим пріоритетом, ніж у бекапу, і бекап автоматично перетворюється назад, знімає з себе IP-адреси, ніяких ручних дій при цьому робити не треба.

Як Badoo досяг можливості віддавати 200k фото в секунду

Таким чином, відмовостійкість зовнішньої IP-адреси ми забезпечили. Наступна частина — із зовнішньої IP-адреси якось балансувати трафік на фото-роутери, які вже термінують його. З протоколами балансування все зрозуміло. Це або простий round-robin, або трохи складніші речі, wrr, list connection і таке інше. Це, в принципі, описано в документації, нічого такого особливого немає. А ось метод доставки… Тут зупинимося докладніше – чому вибрали один із них. Це NAT, Direct Routing та TUN. Справа в тому, що ми одразу закладалися на віддачу 100 гігабіт трафіку з майданчиків. Це якщо прикинути, чи потрібно 10 гігабітних карток, правильно? 10 гігабітних карток в одному сервері — це вже виходить за рамки, принаймні нашого поняття «стандартне обладнання». І тут ми згадали, що ми не просто віддаємо якийсь трафік, ми віддаємо фотографії.

У чому особливість? - Колосальна різниця між вхідним і вихідним трафіком. Вхідний трафік дуже маленький, що виходить дуже великий:

Як Badoo досяг можливості віддавати 200k фото в секунду

Якщо подивитися на ці графіки, то видно, що зараз на директора надходить близько 200 Мб в секунду, це звичайнісінький день. Віддаємо ж ми назад 4,500 Мб на секунду, співвідношення у нас приблизно 1/22. Вже зрозуміло, що для повного забезпечення вихідного трафіку на 22 сервери робочих достатньо одного, який приймає це з'єднання. Тут нам на допомогу приходить саме алгоритм direct routing, алгоритм маршрутизації.

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

Що особливо приємно — таке рішення не має на увазі кардинальної ситуації локальної мережі, для нас це було важливо, нам треба було вирішити це мінімальними витратами. Якщо подивитися на виведення команди IPVS admin, то ми побачимо, як це виглядає. Ось ми маємо якийсь віртуальний сервер, на 443 порту, слухає, приймає з'єднання, йде перерахування всіх робочих серверів, і видно, що connection — він, плюс-мінус, однаковий. Якщо ми подивимося статистику, на тому ж віртуальному сервері, у нас є вхідні пакети, вхідні з'єднання, але відсутні вихідні. Вихідні з'єднання йдуть безпосередньо клієнту. Добре, що розбалансувати ми змогли. Тепер, що буде, якщо у нас один із фото-роутерів йде у відмову? Адже залізо — це залізо. Може піти в kernel panic, може зламатися, може згоріти блок живлення. Що завгодно. Для цього і потрібні health-check'і. Вони можуть бути як найпростішими — перевірка на те, як порт у нас відкритий, — так і якась складніша, аж до якихось самописних скриптів, які навіть бізнес-логіку перевірятимуть.

Ми зупинилися десь посередині: у нас йде https-запит на певний location, викликається скрипт, якщо він відповідає 200-м відповіддю, ми вважаємо, що з цим сервером все нормально, що він живий і його цілком спокійно можна вмикати.

Як це, знову ж таки, виглядає на практиці. Вимкнули сервер допустимо на обслуговування – перепрошивка BIOS, наприклад. У логах у нас тут же відбувається тайм, бачимо перший рядок, потім після трьох спроб позначається як «зафейлен», і він видаляється просто зі списку.

Як Badoo досяг можливості віддавати 200k фото в секунду

Можливий ще другий варіант поведінки, коли просто VS виставляється у нуль, але у разі віддачі фотографії це працює погано. Сервер піднімається, запускається там Nginx, тут же health-check'і розуміють, що коннект проходить, що все відмінно, і сервер з'являється у нас у списку, і на нього відразу починає подаватися навантаження. Жодних при цьому ручних дій від чергового адміну не потрібно. Вночі сервер перезавантажився — відділ моніторингу нам із цього приводу вночі не дзвонить. Повідомляють, що таке було, все нормально.

Отже, досить простим способом, за допомогою невеликої кількості сервером ми проблему зовнішньої стійкості до відмови вирішили.

Залишилося сказати, що все це, звичайно, потрібно моніторити. Окремо слід зазначити, що Keepalivede, як дуже давно написаний софт, має купу способів його замоніторити як за допомогою перевірок через DBus, SMTP, SNMP, так і стандартним Zabbix'ом. Плюс він сам по собі вміє писати листи практично на кожен чих, і чесно кажучи, ми в якийсь момент думали навіть вимкнути, тому що пише він дуже багато листів на будь-яке перемикання трафіку, включення, на кожен IP-шник і так далі . Звісно, ​​якщо серверів багато, можна цими листами себе завалити. Стандартними способами моніторимо nginx на фото-роутерах, і нікуди не подівся моніторинг заліза. Ми б, звичайно, радили ще дві речі: це по-перше, зовнішні health-check'и доступності, тому що навіть якщо все працює, насправді, можливо, користувачі фотографії не отримують через проблеми із зовнішніми провайдерами чи чогось. то складнішого. Завжди варто тримати десь в іншій мережі, в amazon або ще десь окрему машину, яка зможе зовні пінгувати ваші сервери, і також варто використовувати або anomaly detection, для тих, хто вміє в хитрий machine learning, або простий моніторинг, хоча б у тому, щоб відстежувати, якщо реквести різко впали, чи навпаки, зросли. Теж буває корисно.

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

Що ми зрештою отримали? Проблема у нас була у січневі свята 2018-го. За перші півроку поки ми вводили цю схему в дію, розширювали вже на весь трафік, щоб весь трафік зняти з LTM, ми виросли тільки по трафіку в одному дата-центрі з 40 до 60 гігабіт, і при цьому за весь 2018-й рік змогли віддавати практично втричі більше фотографій на секунду.

Як Badoo досяг можливості віддавати 200k фото в секунду

Джерело: habr.com

Додати коментар або відгук