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 є надлишковим: ті самі задачі простіше вирішити бібліотекою на боці сервіса.