Архітектура зберігання та віддачі фотографій у Badoo

Архітектура зберігання та віддачі фотографій у Badoo

Артем Денисов ( bo0rsh201, Badoo)

Badoo - це найбільший у світі сайт знайомств. На даний момент у нас зареєстровано 330 мільйонів користувачів по всьому світу. Але, що набагато важливіше в контексті нашої сьогоднішньої розмови, — це те, що ми зберігаємо близько 3 петабайт фотографій користувача. Щодня наші користувачі заливають близько 3,5 мільйонів нових фотографій, і навантаження на читання складає близько 80 тисяч запитів за секунду. Це досить багато для нашого бекенда, і з цим іноді бувають труднощі.

Архітектура зберігання та віддачі фотографій у Badoo

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

А тепер давайте почнемо.


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

Архітектура зберігання та віддачі фотографій у Badoo

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

  • сучасний хмарний storage,
  • коробкове рішення, яких зараз теж дуже багато;
  • можемо засетапити кілька машин у своєму дата-центрі і поставити на них великі жорсткі диски та зберігати фотографії там.

Badoo історично — і зараз, і тоді (на той час, коли це тільки зароджувалося) живе на власних серверах, усередині наших власних ДЦ. Тому для нас цей варіант був оптимальним.

Архітектура зберігання та віддачі фотографій у Badoo

Ми просто взяли кілька машин, назвали їх "photos", у нас вийшов такий кластер, який зберігає фотографії. Але, здається, чогось не вистачає. Для того щоб все це працювало, потрібно якимось чином визначити, на якій машині, які фотографії ми будемо зберігати. І тут також не треба відкривати Америку.

Архітектура зберігання та віддачі фотографій у Badoo

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

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

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

Так воно якийсь час було в нас.

Архітектура зберігання та віддачі фотографій у Badoo

Це було десь у 2009 році. Доставляли машини, доставляли...

І в якийсь момент ми почали помічати, що ця схема має певні недоліки. Які вади?

Насамперед, це обмежена місткість. Ми на один фізичний сервер можемо запхати не так багато жорстких дисків, як нам хотілося б. І це з часом і зі зростанням dataset'а стало певною проблемою.

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

Це все було на 2009 рік, але, в принципі, ці вимоги є актуальними і на сьогодні. У нас ретроспектива, тож у 2009 році все було погано із цим зовсім.

І останній пункт – це ціна.

Архітектура зберігання та віддачі фотографій у Badoo

Ціна тоді була дуже кусаючою, і нам потрібно було шукати якісь альтернативи. Тобто. нам потрібно було якось краще утилізувати і місце у дата-центрах, і безпосередньо фізичні сервери, на яких усе це розміщено. І наші системні інженери розпочали велике дослідження, в якому переглянули купу різних варіантів. Вони дивилися і на кластерні файлові системи, такі як PolyCeph і Lustre. Там були проблеми з продуктивністю та досить важка експлуатація. Відмовилися. Пробували монтувати весь dataset по NFS на кожну тачку, щоб у такий спосіб якось масштабувати. Читання теж погано зайшло, пробували різні рішення від різних вендорів.

І зрештою ми зупинилися на тому, що ми почали використовувати так зване Storage Area Network.

Архітектура зберігання та віддачі фотографій у Badoo

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

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

Другий плюс.

Архітектура зберігання та віддачі фотографій у Badoo

Це те, що місткість стала набагато більшою, тобто. ми в набагато меншому обсязі можемо розмістити набагато більше storage'а.

Але були й мінуси, які виявились досить швидко. Зі зростанням числа користувачів та навантаження на цю систему почали виникати проблеми з продуктивністю. І проблема тут досить очевидна - будь-яка SHD, призначена для того, щоб зберігати багато фотографій у маленькому обсязі, як правило, страждає від інтенсивного читання. Це насправді актуально і для будь-якого хмарного storage'а, і будь-чого. Зараз у нас не існує ідеального storage'а, який був би нескінченно масштабований, в нього можна було б усе, що завгодно, запихати, і він дуже добре переносив би читання. Особливо випадкові читання.

