Мікро, міні та макросервіси. Що за чим стоїть та що обрати для проєкту

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

«Making decisions in system design is all about trade-offs...»
― Sam Newman, Building Microservices: Designing Fine-Grained Systems

Всім привіт! Я Артем Палагно — Tech Lead у компанії SoftServe. І майже всю свою кар’єру, за винятком 2-3 місяців, я працюю з мікросервісами у різному їхньому прояві — міграція з моноліту, проєктування та побудова з нуля, а також робота з вже існуючими. Досвіду в темі маю достатньо, але нещодавно дуже здивувався, коли вперше почув про концепцію мінісервісів. Тому вирішив розібратися і поділитися своїми висновками та порадами. Вони будуть корисні тим розробникам, хто прагне структурувати свої знання, а також тим, хто вперше ознайомлюється з мінісервісами.

Вступ: поява мінісервісів

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

Тому як третя альтернатива з’являється підхід з використанням мінісервісів.

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

Розбираємося в типах сервісів

Моноліт (макросервіс)

Переваги:

  • Простота. Монолітні архітектури прості в створенні, тестуванні та розгортанні. Здебільшого масштабується вертикально (шляхом збільшення кількості RAM та CPU), адже використання горизонтального масштабування не є доцільним.
  • Наскрізні проблеми (Cross-cutting concerns): за допомогою єдиної кодової бази монолітні програми можуть легко впоратися з наскрізними проблемами (Cross-cutting concerns), такими як логування, керування конфігурацією та моніторинг продуктивності.
  • Ефективність. Компоненти в моноліті зазвичай спільно використовують пам’ять (RAM), тому і взаємодія між ними є швидшою, ніж зв’язок між сервісами за допомогою IPC[1] або інших механізмів.

Недоліки:

  • Надійність. Помилка в будь-якому з модулів програми може призвести до зупинки всього додатка.
  • Оновлення. Через єдину велику кодову базу та тісне зв’язування, для кожного оновлення потрібно буде розгортати весь додаток.
  • Стек технологій. Монолітний додаток повинен використовувати один і той же технологічний стек. Зміни в технологічному стеку коштують дорого, як з точки зору часу, так і витрат.

Мікросервіс

Переваги:

  • Можливість експериментувати з різними технологіями: між модулями є менші технологічні залежності. Відкат до попередніх ітерацій менш складний.
  • Мале зв’язування (Low coupling): компоненти мікросервісів слабо пов’язані, тому вони не є взаємозалежними та можуть бути перевірені окремо. Це також робить додаток більш адаптованим до змін з часом.
  • Покращена ізоляція несправностей (fault isolation): великі додатки продовжують функціонувати у випадку збою одного модуля.

Недоліки:

  • Комунікація між сервісами ускладняється: оскільки тепер кожен сервіс є незалежним, тому необхідно звертати увагу на варіанти взаємодій для service-to-service communication[2]. В одному з таких сценаріїв розробники можуть бути змушені писати додатковий код, щоб уникнути збоїв.
  • Тестування та моніторинг: щойно ви розбиваєте програми на незалежні сервіси, у вас буде більше рухомих частин, які потрібно відстежувати та виправляти. Без належних інструментів тестування та моніторингу ситуація може швидко вийти з-під контролю.

Мінісервіси

Переваги:

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

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

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

Недоліки:

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

На відміну від переваг, недоліки у мінісервісах не такі значущі, ніж у мікросервісах.

Порівнюємо міні та мікросервіси

Щоб зрозуміти різницю між мікросервісами та мінісервісами, розглянемо варіанти реалізації одного ТЗ, використовуючи два підходи, та порівняємо їх між собою.

ТЗ: Web-додаток з аутентифікацією, рендерингом pdf та відправкою файлів поштою.

Підхід № 1 (мікросервісний)

Згідно з розділенням відповідальності (Separation of Concerns) проєктуємо мікросервіси з чітко визначеними обов’язками і отримаємо:

1) HTML-renderer service — сервіс рендерингу html.

2) HTML-to-PDF service — сервіс конвертації html to pdf.

3) Mailer service — сервіс поштової відправки.

4) JWT-generator service — сервіс генерації jwt токенів.

5) Authentication service — сервіс аутентифікації.

6) File-proxy service — сервіс взаємодії з файлами.

Підхід № 2 (мінісервісний)

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

1) PDF-renderer service — інкапсулює функціональність HTML-renderer service та HTML-to-PDF.

2) Mailer service — залишається незмінним.

3) Authentication service — інкапсулює функціональність JWT-generator service та Authentication service.

4) File proxy service — залишається незмінним.

Висновок: мікро та міні сервіси залишаються схожими з точки зору взаємодії між собою, або з базою даних, єдина відмінність — це кількість обов’язків, які інкапсулюються (мікросервіс = 1, мінісервіс > 1).

Порівняння у глобальному масштабі

Порівняємо основні характеристики різних підходів:

Macroservice/monolith

Miniservice

Microservice

Code

(варіативність мов програмування)

1/5

3/5

5/5

Deploy

(складність)

5/5

2/5

1/5

Reuse

1/5

3/5

5/5

Development time

5/5

3/5

1/5

Висновок: кожен з підходів має свої переваги та недоліки, але для кожного з підходів є свій головний use-case.

Monolith — маленький додаток (не буде збільшуватись) для швидкого виходу у production.

Miniservice — м’яка міграція (менш затратна) з Monolith.

Microservice — побудова складного/великого додатку з 0 (from scratch).

Що обрати: критерії

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

  1. Чи чітко визначені вимоги додатку?
  2. Чи оцінили ми бізнес-ризики?
  3. Чи має наша команда достатньо експертизи (у мікро/міні/макро)?
  4. Чи маємо ми необхідну інфраструктуру?

Додаткова інформація

  1. IPC (Inter-process communication).
  2. Service-to-service communication.

Сподобалась стаття? Натискай «Подобається» внизу. Це допоможе автору виграти подарунок у програмі #ПишуНаDOU

👍ПодобаєтьсяСподобалось18
До обраногоВ обраному10
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

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

Дякую за статтю та ресурси

ну тобто дали назви «віковічному кейсу», ще до мікросервісного хайпу:

маємо моноліт

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

у статті наведені такі типові модулі:
PDF-renderer service
Mailer service

Це перші кандидати які виносяться з e-commerce моноліту наприклад.

Коли таким сервісам теж треба аутентифікація то виноситься з моноліту і
Authentication service

У чому складність деплою моноліту порівняно з мікросервісом?

Взагалі, моноліт легше деплоїти у порівнянні з мікросервісами, але проблеми можуть бути наступні:
1) High-risk deployment: Deploying an entire solution or application in one go poses a high risk as all modules are going to be deployed even for a single change in one of the modules.
2) Higher testing time: As we have to deploy the complete application, we will have to test the functionality of the entire application. We can’t go live without testing. Due to higher interdependency, the change might cause a problem in some other module.
3) Unplanned downtime: Complete production deployment needs code to be fully tested and hence we need to schedule our production deployment. This is a time-consuming task that results in high downtime. Although planned downtime, during this time, both business and customers will be affected due to the unavailability of the system; this could cause revenue loss to the business.

З мікросервісами така ж лажа буває.

Перші два пункти мабуть більш актуальні для коду, який треба ще компілювати. Для Node.js це не актуально. Не бачу по третьому пункті які переваги мають мікросервіси.

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

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

Я так розумію, що стаття написана на основі вашого досвіду. Тому цікаво було б послухати приклади із реального життя.
Наприклад, «ми вирішили зробити цей проект як мікросервіс/мінісервіс, і це було неправильне рішення, тому ....» Або ми робили все правильно, але дещо не вийшло, тому що ...

і це було неправильне рішення, тому

Хороший вопрос. Вот тут как раз ответ зайдёт в ступор, поскольку никто не признается, что облажались в архитектурных решениях.

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

Дякую за статтю, але є декiлька запитань:

Здебільшого масштабується вертикально (шляхом збільшення кількості RAM та CPU), адже використання горизонтального масштабування не є доцільним.

Цікаво, а чому? Горизонтальне масштабування по-перше, забезпечує High availability, по-друге, знижує навантаження на кожен сервер.

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

Наприклад, яка помилка? Я часто зустрічав помилки, які робили компонент неробочим. Але щось не пам’ятаю помилки, які призводили саме до зупинки додатка.

Переваги:

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

Тут трохи спірне питання. Уявімо ситуацію, що вам потрібно створити три команди розробників (у кожної свій мікросервіс).
І ви замість трьох Java команд набираєте команду на Java, Пітоні та Node.js. Теоретично це працюватиме, але наскільки ефективно вони разом працюватимуть, вирішувати спільні проблеми, створювати best practices?

Мале зв’язування (Low coupling): компоненти мікросервісів слабо пов’язані, тому вони не є взаємозалежними та можуть бути перевірені окремо. Це також робить додаток більш адаптованим до змін з часом.

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

Покращена ізоляція несправностей (fault isolation): великі додатки продовжують функціонувати у випадку збою одного модуля.

Якщо чесно, то не завжди. Тут часто відбувається «ефект доміно». Коли один сервіс перестає працювати, а ті сервіси, що залежать від нього (використовують його REST API, наприклад), також перестають працювати. І це може торкнутися весь проект, якщо не передбачити спеціальних заходів (типу circuit breaker)

Дякую за коментар,

Здебільшого масштабується вертикально (шляхом збільшення кількості RAM та CPU), адже використання горизонтального масштабування не є доцільним.

Цікаво, а чому? Горизонтальне масштабування по-перше, забезпечує High availability, по-друге, знижує навантаження на кожен сервер.

Горизонтальне масштабування для монолітів не є оптимальним з точки зору використанням пам’яті, адже на 99.9% потрібно вам не потрібно скейлити весь функціонал моноліту, а лише окрему його частину

Надійність. Помилка в будь-якому з модулів програми може призвести до зупинки всього додатка.
Наприклад, яка помилка? Я часто зустрічав помилки, які робили компонент неробочим. Але щось не пам’ятаю помилки, які призводили саме до зупинки додатка.

Як приклад — Memory leak

Переваги:
Можливість експериментувати з різними технологіями: між модулями є менші технологічні залежності. Відкат до попередніх ітерацій менш складний.

Тут трохи спірне питання. Уявімо ситуацію, що вам потрібно створити три команди розробників (у кожної свій мікросервіс).
І ви замість трьох Java команд набираєте команду на Java, Пітоні та Node.js. Теоретично це працюватиме, але наскільки ефективно вони разом працюватимуть, вирішувати спільні проблеми, створювати best practices?

Як і мікросервіси, команди повинні будти loosely coupled, і тому спільних проблем у них не має бути. Лише у випадку небхідності інтеграції мікросервісів команди мають комунікувати щодо формату взаємодії(message format, API contract).
Щодо команд, рекомендую ознайомитись зі статтею.

Мале зв’язування (Low coupling): компоненти мікросервісів слабо пов’язані, тому вони не є взаємозалежними та можуть бути перевірені окремо. Це також робить додаток більш адаптованим до змін з часом.

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

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

Покращена ізоляція несправностей (fault isolation): великі додатки продовжують функціонувати у випадку збою одного модуля.

Якщо чесно, то не завжди. Тут часто відбувається «ефект доміно». Коли один сервіс перестає працювати, а ті сервіси, що залежать від нього (використовують його REST API, наприклад), також перестають працювати. І це може торкнутися весь проект, якщо не передбачити спеціальних заходів (типу circuit breaker)

Імплементація мікросервісного підходу має на увазі і обробку negative case’ів, тому проблеми типу «ефект доміно» мають бути оброблені ще на етапі проєктування архітектури.

Так делить приложение на модули можно исходя из соображений горизонтального масштабирования. То есть, какой-то редко используемый функционал можно весь держать в 1 сервисе, а тот, где идут большие вычисления, или поступает много запросов — выносить в отдельный сервис, который можно запускать на несколько подов.

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

До порівняльної таблиці є питання.

Що означає для моноліта оцінка 1/5 у рядку Reuse? Це означає, що повторне використання коду в моноліті дуже погане? В чому саме це проявляється? У моноліті важко використовувати написану функцію чи клас? Чи що тут мається на увазі?

Аналогічне питання до часу на розробку. Чому в цьому плані моноліт оцінено на 5/5? Можливо тут мається на увазі оцінка, що типу тут все ок із цим. Але це ще й можна трактувати як найбільше часу потрібно для моноліта.

Reuse говорить про те, чи можемо ми перевикористати цілий сервіс, тому у випадку моноліта оцінка є низькою.

Щодо оцінок, то 1/5 — мінімальна оцінка, 5/5 — максимальна оцінка. Тому 5/5 у development time каже, що розробка і деплоймент займе менше часу ніж у іншіх.

Reuse говорить про те, чи можемо ми перевикористати цілий сервіс, тому у випадку моноліта оцінка є низькою.

Усе вказує на те, що ви «за вуха притягуєте» негатив моноліта. У моноліті запросто можна створити модуль — аналог мікросервісу, який потім легко і невимушено можна опублікувати, наприклад, на npmjs.com.

Для об’єктивності порівняння вам очевидно бракує оцінки саме модульного моноліта. Переваги у мікросервісів є, але на багатомільйонну країну, таку як Україна, проектів, які реально потребують мікросервісів, мабуть менше десятка назбирається (Розетка, Хотлайн, olx, Лун і т.д.). Для решти, мікросервіси мають набагато більше мінусів, ніж плюсів.

Я ж і не кажу, що мікросервіси це панацея, для кожного з підходів є свої use case’и та свої недоліки і переваги, що відображені в порівняльній таблиці. Головна ідея моєї статті — донести, що крім моноліту і мікросервісів є ще варіанти.

Дякую, цікаво було почитати. Суть викладаєте добре, але орфографічних помилок в тексті вистачає. Ну і на «застосунок» говорити «додаток» — це вже іншого роду помилка.

Дякую за коментар, врахую ваші зауваження)

Підписатись на коментарі