Skip to content

Microservices

Microservices

Моноліт

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

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

Моноліт підходить для невеликих команд і проєктів із передбачуваними обсягами роботи. Його також використовують для MVP (мінімально життєздатного продукту), де швидкість розробки важливіша за складні архітектурні рішення.

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

Переваги моноліту

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

Недоліки моноліту

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

Links

Microservices - Мікросервіси

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

Кожен сервіс є незалежним, має свою кодову базу, базу даних (або доступ до окремих таблиць) і виконує одне конкретне завдання. Взаємодія між сервісами зазвичай відбувається через легкі протоколи, такі як HTTP (REST, GraphQL) або брокери повідомлень (RabbitMQ, Kafka).

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

Переваги мікросервісів

  • Зручність масштабування: кожен сервіс можна масштабувати незалежно від інших.
  • Можливість використовувати різні технології для різних сервісів залежно від їхніх потреб.
  • Полегшення підтримки: зміни в одному сервісі не впливають на інші (за умови чітких контрактів).
  • Зменшення ризику: збій в одному сервісі не зупиняє роботу всієї системи.

Недоліки мікросервісів

  • Ускладнена інфраструктура: потрібен надійний механізм для оркестрації сервісів, моніторингу та логування.
  • Накладні витрати на комунікацію: виклики між сервісами додають латентність і вимагають захисту від помилок.
  • Складнощі в забезпеченні узгодженості даних між сервісами.

Моноліт vs Мікросервіси

Summary

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

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

Сценарії для Моноліту

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

Сценарії для Мікросервісів

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

Database per Service

Summary

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

Принцип роботи

Сервіс інкапсулює не лише код бізнес-логіки, а й схему даних. Інші сервіси отримують потрібні їм дані через публічний API цього сервіса (REST/gRPC) або через події (publish-subscribe), а не безпосереднім SQL-запитом до чужої таблиці. Таким чином зміна внутрішньої схеми не вимагає скоординованого деплою кількох сервісів.

Переваги

  • Автономність деплою. Зміна схеми всередині сервіса не зачіпає інших.
  • Свобода вибору сховища. Сервіс пошуку може триматися на Elasticsearch, профільний - на Postgres, аналітичний - на ClickHouse.
  • Локальні транзакції залишаються простими. Один сервіс - один транзакційний контекст у власній БД.

Недоліки

  • Розподілена консистентність. Дані, які раніше об'єднувалися одним JOIN, тепер вимагають Saga / Outbox / CDC для синхронізації.
  • Дублювання даних. Профільний сервіс часто тримає копію частини даних з інших сервісів для локального читання.
  • Звітність ускладнюється. Кросс-сервісна аналітика будується через ETL/CDC у окреме сховище, а не одним SQL-запитом.

Типові порушення та як з ними боротися

  • Запис з іншого сервісу в чужу таблицю - найчастіший антипатерн. Поза часом інциденту виявити складно, бо доступ, що починався лише з читання, з часом отримує і запис. Фіксується ревізією міграцій і правом доступу на рівні ролей у БД: сервіс має read/write лише на власні таблиці.
  • Прямі JOIN'и через dblink/foreign tables. Те саме - повертає сильну зв'язаність схем. Замінюється API-викликом або реплікацією через CDC.
  • Пряме читання чужої БД для звітності - вирішується розгортанням окремої read-репліки або потоку CDC в аналітичне сховище, а не прямим читанням операційної БД іншого сервіса.

Зв'язок із суміжними патернами

  • Saga / Transactional Outbox / Inbox Pattern - механіки, які підтримують консистентність даних, розділених між сервісами.
  • CQRS - природне продовження: read-модель агрегує дані з кількох сервісів у окремому сховищі, не порушуючи кордонів запису.

API Gateway

Summary

API Gateway - єдина точка входу для зовнішніх клієнтів у систему мікросервісів. Виконує маршрутизацію, агрегацію відповідей, балансування навантаження, аутентифікацію та rate limiting, звільняючи окремі сервіси від цих cross-cutting обов'язків.

Призначення

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

Принцип роботи

  • Маршрутизація. Зовнішній запит GET /api/v1/orders/42 потрапляє на gateway, який за конфігурацією маршрутів пересилає його до сервіса замовлень.
  • Агрегація відповідей. Один зовнішній запит (GET /api/v1/checkout/summary) може транслюватися в кілька внутрішніх викликів (профіль, кошик, доставка) і повертати клієнту склеєну відповідь. Цей варіант роботи gateway інколи називають Backend-for-Frontend (BFF).
  • Аутентифікація і авторизація. Gateway перевіряє JWT або сесійний токен, після чого передає внутрішнім сервісам уже довірений user_id у заголовку. Самі сервіси не повторюють перевірку підпису.
  • Rate limiting. Обмеження частоти запитів per-user або per-IP реалізується на gateway, а не в кожному сервісі окремо. Деталі алгоритмів - у секції Rate Limiter.
  • Спостережуваність. Структуровані логи, metrics і trace-id додаються у вхідному пункті і прокидаються далі по системі.

Балансування навантаження на gateway

Балансування буває двох рівнів:

  • L4 (транспортний). Балансер працює на рівні TCP/UDP, не розбирає HTTP. Розподіл - за вихідним IP/портом, round-robin, hash. Дешевший і швидший, але не вміє роздавати запити за HTTP-заголовками чи cookie.
  • L7 (прикладний). Балансер розбирає HTTP-запит і розподіляє його за заголовками, шляхом, cookie. Дорожчий за CPU, але потрібен для sticky sessions, A/B-тестування, canary deployments.

Sticky sessions і consistent hashing

Якщо сесія користувача збережена в пам'яті конкретного інстанса сервіса (anti-pattern для stateless систем, але реальність для legacy), потрібно скеровувати наступні запити цього користувача на той самий інстанс - sticky session, зазвичай через cookie на L7.

Простий hash за IP має проблему: при додаванні чи видаленні інстанса перерозподіляється майже весь трафік. Consistent hashing мінімізує зсуви - при зміні складу пулу переходить лише пропорційна частка ключів. Деталі - стандартна тема system design.

Обмеження

  • Single point of failure. Gateway сам потребує кластеризації і резервування - інакше один збій кладе всю систему.
  • Додаткова латентність. Один зайвий мережевий хоп для кожного зовнішнього запита.
  • Антипатерн "товстого gateway". Перенесення бізнес-логіки у gateway перетворює його на моноліт, на заміну якому й проектувалася мікросервісна архітектура.

Реалізації

  • Готові: Kong, Traefik, Envoy + Istio, AWS API Gateway, Cloudflare Gateway.
  • Self-hosted: nginx з Lua-модулями, HAProxy.

Зв'язок із Sidecar pattern / Service Mesh

Gateway обробляє трафік north-south (зовнішній клієнт ↔ система). Sidecar і Service Mesh обробляють трафік east-west (сервіс ↔ сервіс всередині системи). Це різні шари, які доповнюють одне одного, а не замінюють.

Sidecar pattern

Summary

Sidecar - допоміжний процес (зазвичай окремий контейнер у тому ж pod'і), що працює поруч з основним сервісом і обслуговує cross-cutting задачі: TLS, retry, circuit breaker, метрики, трасування, авторизацію. Бізнес-сервіс залишається мінімалістичним, інфраструктурна логіка - в sidecar.

Проблема, яку вирішує

У системі з десятками сервісів на різних мовах кожна команда має повторно реалізовувати ті самі речі: HTTPS, ретраї, таймаути, circuit breaker, експорт метрик у Prometheus, distributed tracing, JWT-перевірки. Це дублювання коду, дублювання інцидентів і дублювання версій бібліотек.

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

Принцип роботи

  • У pod'і Kubernetes (або одиниці деплою VM-епохи) запускається два контейнери: основний бізнес-контейнер та sidecar.
  • Зовнішній трафік потрапляє в sidecar (через iptables-перенаправлення або явний slug у мережевому стеці). Sidecar виконує TLS-термінацію, перевіряє токени, застосовує політики, потім пересилає запит на localhost у бізнес-контейнер.
  • Вихідний трафік від бізнес-контейнера так само йде через sidecar, який додає mTLS, ретраї, circuit breaker, metric/trace-id.
  • Конфігурація політик надходить з центрального control plane і не зашита в код сервіса.

Типові обов'язки sidecar

  • Транспортна безпека: mTLS між сервісами (див. підсекцію в Service Mesh).
  • Стійкість: retry з exponential backoff + jitter, circuit breaker, таймаути на з'єднання та читання, bulkhead-ізоляція.
  • Спостережуваність: експорт RED-метрик (Rate, Errors, Duration), distributed tracing (OpenTelemetry).
  • Авторизація: RBAC і per-route політики, JWT-перевірка, ACL за SPIFFE-ідентифікатором.
  • Маршрутизація: canary, traffic splitting, retry policies per-endpoint.

Переваги

  • Polyglot-системи. Sidecar один на всі мови - бізнес-сервіс може бути написаний на Go, Python, Rust без потреби тримати n SDK.
  • Оновлення без правки бізнес-коду. Зміна політики ретраю - конфіг у control plane, без релізу сервіса.
  • Чистий бізнес-сервіс. У коді сервіса немає requests.adapters.HTTPAdapter(max_retries=...), tenacity, перевірки JWT, експорту метрик.

Недоліки

  • Зайвий хоп на кожен запит. Латентність трохи зростає (зазвичай порядку міллісекунд для well-tuned Envoy).
  • Ресурсна вартість. На кожен pod - додатковий процес з власною пам'яттю та CPU. У масштабі тисяч pod'ів це помітно.
  • Складність деплою. Pod зі sidecar'ом стає двоконтейнерним - діагностика, моніторинг, версіонування ускладнюються.

Реалізації

  • Envoy - найпоширеніший sidecar-проксі (C++), використовується в Istio, Consul Connect, AWS App Mesh.
  • Linkerd-proxy - легший Rust-проксі.
  • NGINX як sidecar - історична опція, менш гнучка за політиками.

Зв'язок з Service Mesh

Sidecar - data plane service mesh. Сам по собі sidecar - лише процес-проксі; інфраструктура, що ним керує (control plane, конфіги, сертифікати), - це і є service mesh.

Service Mesh

Summary

Service Mesh - інфраструктурний шар для управління міжсервісною комунікацією, що складається з data plane (sidecar-проксі біля кожного сервіса) і control plane (централізованого керування політиками, маршрутизацією, сертифікатами). Виносить cross-cutting логіку (mTLS, retry, метрики, traffic splitting) з коду сервісів у конфігурацію.

Архітектура

  • Data plane - sidecar'и (зазвичай Envoy) поруч з кожним екземпляром сервіса; перехоплюють вхідний і вихідний трафік.
  • Control plane - центральні компоненти (Istiod в Istio, Linkerd control plane), що видають data plane'у:
  • політики маршрутизації (VirtualService, DestinationRule),
  • сертифікати для mTLS (через SPIFFE/SPIRE),
  • конфіг retry/timeout/circuit breaker,
  • правила авторизації (AuthorizationPolicy).

Сервіс не знає про mesh - він робить звичайний http://orders/list, а sidecar розв'язує адресу, додає mTLS і метрики.

Можливості

  • mTLS між усіма сервісами (див. нижче).
  • Traffic management: canary release (5% трафіку на v2), shadow traffic, retry-budget, fault injection для chaos engineering.
  • Observability: автоматичні RED-метрики, distributed tracing для всіх викликів без правки коду.
  • Security: zero-trust політики "сервіс A може звертатися до сервіса B лише на /orders".

mTLS на рівні mesh

mTLS (mutual TLS) - двостороння автентифікація: і клієнт, і сервер пред'являють X.509-сертифікат. У звичайному TLS лише сервер автентифікує себе перед клієнтом, у mTLS - обидві сторони.

Переваги над JWT-перевіркою в коді кожного сервіса:

  • Ідентичність на рівні з'єднання, а не запиту. Sidecar встановлює mTLS-сесію один раз; усі запити в ній уже довірені. Не потрібно валідувати підпис JWT на кожен HTTP-виклик.
  • Один шар відповідальності. Видача й ротація сертифікатів - повністю в control plane (Istiod, SPIRE). Бізнес-код не знає про секрети.
  • Zero-trust за замовчуванням. Без mTLS неможливо встановити з'єднання, тому припущення про довірену внутрішню мережу відпадає - кожен виклик автентифікується криптографічно.
  • Інтеграція з RBAC. Політики виду "сервіс orders може звертатися до payments лише на endpoint /charge" описуються у декларативному конфізі mesh, а не в коді кожного сервіса.

JWT при цьому не зникає - він залишається механізмом передачі користувацької ідентичності (user_id, roles) крізь систему. mTLS вирішує сервісну ідентичність.

Реалізації

  • Istio + Envoy - найфункціональніший і найскладніший варіант.
  • Linkerd - простіший, легший Rust-проксі, менше функцій.
  • Consul Connect - mesh від HashiCorp, інтегрований з Consul service discovery.
  • AWS App Mesh - керована опція в AWS.

Сценарії застосування

  • Сотні мікросервісів на різних мовах із спільними вимогами до безпеки, спостережуваності, ретраїв.
  • Регуляторні вимоги до шифрування трафіку всередині кластера.
  • Складні стратегії релізу (canary, blue-green) на рівні маршрутизації.

Для невеликих систем (десятки сервісів, одна команда, одна мова) service mesh є надлишковим: ті самі задачі простіше вирішити бібліотекою на боці сервіса.