Архітектура зберігання та віддачі фотографій у Badoo

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

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

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

Архітектура зберігання та віддачі фотографій у Badoo

І тут все грає нам на руку.

Я в першому слайді вже казав: у нас 80 тисяч запитів на секунду на читання за всього 3,5 мільйонів аплоадів на день. Тобто це різниця на три порядки. Очевидно, що треба оптимізувати читання і практично зрозуміло як.

Є ще один короткий момент. Специфіка сервісу така, що людина реєструється, заливає фотографію, далі починає активно дивитися інших людей, лайкати їх, її активно показують іншим людям. Потім він знаходить собі пару або не знаходить пару, це вже як вийде, і на якийсь час перестає користуватися сервісом. У цей момент, коли він користується, його фотки дуже гарячі – вони потрібні, їх переглядає дуже багато людей. Як тільки він перестає це робити, досить швидко він випадає з таких інтенсивних показів іншим людям, як раніше, і його фотки практично не запитуються.

Архітектура зберігання та віддачі фотографій у Badoo

Тобто. у нас дуже маленький гарячий dataset. Але при цьому за ним дуже багато запитів. І цілком очевидним рішенням тут напрошується додати кеш.

Кеш із LRU всі проблеми наші вирішить. Що ми робимо?

Архітектура зберігання та віддачі фотографій у Badoo

Ми додаємо перед нашим великим кластером зі storage'ом ще один порівняно невеликий, який називається фотокеші (photoscache). Це, по суті, просто кешуючий proxy.

Як це працює зсередини? Ось наш користувач, ось storage. Все як раніше. Що ми додаємо між ними?

Архітектура зберігання та віддачі фотографій у Badoo

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

Як це виглядає? Користувач надсилає запит за фоткою. NGINX шукає її спочатку у локальному кеші. Якщо ні, робить просто proxy_pass на наш storage, завантажує фотографію звідти і дає її користувачеві.

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

Архітектура зберігання та віддачі фотографій у Badoo

Кеш логічно розділений на три шари. Коли я говорю «три шари», це не означає, що там якась складна система. Ні, це умовно просто три директорії у файловій системі:

  1. Це буфер, куди потрапляють щойно завантажені з proxy фотографії.
  2. Це гарячий кеш, в якому зберігаються фотографії, що активно запитуються зараз.
  3. І холодний кеш, куди поступово фотографії виштовхуються із гарячого, коли до них приходить менше request'ів.

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

Архітектура зберігання та віддачі фотографій у Badoo

Nginx просто на кожен запит пише на RAMDisk access.log, в якому вказує шлях до фотки, яку він зараз обслужив (відносний шлях, природно), і те, яким розділом вона обслуговувалась. Тобто. там може бути написано "photo 1" і далі або буфер, або гарячий кеш, або холодний кеш, або проксі.

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

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

Архітектура зберігання та віддачі фотографій у Badoo

Він просто збирає там, веде лічильники та періодично робить наступне. Активно запитувані фотографії, за якими приходить багато request'ів, він рухає в гарячий кеш, де б вони не лежали.

Архітектура зберігання та віддачі фотографій у Badoo

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

Архітектура зберігання та віддачі фотографій у Badoo

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

Для того, щоб фотка зберігалася відразу ж при проксуванні в буфер, ми використовуємо директиву proxy_store і буфер - це також RAMDisk, тобто. для користувача це працює дуже швидко. Це щодо нутрощів самого кешируючого сервера.

Залишилося питання про те, як розподіляти request'и по цих серверах.

Припустимо, є кластер з двадцяти storage-машин і три сервери, що кешують (так вийшло).

Архітектура зберігання та віддачі фотографій у Badoo

