Основний принцип мікросервісної архітектури – коли маємо незалежний функціонал чи певну незалежну бізнес-логіку, їх можна виділити в окремий мікросервіс. Водночас є кілька застережень: не варто (1) дробити все на занадто малі частини, (2) змішувати різні компоненти системи чи бізнес-функціоналу та (3) використовувати універсальний підхід до всіх мікросервісів.
А ось що варто, так це дати можливість мікросервісам масштабуватися. Бажано автоматично та без ретельного нагляду розробників.
Про те, як це роблять у FAVBET Tech, розповідає в партнерському матеріалі Head of Engineering Максим Ільїн.
Підходи до створення мікросервісів у FAVBET Tech
Максим Ільїн, Head of Engineering
Частина наших мікросервісів переписана з моноліту, частина – створена одразу окремими сервісами (коли ми говоримо про повністю новий функціонал). В обох випадках, перш ніж стати до роботи, ми оцінювали, чи може цей функціонал існувати як окрема самодостатня служба. Якщо відповідь була «так», робили мікросервіс.
Усі мікросервіси взаємодіють з іншими частинами продукту, до них можуть звертатися із запитами для отримання потрібної інформації, як-от фінансових даних користувачів. Через це виникає декілька правил створення мікросервісів:
- Не дробити функціонал на занадто малі частини. Наприклад, немає сенсу створювати окремі мікросервіси для функцій, які завжди виконуються разом у межах одного бізнес-процесу.
- Не змішувати різні компоненти. Наприклад, дані користувачів (які зазвичай містять чутливу інформацію) з репортинговою системою (яка має інші вимоги до зберігання та обробки даних).
- Вибирати архітектуру окремого мікросервісу залежно від завдань, які він виконуватиме.
Останнє розглянемо детальніше.
Взаємодія між мікросервісами може погано впливати на продуктивність системи, особливо під високим навантаженням. Цей вплив, так само як і витрати ресурсів, варто мінімізувати. Наприклад, правильно вибрати протоколи обміну даними між мікросервісами.
До івент-систем (сервіс історії дій користувача) зазвичай звертаються через асинхронний протокол: у FAVBET Tech це RabbitMQ. Більшість мікросервісів надсилають інформацію, повідомляючи про певну подію в системі. Цей мікросервіс бере її до уваги, зберігає або реагує за потреби.
Таким чином асинхронні протоколи дозволяють сервісам продовжувати роботу, не блокуючись на етапі надсилання повідомлення. Іншими словами, сервіси не чекають відповіді від мікросервісу, що забезпечує ефективнішу роботу системи.
Водночас є випадки, коли потрібна синхронна відповідь. Наприклад, один мікросервіс звертається до іншого із запитом і має одразу отримати результат.
КЕЙС
Одного разу ми розробляли нове сховище сесій для користувачів. Щоб кожному користувачу, який заходить на сайт (як анонімному, так й авторизованому), присвоювалась сесія – унікальний ідентифікатор. Завдяки цьому ми могли розпізнати, з якого пристрою або країни він заходив, і ця інформація зберігалася в системі.
Під час розробки мікросервісу ми невдало вибрали механізм синхронізації та обміну станом між його інстансами (процесами). Через це виникало дублювання та велика затримка при старті додаткового інстансу. Підхід треба було змінити. Основною проблемою стало те, що реальне навантаження виявилося значно більшим за наші розрахунки.
Процес перероблення був тривалим – це зайняло десь 2–3 місяці. Основним викликом було впровадити необхідні зміни, але зберегти протокол взаємодії між іншими сервісами. Після доопрацювань система почала функціонувати стабільно.
Як масштабувати мікросервіс: найпростіша робоча схема
Ми намагаємося проєктувати архітектуру так, щоб навіть через 10 років можна було змінити конфігурацію мікросервісу та забезпечити підтримку ще більшого навантаження.
Найпростіший спосіб масштабувати сервіс – додати необхідну кількість інстансів (копій мікросервісу), яких було б достатньо для поточного навантаження.
Кінцевий сервіс має бути архітектурно підготовлений до обробки великого потоку даних незалежно від того, чи це запити від зовнішніх користувачів, чи запит від іншого внутрішнього мікросервісу. Такі прості підходи до архітектури дозволяють масштабуватися та бути готовим до будь-яких навантажень.
Крім того, при розробці на Erlang є багато внутрішніх механізмів для масштабування. Один з них, найпростіший, це додавання кількості процесів для обробки певного функціоналу.
Як Kubernetes і Docker впливає на роботу мікросервісів
Ізоляція ресурсів для мікросервісів
Одна з основних переваг Docker – можливість ізолювати мікросервіси за ресурсами. Якщо ми розглянемо фізичний сервер, то на ньому можна запустити кілька мікросервісів. Проте іноді виникають ситуації, коли один з них працює некоректно, отримує надмірне навантаження чи має помилки. У такому разі він може споживати всі ресурси сервера, що вплине на інші мікросервіси, які розгорнуті на ньому.
У Docker це неможливо, оскільки кожен мікросервіс запускається в окремому контейнері з ізольованими ресурсами. Таким чином, вони не впливають один на одного. Якщо виникне проблема з одним мікросервісом або контейнером, у якому він запущений, то впаде лише цей контейнер. Kubernetes своєю чергою намагатиметься перезапустити його.
Автоскейлінг
Ще однією перевагою є автоскейлінг (автоматичне масштабування). Наприклад, можна налаштувати систему так, щоб при збільшенні навантаження вона додавала додаткові інстанси та розподіляла навантаження між ними. І навпаки, якщо Kubernetes бачить, що запитів менше, то система може вимикати зайві процеси, залишаючи стільки, скільки необхідно для роботи.
Деплой і релізи
Третій важливий аспект – деплой і релізи. Це значно спрощує життя як для DevOps-інженерів, так і для розробників. Завдяки Docker і Kubernetes можна швидко розгортати й оновлювати застосунки. У разі проблем з новою версією можна виконати відкат (rollback) до попередньої стабільної версії.
Для DevOps-інженерів це також зменшує обсяг роботи. Наприклад, їм не потрібно багато часу приділяти релізам або слідкувати за віртуальними машинами. Kubernetes дозволяє під час оновлення версії направляти трафік на старі інстанси, поки нова версія запускається і проходить перевірку. Як тільки новий сервіс готовий до роботи, трафік починає перенаправлятися на нього.
Робота з навантаженнями на продакшені
Після створення MVP сервісу ми проводимо перформанс-тести, щоб оцінити його продуктивність. У тестовому середовищі, де є певні обмеження за ресурсами, ми перевіряємо, яке навантаження сервіс здатен витримати. Якщо бачимо, що у продакшені буде більше навантаження, додаємо ресурси й оцінюємо, як це впливає на можливості сервісу.
Вносити зміни в готові мікросервіси потрібно не дуже часто. Це зазвичай відбувається, якщо зростає навантаження на систему через наплив клієнтів. Наприклад, коли закривалися конкуренти, команда FAVBET Tech стикнулася зі значним приростом клієнтів, що призвело до збільшення навантаження. Завдяки метрикам ми бачили підвищене використання ресурсів і ситуації з вузькими місцями у функціоналі.
Спочатку намагалися збільшити ресурси і стежили, як сервіс поводиться під навантаженням. Якщо цього було недостатньо, ми проводили масштабування, додаючи нові інстанси; також інколи робили оптимізації самого мікросервісу.
Але такий процес є рідкісним. У звичайних умовах приріст клієнтів є більш поступовим, тому такі зміни можуть відбуватися раз на пів року або раз на рік. До того ж наші DevOps-інженери постійно моніторять ситуацію та навіть без залучення розробників можуть самостійно додавати ресурси, щоб система працювала коректно під більшим навантаженням.
Важливість метрик і логування
Логування – це важливий інструмент, проте він скоріше допоміжний, а не основний. Зазвичай ми намагаємося логувати тільки ті події, які пов’язані з помилками в мікросервісах. Бо логування споживає ресурси, і, якщо логувати все підряд, це буде дорого та не дуже інформативно.
Втім, бувають ситуації, коли система працює нормально, помилок немає, але водночас щось функціонує некоректно. У таких випадках ми можемо вмикати дебаг-режим на короткий проміжок часу. У цьому режимі логуються всі події.
Натомість метрики відстежуються постійно. Для цього використовуються три основні системи:
- Prometheus. Серверна частина, яка зберігає та агрегує метрики.
- Grafana. Виступає графічним інтерфейсом.
Prometheus і Graphana (у зв’язці) ми використовуємо здебільшого для перегляду агрегованих даних у динаміці, наприклад, за останній тиждень чи годину. Особливо важливими є перевірки, що відбуваються під час дій користувачів, наприклад, коли вони роблять ставки. Подібні метрики інтегровані в кожен мікросервіс. Коли навантаження на систему зростає, вони зростають лінійно. Це допомагає виявляти вузькі місця (bottlenecks).
Також за допомогою метрик ми аналізуємо вплив релізів нового функціоналу. Наприклад, порівнюємо показники за тиждень до релізу з тими, що є після нього, щоб оцінити, чи покращилася або погіршилася ситуація.
- Власна система метрик, розроблена всередині компанії. Використовується для моніторингу метрик у реальному часі. Тобто ця система показує поточний стан системи та відображає деякі специфічні метрики, пов’язані саме з віртуальною машиною Erlang.
Кількість метрик, які ми відстежуємо, залежить від мікросервісу та його функціоналу. Для найпростішого мікросервісу це може бути 10–15 метрик, тоді як для складнішого – до 50.
Приклади метрик:
- Час виконання запитів між мікросервісами.
- Час виконання запитів до бази даних.
- Показники використання ресурсів, таких як ОЗУ та CPU.
- Кількість відправлених подій у різні сервіси: ми записуємо кількість відправлених повідомлень, час виконання, отримані відповіді та час їх обробки.
- Час виконання валідацій: фінансових, ігрових подій.
- Різні показники кількості подій від зовнішніх користувачів.
Також у нас налаштовані автоматизовані алерти на деякі метрики. Наприклад, занадто низька кількість авторизованих користувачів або якийсь мікросервіс споживає дуже багато оперативної пам’яті, нестандартні помилки при роботі із зовнішніми мікросервісами – тоді надходить алерт у месенджер або на пошту.
Як у FAVBET Tech працюють із проблемами
У першу чергу – наприклад, якщо бачимо надмірне споживання пам’яті – ми виконуємо рестарт, щоб звільнити ресурси. Далі починаємо аналізувати проблему: дивимося графіки різних мікросервісів, перевіряємо, чи були релізи або оновлення за останній час, коли виникла проблема, чи як давно вони були. Якщо проблема з’явилася після релізу, ми аналізуємо, які зміни могли спричинити некоректну роботу.
Зазвичай нові проблеми розв’язуються доволі швидко: протягом кількох годин. Але є й такі, що повторюються та виникають час від часу. Ми вже знаємо їхню типову поведінку за метриками і враховуємо це в роботі, хоча їх вирішення може зайняти певний час.
Деякі проблеми можуть розв’язуватися тиждень-два. Але поки це триває, ми використовуємо тимчасові рішення. Наприклад, повертаємося на попередню, стабільнішу, версію. Якщо це не допомагає і проблема виникає через зростання навантаження, додаємо нові інстанси або збільшуємо ресурси на серверах. Це дозволяє підтримувати стабільність системи, поки триває пошук для остаточного розв’язання проблеми.
Візуал: редакція highload.tech
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: