Microservices
Microservices⚑
Моноліт [💡10/100]⚑
Моноліт — це архітектурний підхід до розробки програмного забезпечення, в якому вся функціональність додатку зосереджена в одній єдиній кодовій базі та розгортається як єдине ціле. Це традиційний і часто простіший підхід до побудови систем, але з часом може створювати певні виклики у підтримці та масштабуванні.
Усі компоненти програми (бізнес-логіка, інтерфейс користувача, доступ до бази даних) знаходяться в одному додатку. Логіка взаємодії між компонентами часто реалізується через прямі виклики методів або функцій.
Моноліт підходить для невеликих команд і проєктів із передбачуваними обсягами роботи. Його також використовують для MVP (мінімально життєздатного продукту), де швидкість розробки важливіша за складні архітектурні рішення.
Моноліт часто є першим кроком у розробці, але з часом додатки можуть бути розділені на мікросервіси для підвищення гнучкості та масштабованості.
Переваги моноліту
- Простота розробки та деплойменту, особливо на ранніх етапах проєкту.
- Можливість розгорнути весь додаток як один артефакт, що спрощує адміністрування.
- Менші накладні витрати на мережеву взаємодію між компонентами.
Недоліки моноліту
- З часом ускладнюється підтримка великого коду, особливо якщо команда зростає.
- Масштабування потребує збільшення ресурсів для всього додатку, навіть якщо навантаження створює лише одна його частина.
- Тісно пов’язані компоненти можуть ускладнювати впровадження нових технологій або змін.
Links
Microservices - Мікросервіси [🔥31/100]⚑
Мікросервіси — це архітектурний стиль, який передбачає розбиття програми на набір незалежних компонентів (сервісів), кожен з яких відповідає за окрему бізнес-функцію і може розгортатися та масштабуватися окремо. Цей підхід спрямований на підвищення гнучкості, простоти підтримки та масштабування великих систем.
Кожен сервіс є незалежним, має свою кодову базу, базу даних (або доступ до окремих таблиць) і виконує одне конкретне завдання. Взаємодія між сервісами зазвичай відбувається через легкі протоколи, такі як HTTP (REST, GraphQL) або брокери повідомлень (RabbitMQ, Kafka).
Підходить для великих і динамічних проєктів, де важлива можливість масштабувати окремі частини системи або швидко впроваджувати зміни. Мікросервіси дозволяють створювати гнучкі та масштабовані системи, але потребують ретельного планування, щоб уникнути зростання складності в управлінні.
Переваги мікросервісів
- Зручність масштабування: кожен сервіс можна масштабувати незалежно від інших.
- Можливість використовувати різні технології для різних сервісів залежно від їхніх потреб.
- Полегшення підтримки: зміни в одному сервісі не впливають на інші (за умови чітких контрактів).
- Зменшення ризику: збій в одному сервісі не зупиняє роботу всієї системи.
Недоліки мікросервісів
- Ускладнена інфраструктура: потрібен надійний механізм для оркестрації сервісів, моніторингу та логування.
- Накладні витрати на комунікацію: виклики між сервісами додають латентність і вимагають захисту від помилок.
- Складнощі в забезпеченні узгодженості даних між сервісами.
Моноліт vs Мікросервіси [💡26/100]⚑
Summary
Моноліт підходить для простих рішень і швидкого старту, тоді як мікросервіси необхідні для великих, складних проєктів, які вимагають масштабованості та модульності.
Вибір між монолітною архітектурою та мікросервісами залежить від масштабів проєкту, команди та бізнес-вимог. Кожен підхід має свої сильні сторони, які слід враховувати залежно від конкретної ситуації.
Сценарії для Моноліту
- Проєкт невеликий, із чітко визначеною функціональністю та обмеженими масштабами.
- Команда розробників мала, і всі члени працюють над одним кодом.
- Потрібно швидко створити MVP (мінімально життєздатний продукт).
- Інфраструктурні ресурси обмежені, немає необхідності в складній оркестрації.
- Немає значних вимог до незалежного масштабування різних компонентів.
- Основний фокус на простоті розробки, підтримки та розгортання.
Сценарії для Мікросервісів
- Проєкт великий і передбачає постійне зростання як за функціональністю, так і за навантаженням.
- Є чітка потреба в масштабуванні окремих частин системи, наприклад, модуля оплати чи пошуку.
- У проєкті працює велика команда, яка може бути розділена на кілька груп, кожна з яких відповідає за окремий сервіс.
- Проєкт потребує високої гнучкості у виборі технологій для різних частин системи.
- Необхідно забезпечити високу надійність: збої в одному сервісі не повинні впливати на роботу інших.
- Бізнес-вимоги швидко змінюються, і потрібна модульність для швидкого впровадження нових функцій.
Розрізнення мікросервісів і модульного моноліту
Поширена помилка на співбесіді - назвати ознакою інкапсуляцію або взаємозамінність ("кожен сервіс замкнений у собі", "сервіс можна підмінити іншою реалізацією"). Це не відмітні ознаки: добре спроєктований модуль моноліту так само інкапсульований, а взаємозамінність реалізацій - це LSP, який однаково діє і для класів. Відрізняють архітектури не властивості коду, а спосіб розгортання та володіння ресурсами - те, що видно ззовні, зі схем і конфігурації:
- Окремий процес і незалежний деплой. Модулі моноліту виконуються в одному процесі і деплояться разом; мікросервіси - окремі процеси/контейнери з власним циклом релізу. Це необхідна ознака: без неї це моноліт незалежно від того, наскільки "сервісним" виглядає код.
- Власна БД на сервіс (див. Database per Service). Якщо кожен компонент має своє сховище й не ходить у чужі таблиці - це майже напевно мікросервіси; модулі моноліту зазвичай ділять одну БД.
- Поліглотний рантайм. Компоненти на різних мовах (Python + Go + JVM) - достатня, хоча й не необхідна ознака: класи моноліту живуть в одному рантаймі, тож різні мови майже завжди означають окремі сервіси за мережевою межею.
Жодна ознака окремо не є абсолютною (мікросервіси бувають одномовними; спільна БД - відомий антипатерн distributed monolith), але разом вони дають надійне розрізнення.
Strangler Fig (поступова міграція моноліту)⚑
Summary
Поступова заміна моноліту: перед ним ставлять маршрутизуючий фасад, який перенаправляє окремі частини функціональності на нові сервіси. Моноліт "обплітається" новими сервісами і поступово відмирає - замість ризикованого переписування з нуля.
Проблема, яку вирішує
Переписування великої системи "з нуля" (big-bang rewrite) ризиковане: розробка нової версії триває місяцями, на цей час доводиться заморожувати зміни у старій, а перемикання відбувається одномоментно - якщо щось зламано, страждає вся система. Strangler Fig замінює це інкрементною міграцією, де кожен крок невеликий, перевіряється у проді й допускає відкат.
Принцип роботи
Назва походить від фікуса-душителя, який обплітає дерево-господар і поступово займає його місце. Перед монолітом розташовують маршрутизуючий шар (проксі або API Gateway). Окремий домен виносять у новий сервіс, після чого маршрутизатор спрямовує відповідні запити на нього, а решту - далі в моноліт. З кожним кроком на нові сервіси переходить більше маршрутів; моноліт зменшується і зрештою вимикається повністю.
Перехідний стан
Поки міграція триває, винесений сервіс може тимчасово читати дані зі спільної з монолітом БД - це прийнятний проміжний компроміс, а не цільовий стан. Кінцева мета - власна БД на сервіс (Database per Service): кожен сервіс володіє власним сховищем, а спільні дані синхронізуються через API чи події.
Обмеження
- Потрібен надійний маршрутизуючий шар; помилки маршрутизації зачіпають увесь трафік.
- На час переходу логіка частково дублюється між монолітом і новим сервісом.
- Поки БД спільна, зберігається зв'язаність схем - повна автономність настає лише після розділення даних.
- Межі виносу мають збігатися з bounded context'ами (див.
architecture/ddd.md); невдалий поділ дає distributed monolith.
Database per Service [💡17/100]⚑
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 [💡22/100]⚑
Summary
API Gateway - єдина точка входу для зовнішніх клієнтів у систему мікросервісів. Виконує маршрутизацію, агрегацію відповідей, балансування навантаження, аутентифікацію та rate limiting, звільняючи окремі сервіси від цих cross-cutting обов'язків.
Призначення
У системі з десятками мікросервісів зовнішньому клієнту незручно знати адресу кожного сервіса і дублювати в коді логіку авторизації, ретраїв, балансування. Gateway виносить цю логіку на один шар перед сервісами.
Принцип роботи
- Маршрутизація. Зовнішній запит
GET /api/v1/orders/42потрапляє на gateway, який за конфігурацією маршрутів пересилає його до сервіса замовлень. - Агрегація відповідей. Один зовнішній запит (
GET /api/v1/checkout/summary) може транслюватися в кілька внутрішніх викликів (профіль, кошик, доставка) і повертати клієнту склеєну відповідь. Цей варіант роботи gateway інколи називають Backend-for-Frontend (BFF). Узагальнення цього підходу - composite UI: коли дані одного екрана розпорошені по кількох bounded context'ах (хибний агрегат), кожен контекст віддає й рендерить свій зріз; на запис один зовнішній POST навпаки розкладається (decomposition) на виклики до контекстів-власників, а кожен контекст кешує свій зріз із власним TTL (ціну - хвилини, назву й опис - місяці, наявність - без кешу). - Аутентифікація і авторизація. 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.
Окремо розрізняють reverse proxy і load balancer: reverse proxy корисний навіть з одним бекендом (термінація TLS, стиснення, кеш статики, приховування внутрішніх серверів), тоді як load balancer розподіляє запити між кількома інстансами.
Алгоритми розподілу. Незалежно від рівня, балансер обирає інстанс за алгоритмом:
- Round-robin - по колу, порівну. Просто; ігнорує реальне навантаження інстанса.
- Weighted round-robin - як round-robin, але потужнішим інстансам дають більшу вагу (більше запитів). Враховує різну ємність машин, але ваги статичні й не відбивають навантаження в реальному часі.
- Least connections - на інстанс із найменшою кількістю активних з'єднань. Динамічно враховує поточне навантаження; корисно, коли з'єднання мають різну тривалість.
- Hash-based - інстанс обирається хешем від ключа (IP клієнта,
user_id). Той самий клієнт стабільно потрапляє на той самий інстанс (основа sticky sessions); зміна складу пулу ламає прив'язку - звідси consistent hashing нижче.
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 (сервіс ↔ сервіс всередині системи). Це різні шари, які доповнюють одне одного, а не замінюють.
Service Discovery⚑
Summary
Service Discovery - механізм, який дозволяє сервісам знаходити одне одного у динамічному оточенні, де інстанси постійно з'являються (auto-scaling), переміщуються (rolling deploy) і зникають (crash, K8s reschedule). Без Discovery hard-coded IP-адреси перестають працювати першого ж дня. Дві основні моделі - client-side (клієнт сам опитує реєстр і обирає інстанс) і server-side (інфраструктурний шар - API Gateway, Service Mesh, K8s Service - робить це за клієнта).
Проблема, яку розв'язує
У монолітній системі компоненти "знаходять" одне одного через виклики функцій у тому ж процесі. У мікросервісах виклик "клієнт → сервіс orders" вимагає IP-адреси і порту. Якщо orders має 5 інстансів, які можуть зникнути і з'явитись будь-якої миті, hard-coded конфіг не підходить.
Service Discovery - dynamic name-resolution для сервісів: за іменем (orders.prod.local) повертає актуальний список здорових ендпоінтів.
Client-side discovery
Клієнт запитує реєстр (Service Registry) і отримує список інстансів, обирає один (load balance) і робить запит напряму.
[client] → [service registry] → list of orders instances
[client] → directly to orders-instance-3:8080
Реалізації:
- Netflix Eureka - історично канонічна для Spring/Java; REST API, heartbeat для liveness, dropped інстанси через ~30s без heartbeat'у.
- HashiCorp Consul - крім registry дає health checks, KV store, DNS interface (можна запитувати сервіси як звичайний DNS).
- etcd / ZooKeeper - low-level KV-сховища, на яких будують registry (Kubernetes сам використовує etcd для service-discovery).
Переваги: клієнт контролює load-balancing (наприклад, sticky session, geographic affinity), один мережевий хоп. Недоліки: кожен клієнт мусить мати logic discovery (бібліотека на кожній мові), складніше міняти стратегію без redeploy клієнтів.
Server-side discovery
Клієнт робить запит на стабільну точку (API Gateway, Service Mesh sidecar, K8s Service), а вона сама знає, куди розподілити.
Реалізації:
- Kubernetes Service - найпоширеніша сучасна модель:
kube-proxyробить iptables/eBPF-правила, які перенаправляють трафік на здорові поди. Клієнт бачить тільки стабільнийClusterIPабо DNS-ім'яorders.default.svc.cluster.local. - API Gateway (Kong, Nginx, Spring Cloud Gateway) - shed-зовнішнього трафіку, плюс service-discovery лише на boundary системи.
- Service Mesh (Istio, Linkerd) - sidecar-проксі бачить service registry через control plane і робить routing/load-balancing замість клієнта.
Переваги: клієнт нічого не знає про discovery (звичайний HTTP до orders/), можна міняти стратегію без redeploy. Недоліки: додатковий мережевий хоп (через gateway/proxy), gateway сам стає critical-path component.
Реєстрація і health checks
Інстанс має зареєструватись у реєстрі при старті і слати періодичний heartbeat. Якщо heartbeat припинився - реєстр позначає інстанс як unhealthy і прибирає його з ротації. Канонічні підходи:
- Self-registration - сервіс сам викликає
register()при старті,deregister()при graceful shutdown. Просто, але вимагає бібліотеки на кожній мові. - Third-party registration - окремий компонент (
registratorу Docker, K8s controller для подів) спостерігає за станом і реєструє/дереєструє за сервіс. Канонічно у K8s.
Сучасний default - Kubernetes
У K8s service discovery вбудований: Pod автоматично реєструється у endpoints об'єкті свого Service'у; kube-proxy/CoreDNS забезпечують resolution. Окремий Eureka/Consul у K8s-cluster'і зазвичай зайвий - дубль функціональності. Eureka/Consul залишаються релевантними поза K8s (VM-on-prem, multi-cluster, гібридні середовища).
Links
- Microsoft docs: Service registry pattern
- HashiCorp Consul docs: Service Discovery
- Kubernetes docs: Services
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 є надлишковим: ті самі задачі простіше вирішити бібліотекою на боці сервіса.
Distributed Tracing [💡10/100]⚑
Summary
Distributed Tracing - спосіб простежити одиничний запит, що проходить через N мікросервісів, як єдину сутність. У монолітному застосунку стек-трейс покаже шлях запиту; у мікросервісах кожен сервіс має свій лог, і без явного Trace ID, що супроводжує запит, реконструювати картину неможливо. Канонічний стандарт - OpenTelemetry (об'єднання OpenTracing + OpenCensus, CNCF, 2019).
Проблема, яку розв'язує
Запит клієнта POST /orders у системі з 5 сервісами проходить через api-gateway → orders → inventory → payments → notifications. Якщо payments повертає 500, у логах notifications нічого не видно, а у логах orders - тільки факт, що "виклик payments впав". Без єдиного ідентифікатора неможливо знайти ВСІ записи, що належать цьому конкретному запиту, серед мільйонів інших.
Trace, span, parent-child
OpenTelemetry і попередники (Dapper, Zipkin) описують модель:
- Trace - вся життєва історія одного запиту через систему. Має унікальний
trace_id(наприклад, 128-bit UUID). - Span - одиничний відрізок роботи в межах одного сервісу (HTTP-call, DB-query, обробка повідомлення з MQ). Має
span_id,parent_span_id(для побудови дерева),start_time,duration,attributes(теги). - Один Trace складається з багатьох Spans, утворюючи дерево або DAG.
Trace abc123 (POST /orders, 850ms total)
├── span_1: api-gateway (auth, 5ms)
└── span_2: orders.create (820ms)
├── span_3: orders.db_insert (40ms)
├── span_4: inventory.check (50ms)
│ └── span_5: inventory.db_query (15ms)
└── span_6: payments.charge (700ms) ← bottleneck
Trace ID propagation
trace_id народжується на edge (API Gateway генерує його для нового запиту, який не приніс свого) і передається у кожен наступний синхронний/асинхронний виклик через HTTP-заголовки. Стандарт - W3C Trace Context (traceparent, tracestate).
Кожен сервіс на ході:
- Витягує
trace_idіз вхідного запиту (або генерує, якщо немає). - Створює власний span з цим
trace_idіparent_span_id. - Додає
trace_idу структуровані логи ({"trace_id": "abc123", "msg": "..."}). - Передає
trace_idу вихідні виклики (HTTP-headers, Kafka-headers).
OpenTelemetry як стандарт
OpenTelemetry - єдиний sdk + протокол (OTLP) для збору метрик, логів і трейсів. Bтратив фрагментацію (раніше: OpenTracing для трейсів, OpenCensus для метрик, що дублювали один одного). Канонічний пайплайн:
Для популярних бібліотек (requests, aiohttp, SQLAlchemy, asyncpg, FastAPI middleware) існує auto-instrumentation - окремі пакети (opentelemetry-instrumentation-<lib>) обгортають виклики бібліотеки у hook'и і експортують span'и без зміни коду застосунку.
Log aggregation: ELK, Loki, CloudWatch
Trace ID у логах марний без агрегації - логи лежать на N машинах. Канонічний пайплайн:
- ELK (Elasticsearch + Logstash + Kibana) - індексує всі логи, дозволяє query
trace_id:abc123для одиничного запиту. - Grafana Loki - простіший і дешевший за ELK, label-based індексація; query через LogQL.
- Fluentd / Fluent Bit - log forwarder (заміна Logstash), збирає логи з контейнерів і відправляє у centralized сховище.
- AWS CloudWatch Logs / GCP Cloud Logging - cloud-native варіанти.
Кореляція з метриками і алертами
Високий за тривалістю span у трейсі → алерт у Grafana → click на трейс у Jaeger → деталі span'у payments.charge → click на trace_id у Kibana → повний contextual лог. Це і є "observability" триада: logs + metrics + traces, об'єднані через trace_id/span_id.
Sidecar / Service Mesh як safety net
У Service Mesh (Istio + Envoy) інструментація трейсингу - автоматична: sidecar бачить весь HTTP-трафік, генерує/прокидає trace headers, експортує span'и без зміни коду сервісу. Деталі - у Service Mesh.
Links
- Google Dapper paper (2010) - оригінальна архітектура, з якої виросли Zipkin/Jaeger/OpenTelemetry
- W3C Trace Context - стандарт propagation через HTTP
- OpenTelemetry docs - сучасний стандарт інструментації
- The Three Pillars of Observability - Cindy Sridharan, чому logs+metrics+traces потрібні разом