Нам потрібно якимось чином визначити, які request'и за якими фотками та куди приземлити.

Найбанальніший варіант – це Round Robin. Чи випадково це робити?

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

Нам потрібно якимось чином однозначно визначати, на який сервер приземляти якийсь request.

Є банальний спосіб. Ми беремо хеш від URL або хеш від нашого ключа шардингу, який є в URL, і ділимо його націло на кількість серверів. Буде працювати? Буде.

Архітектура зберігання та віддачі фотографій у Badoo

Тобто. у нас стовідсотковий request, наприклад, за якимось example_url завжди буде приземлятися на сервер з індексом «2», і кеш буде постійно утилізований якнайкраще.

Але виникає проблема з вирішардингом у такій схемі. Решардинг – я маю на увазі зміну кількості серверів.

Припустимо, що наш кластер, що кеширує, перестав справлятися, і ми вирішили додати ще одну машину.

Додаємо.

Архітектура зберігання та віддачі фотографій у Badoo

У нас тепер все ділиться не на три, а на чотири. Таким чином, практично всі ключі, які у нас раніше були, практично всі URL тепер живуть на інших серверах. Весь кеш інвалідів просто моментом. Всі запити повалили на наш кластер-storage, йому стало погано, відмова обслуговування та незадоволені користувачі. Так не хочеться робити.

Цей варіант нам також не підходить.

Т.о. що ми повинні зробити? Ми повинні якимось чином ефективно використовувати кеш, постійно приземляти один request на той самий сервер, але при цьому бути стійкими до вирішардингу. І таке рішення є, воно не те щоб складне. Називається consistent hashing.

Архітектура зберігання та віддачі фотографій у Badoo

Як це виглядає?

Архітектура зберігання та віддачі фотографій у Badoo

Ми беремо якусь функцію від шардинг-ключа та розмазуємо всі її значення на колі. Тобто. у точці 0 у нас сходяться її мінімальні та максимальні значення. Далі ми на цьому ж колі розміщуємо всі наші сервери приблизно таким чином:

Архітектура зберігання та віддачі фотографій у Badoo

Кожен сервер визначається однією точкою, і той сектор, що йде до нього за годинниковою стрілкою, відповідно обслуговується цим хостом. Коли нам приходять запити, ми відразу ж бачимо, що, наприклад, запит А – у нього там хеш такий – і він обслуговується сервером 2. Запит Б – сервером 3. І так далі.

Архітектура зберігання та віддачі фотографій у Badoo

Що в цій ситуації відбувається при вирішенні рдингу?

Архітектура зберігання та віддачі фотографій у Badoo

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

Архітектура зберігання та віддачі фотографій у Badoo

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

Єдине питання залишається при відмовах. Припустимо, що в нас якась тачка вийшла з ладу.

Архітектура зберігання та віддачі фотографій у Badoo

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

Це щодо системи кешування. Погляньмо на результати.

Здавалося б, нічого складного тут нема. Але такий спосіб управління кешом дав нам хітрейт близько 98%. Тобто. з ось цих 80 тисяч request'ів за секунду тільки 1600 доходить до storage'ів, і це абсолютно нормальне навантаження, вони спокійно це переживають, у нас завжди є запас.

Ми розмістили ці сервери в трьох наших DC і отримали три точки присутності — Прага, Майамі та Гонконг.

Архітектура зберігання та віддачі фотографій у Badoo

Т.о. вони більш-менш локально розташовані до кожного з цільових ринків.

І як приємний бонус ми отримали ось цей кешуючий proxy, на якому CPU насправді простоює, тому що для віддачі контенту він не так сильно потрібен. І там за допомогою NGINX+ Lua ми реалізували дуже багато утилітарної логіки.

Архітектура зберігання та віддачі фотографій у Badoo

Наприклад, можемо експериментувати з webp або progressive jpeg (це ефективні сучасні формати), дивитися, як це впливає на трафік, приймати якісь рішення, включати для певних країн і т.д.; робити динамічний resize або crop фотографії на льоту.

Це хороший usecase, коли у вас, наприклад, є мобільний додаток, який показує фотки, і мобільний додаток не хоче витрачати CPU клієнта на те, щоб запросити велику фотографію і ресайзувати її потім до якогось розміру, щоб запхати на хмиз. Ми можемо просто динамічно вказати в URL якісь параметри в UPort умовний, і фотокеш сам відресайзить фотографію. Як правило, він підбере той розмір, який у нас фізично є на диску, максимально близький до запитуваного, і задавнкеллить його в конкретних координатах.

До речі, ми виклали у відкритий доступ відеозаписи останніх п'яти років конференції розробників високонавантажених систем HighLoad ++. Дивіться, вивчайте, діліться та підписуйтесь на канал YouTube.

Також ми можемо додавати туди багато продуктової логіки. Наприклад, ми можемо за параметрами URL'а додавати різні watermark'и, можемо блюрити фотографії, розмивати або пікселювати. Це коли ми хочемо показати фотографію людини, але не хочемо показувати її обличчя, це добре працює, це все реалізовано тут.

Що ми здобули? Ми отримали три точки присутності, добрий хітрейт, і при цьому у нас не простоює CPU на цих машинах. Він тепер став, звичайно, важливішим, ніж раніше. Нам потрібно ставити машини собі сильніше, але це того варте.

Це щодо віддачі фотографій. Тут усе досить зрозуміло та очевидно. Я думаю, що я не відкрив Америку, тому працює практично будь-який СDN.

І, швидше за все, у досвідченого слухача могло виникнути питання: а чому просто не взяти і не поміняти все на СDN? Було б приблизно те саме, всі сучасні СDN це вміють. І тут низка причин.

Перша – це фотки.

Архітектура зберігання та віддачі фотографій у Badoo

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

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

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

І пункт, що з попереднього –

Архітектура зберігання та віддачі фотографій у Badoo

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

Який висновок напрошується? У нашому випадку CDN це не дуже хороша альтернатива.

Архітектура зберігання та віддачі фотографій у Badoo

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

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

Архітектура зберігання та віддачі фотографій у Badoo

І сучасні СDN мають практично все те, що я вам розповів зараз. За винятком плюс мінус якихось фіч.

Це щодо віддачі фотографій.

Давайте тепер трохи перемістимося вперед у нашій ретроспективі і поговоримо про зберігання.

2013 йшов.

Архітектура зберігання та віддачі фотографій у Badoo

Кешируючі сервери додалися, проблеми з performance'ом пішли. Все добре. Dataset зростає. На 2013 рік у нас було близько 80 серверів, які підключені до storage'ів, та близько 40 кешуючих у кожному ДЦ. Це з 560 терабайт даних кожному ДЦ, тобто. близько петабайту у сумі.

Архітектура зберігання та віддачі фотографій у Badoo

І зі зростанням dataset'а почали сильно зростати експлуатаційні витрати. У чому це виражалося?

Архітектура зберігання та віддачі фотографій у Badoo

У цій схемі, яка намальована — із SAN'ом, із підключеними до нього машинами та кешами — дуже багато точок відмови. Якщо з відмовою серверів, що кешують, ми раніше вже впоралися, там все більш-менш прогнозовано і зрозуміло, то на стороні саме storage'а все було набагато гірше.

По-перше, сам Storage Area Network (SAN), який може відмовити.

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

Архітектура зберігання та віддачі фотографій у Badoo

Їх, звичайно, не так багато, як із самим SAN'ом, але, проте, це теж точки відмови.

Далі сама машина, яка підключена до storage'у. Вона також може вийти з ладу.

Архітектура зберігання та віддачі фотографій у Badoo

У нас три точки відмови.

Далі, крім точок відмови, це важкий maintenance самих storage'ей.

Це складна багатокомпонентна система і системним інженерам буває з нею важко.

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

Архітектура зберігання та віддачі фотографій у Badoo

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

Із цим треба було щось робити. І ми вирішили, що треба просто резервувати дані. Це насправді очевидне рішення та гарне. Що ми зробили?

Архітектура зберігання та віддачі фотографій у Badoo

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

Ми просто додали другий розділ.

Архітектура зберігання та віддачі фотографій у Badoo

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

Тут ми просто робимо поряд асинхронну чергу.

Архітектура зберігання та віддачі фотографій у Badoo

Вона не дуже навантажена. Ми знаємо, що мало записів. Черга - це просто табличка в MySQL, в яку пишуться рядки типу "треба забекапити ось цю фотографію". При будь-якій зміні або upload'і ми копіюємо з основного розділу на backup асинхронним або просто якимось background worker'ом.

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

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

І ми додали третій диск, який є маленьким SSD, і назвали його буфером.

Архітектура зберігання та віддачі фотографій у Badoo

Як це тепер працює?

Користувач upload'ит фотку на буфер, далі кидається event у чергу про те, що її треба розкопувати на два розділи. Вона копіюється, і фотографія якийсь час (припустимо, добу) живе на буфері, а потім звідти пуржиться. Це чудово покращує user experience, тому що користувач заливає фотографію, як правило, за нею відразу ж починають йти request'и, або він сам оновив сторінку, зарефреш. Але все це залежить від програми, яка робить upload.

Або, наприклад, інші люди, яким він почав показуватися, відразу ж за цією фоткою посилають request'и. У кеші ще немає, перший запит відбувається дуже швидко. По суті, так само, як із фотокешу. Повільний storage не бере участь взагалі у це. А коли через добу вона буде спуржена, вона вже або закешована на нашому шарі, що кеширує, або вона вже, швидше за все, нікому не потрібна. Тобто. user experience тут дуже добряче підріс за рахунок таких простих маніпуляцій.

Ну і найголовніше: ми перестали втрачати дані.

Архітектура зберігання та віддачі фотографій у Badoo

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

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

Архітектура зберігання та віддачі фотографій у Badoo

По-друге, залишилися проблеми з SAN'ами, залишився їх важкий maintenance і т.д. Це не те, щоб було критичним фактором, але хотілося спробувати якось без цього пожити.

І ми зробили третю версію (по суті другу насправді) — версію резервування. Як це виглядало?

Це те, що було –

Архітектура зберігання та віддачі фотографій у Badoo

Основні проблеми у нас із тим, що це фізичний хост.

Ми, по-перше, прибираємо SAN'и, тому що хочемо поекспериментувати, хочемо спробувати просто локальні жорсткі диски.

Архітектура зберігання та віддачі фотографій у Badoo

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

І далі ми просто беремо наш backup розділ і переносимо його фізично на окрему машину.

Архітектура зберігання та віддачі фотографій у Badoo

Таким чином, ми отримуємо таку схему. У нас є дві тачки, які зберігають однакові dataset'и. Вони резервують один одного повністю і синхронізують дані по мережі через асинхронну чергу в тому самому MySQL.

Архітектура зберігання та віддачі фотографій у Badoo

Чому це добре працює, бо у нас мало записів. Тобто. якби запис був порівнянний з читанням, можливо, ми отримали б якийсь мережевий overhead і проблеми. Записи мало, читання багато - цей спосіб працює добре, тобто. ми досить рідко копіюємо фотографії між цими двома серверами.

Як це працює, якщо трохи детальніше подивитися.

Архітектура зберігання та віддачі фотографій у Badoo

Upload. Балансувальник просто вибирає випадкові хости з парою та робить upload на нього. При цьому він природно робить health checks, дивиться, щоб машина не випала. Тобто. він upload'ит фотки тільки на живий сервер, а потім через асинхронну чергу це все копіюється на його сусіда. З upload'ом все дуже просто.

Із завданням трохи складніше.

Архітектура зберігання та віддачі фотографій у Badoo

Тут нам допоміг Lua, тому що на ванільному NGINX таку логіку зробити важко буває. Ми спочатку робимо request на перший сервер, дивимося, чи є фотографія там, бо потенційно вона може бути залита, наприклад, на сусіда, а сюди ще не доїхала. Якщо фото там є, це добре. Ми її одразу ж віддаємо клієнтові та, можливо, кешуємо.

Архітектура зберігання та віддачі фотографій у Badoo

Якщо її немає, ми просто запитуємо сусіда і звідти її гарантовано отримуємо.

Архітектура зберігання та віддачі фотографій у Badoo

Т.о. знову можна сказати: можуть бути проблеми з performance'ом, тому що постійні round trip'и – фотографію залили, тут її немає, ми робимо два запити замість одного, це має повільно працювати.

У нашій ситуації це працює неквапливо.

Архітектура зберігання та віддачі фотографій у Badoo

У нас збирається купа метрик за цією системою, і умовний хітрейт такого механізму складає близько 95%. Тобто. лаг ось цього backup'а маленький, і за рахунок цього ми практично гарантовано, після того, як фотка була завантажена, забираємо її вже з першого разу і нікуди два рази не ходимо.

Так що ми ще отримали, і що дуже круто?

Раніше ми мали основні backup-розділ, і ми з них читали послідовно. Тобто. ми завжди спочатку шукали на основному, а потім на backup'і. То був один хід.

Тепер ми утилізуємо читання з двох машин одразу. Розподіляємо запити Round Robin'ом. У невеликому відсотку випадків ми робимо два запити. Зате загалом у нас тепер у два рази більший запас читання, ніж був раніше. І навантаження прямо здорово знизилося і на машини, що віддають, і безпосередньо на storage'і, які у нас на той момент теж були.

Щодо відмовостійкості. Власне, за це ми й боролися здебільшого. З стійкістю до відмови тут все вийшло шикарно.

Архітектура зберігання та віддачі фотографій у Badoo

Одна тачка виходить із ладу.

Архітектура зберігання та віддачі фотографій у Badoo

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

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

Архітектура зберігання та віддачі фотографій у Badoo

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

Які можна підсумки підбити з цієї схеми з резервуванням?

Набули відмовостійкості.

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

Отримали подвійний запас читання.

Це дуже хороший бонус у плюс до стійкості до відмови.

Але є й проблеми. Тепер у нас набагато складніша розробка якихось фіч, пов'язаних із цим, тому що система стала на 100% eventually consistent.

Архітектура зберігання та віддачі фотографій у Badoo

Ми повинні, скажімо, у якомусь background job'і постійно думати: «А на якому сервері ми зараз запущені?», «А чи точно тут є актуальна фотка?» і т.д. Це, звичайно, все загорнуте в обгортки, і для програміста, який пише бізнес-логіку, це прозоро. Однак це великий складний шар з'явився. Але ми готові з цим миритися в обмін на ті плюшки, які ми отримали.

І тут знову ж таки виникає деякий конфлікт.

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

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

По-друге, це продуктивніше, тому що у нас немає ось цих автоматичних контролерів, підключення до дискових полиць.

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

Але є і мінуси.

Архітектура зберігання та віддачі фотографій у Badoo

Це приблизно в 1,5 рази дорожче, ніж використовувати SAN навіть за сьогоднішніми цінами. Тому ми вирішили так сміливо не конвертувати весь наш великий кластер у тачки з локальними жорсткими дисками та вирішили залишити гібридне рішення.

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

І у нас з'явився великий запас читання, і ми укрупнили. Якщо раніше ми на одну машину монтували один storage, тепер ми на одну пару монтуємо чотири, наприклад. І це нормально працює.

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

Підсумки

У нас є користувачі – цілих 33 млн.

Ми маємо три точки присутності — Прагу, Майямі, Гонконг.

У них розташований кешуючий шар, який є тачками зі швидкими локальними дисками (SSD), на яких працює простенька машинерія з NGINX, його access.log'а і демони на Python'і, які все це обробляють і менеджують кеш.

За бажання ви у своєму проекті, якщо для вас фотки не такі критичні, як для нас, або якщо trade-off контроль проти швидкості розробки та витрат ресурсів для вас в інший бік, тоді ви можете спокійно замінити його CDN'ом, сучасні CDN це добре роблять.

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

При цьому частина цих машин працює із локальними жорсткими дисками.

Частина цих машин підключено до SAN'ам.

Архітектура зберігання та віддачі фотографій у Badoo

І, з одного боку, це зручніше в експлуатації і трохи продуктивніше, з іншого боку, це зручно з точки зору щільності розміщення та ціни за гігабайт.

Це такий стислий огляд архітектури того, що ми отримали і як це все розвивалося.

Ще кілька порад від кепа, зовсім простих.

По-перше, якщо ви раптом вирішите, що вам терміново потрібно все покращити у вашій інфраструктурі фоток, спочатку поміряйте, тому що можливо нічого не треба покращувати.

Архітектура зберігання та віддачі фотографій у Badoo

Наведу приклад. У нас є кластер машин, який віддає фотографії з attachment'a у чатах, і ось там досі працює схема з 2009 року, і ніхто від цього не страждає. Всім добре, всім подобається.

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

Він дозволяє збирати стадо з NGINX дуже докладну на кожен request і коди відповідей, і розподіл часів - все, що завгодно. У нього є біндінги в різні системи побудови аналітики, і ви можете потім все це красиво дивитися.

Спочатку поміряли – потім покращили.

Далі. Читання ми оптимізуємо кешем, запис шардингом, але це очевидний пункт.

Архітектура зберігання та віддачі фотографій у Badoo

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

Архітектура зберігання та віддачі фотографій у Badoo

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

Наступний пункт. Про resize на льоту.

Раніше ми, коли користувачі upload'или фотографію, нарізали відразу цілу купу розмірів на всі випадки життя, під різних клієнтів, і вони лежали на диску. Тепер ми відмовилися від цього.

Ми залишили лише три основні розміри: маленький, середній та великий. Все інше ми просто даунськейлі з розміру, який стоїть за тим, який нам запитали в Uport, просто робимо даунскейл і віддаємо користувачеві.

CPU кешируючого шару тут виходить набагато дешевше, ніж якби ми постійно перегенерували ці розміри на кожному storage'і. Припустимо, ми хочемо додати новий, це справа на місяць - прогнати скріпт скріпт, який би все це акуратно зробив, при цьому не поклав кластер. Тобто. якщо є можливість зараз вибирати, краще робити якнайменше фізичних розмірів, але щоб хоч якийсь розподіл був, скажімо, три. І все інше просто ресайзувати на льоту за допомогою готових модулів. Це зараз все дуже легко та доступно.

А інкрементальний асинхронний backup – це добре.

Як показала наша практика, ось така схема працює з відкладеним копіюванням змінених файлів.

Архітектура зберігання та віддачі фотографій у Badoo

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

Контакти

» bo0rsh201
» Блог компанії Badoo

Ця доповідь — розшифровка одного з найкращих виступів на конференції розробників високонавантажених систем HighLoad ++. До конференції HighLoad++ 2017 залишилося менше місяця.

У нас уже готова Програма конференції, зараз активно формується розклад.

Цього року продовжуємо досліджувати тему архітектур та масштабування:

Також деякі з цих матеріалів використовуються нами в навчальному онлайн-курсі з розробки високонавантажених систем HighLoad.Guide - Це ланцюжок спеціально підібраних листів, статей, матеріалів, відео. Вже зараз у нашому підручнику понад 30 унікальних матеріалів. Підключайтесь!

Джерело: habr.com

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