Netramesh – легковажне service mesh рішення

У процесі переходу від монолітної програми до мікросервісної архітектури ми стикаємося з новими проблемами.

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

Netramesh – легковажне service mesh рішення

Я довго шукав інструмент, який би допоміг упоратися з такими проблемами (писав про це на Хабрі: 1, 2), але у результаті зробив власне опенсорсне рішення. У статті я розповідаю про переваги підходу service mesh та ділюся новим інструментом для його реалізації.

Розподілений tracing є поширеним вирішенням проблеми пошуку помилок у розподілених системах. Але що якщо в системі ще не впроваджено такий підхід до збору інформації про мережеві взаємодії, або, що гірше, в частині системи він уже справно працює, а в частині його немає, оскільки він не доданий до старих послуг? Для визначення точної кореневої причини проблеми необхідно мати повну картину того, що відбувається у системі. Особливо важливо розуміти, які саме мікросервіси беруть участь у основних критичних для бізнесу шляхах.

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

Service mesh підхід

Головною ідеєю service mesh підходу є додавання ще одного інфраструктурного шару над мережею, який дозволить нам робити будь-які речі із міжсервісною взаємодією. Більшість реалізацій працюють наступним чином: до кожного мікросервісу додається додатковий sidecar контейнер із прозорим проксі, через який пропускається весь вхідний та вихідний трафік сервісу. І це те місце, де ми можемо робити клієнтське балансування, застосовувати політики безпеки, вводити обмеження на кількість запитів і збирати важливу інформацію щодо взаємодії сервісів у production.

Netramesh – легковажне service mesh рішення

Розв'язки

Вже є кілька реалізацій цього підходу: Істіо и linkerd2. Вони надають безліч можливостей із коробки. Але водночас приходить і великий overhead на ресурси. Причому чим більше кластер, в якому працює така система, тим більше буде потрібно ресурсів на підтримку нової інфраструктури. В Авіто ми експлуатуємо кубернети кластери, в яких знаходяться тисячі екземплярів сервісів (і їх число продовжує швидко зростати). У поточній реалізації Istio споживає ~300Mb оперативної пам'яті на кожен екземпляр сервісу. Через велику кількість можливостей прозоре балансування також впливає повний час відповіді сервісів (до 10ms).

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

У результаті ми дійшли свого рішення:  Netramesh.

Netramesh

Netramesh - Це легковажне service mesh рішення з можливістю нескінченного масштабування незалежно від кількості сервісів у системі.

Головними цілями нового рішення були невеликий overhead за ресурсами та висока продуктивність. З основних можливостей ми хотіли мати можливість прозоро відправляти tracing span'и в нашу Jaeger систему.

Сьогодні більшість хмарних рішень реалізується на Golang. І, звісно, ​​на це є свої причини. Писати на Golang мережеві додатки, що працюють асинхронно з введенням-виводом і що масштабуються за потребою на ядра, зручно і досить просто. І, що також дуже важливо, продуктивність виходить достатньою для вирішення цього завдання. Тому ми також обрали Golang.

Продуктивність

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

Давайте подивимося, які результати вийшли.

Оперативна пам'ять

Netramesh споживає ~10Mb без трафіку та 50Mb максимально з навантаженням до 10000 RPS на одну instance.

Istio envoy proxy завжди споживає ~300Mb у наших кластерах з тисячами instance'ів. Це не дозволяє масштабувати його на весь кластер.

Netramesh – легковажне service mesh рішення

Netramesh – легковажне service mesh рішення

З Netramesh ми отримали зменшення споживання пам'яті в ~10 разів.

центральний процесор

Використання CPU відносно рівне під навантаженням. Воно залежить від кількості запитів в одиницю часу до sidecar. Значення при 3000 запитах на секунду в піку:

Netramesh – легковажне service mesh рішення

Netramesh – легковажне service mesh рішення

Є ще один важливий момент: Netramesh – рішення без control plane та без навантаження не споживає процесорний час. З Istio sidecar'и завжди оновлюють endpoint'и сервісів. У результаті ми можемо бачити таку картину без навантаження:

Netramesh – легковажне service mesh рішення

Ми використовуємо HTTP/1 для взаємодії між сервісами. Збільшення часу відповіді у Istio під час проксування через envoy було до 5-10ms, що досить багато для сервісів, які готові відповідати за мілісекунду. З Netramesh цей час зменшився до 0.5-2ms.

масштабованість

Невелика кількість ресурсів, що витрачається кожним проксі, дає можливість розміщувати його поряд із кожним сервісом. Netramesh навмисно був створений без control plane компонента для простої підтримки легковажності кожного sidecar'а. Часто в сервісних mesh рішеннях управління планом розповсюджує сервісну інформацію на кожну сторонуавтомобіля. Разом з нею приїжджає і інформація про timeout'ах, налаштування балансування. Все це дозволяє робити багато корисних речей, але, на жаль, роздмухує sidecar'и в розмірі.

Відкриття служби

Netramesh – легковажне service mesh рішення

Netramesh не додає будь-яких додаткових механізмів для служби discovery. Весь трафік проксірується прозоро через netra sidecar.

Netramesh підтримує HTTP/1 прикладний протокол. Для його визначення використовується конфігурований список портів. Зазвичай у системі є кілька портів, якими відбувається взаємодія по HTTP. Наприклад, у нас для взаємодії сервісів та зовнішніх запитів використовуються 80, 8890, 8080. У такому разі їх можна задати за допомогою змінного оточення NETRA_HTTP_PORTS.

Якщо ви використовуєте Kubernetes як оркестратора та його механізм Service сутностей для внутрішньокластерної взаємодії між сервісами, то механізм залишається таким самим. Спочатку мікросервіс отримує service IP-адресу за допомогою kube-dns і відкриває нове з'єднання до нього. Це з'єднання встановлюється спочатку з локальним netra-sidecar і всі TCP пакети спочатку прилітають саме до netra. Далі вже netra-sidecar встановлює з'єднання з початковою точкою призначення. NAT на pod IP на ноді залишається так само як і без netra.

Розподілений tracing та прокидання контексту

Netramesh надає функціональність, необхідну для відправки tracing span'ів про HTTP взаємодію. Netra-sidecar парять HTTP протокол, вимірюють затримки запитів, дістають необхідну інформацію з HTTP header'ів. Нарешті ми отримуємо всі trace'и у єдиній Jaeger системі. Для тонкої конфігурації також можна використовувати змінні оточення, які надає офіційна бібліотека jaeger go library.

Netramesh – легковажне service mesh рішення

Netramesh – легковажне service mesh рішення

Але є проблема. Поки сервіси не генеруватимуть і прокидатимуть спеціальний uber заголовок, ми не побачимо з'єднані tracing span'и в системі. А це те, що нам потрібне для швидкого пошуку причини проблем. Тут Netramesh знову має рішення. Проксі читають заголовки HTTP і, якщо в них немає uber trace id, генерують його. Netramesh також зберігає інформацію про вхідні та вихідні запити в sidecar і зіставляє їх шляхом збагачення необхідними заголовками вихідних запитів. Все, що потрібно робити в сервісах - прокидати лише один заголовок X-Request-Id, який можна налаштувати за допомогою змінного оточення NETRA_HTTP_REQUEST_ID_HEADER_NAME. Для керування розміром context'а в Netramesh можна задавати наступні змінні оточення: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (час, протягом якого зберігатиметься контекст) та NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (Періодичність підчищення контексту).

Також можна комбінувати декілька шляхів у вашій системі шляхом маркування їх спеціальним сесійним маркером. Netra дозволяє встановити HTTP_HEADER_TAG_MAP для перетворення HTTP заголовків у відповідні tracing span теги. Це може бути особливо корисним для тестування. Після проходження функціонального тесту можна подивитися яка частина системи була порушена, відфільтрувавши за відповідним session ключем.

Визначення джерела запиту

Для визначення того, звідки надійшов запит, можна скористатися функціоналом автоматичного додавання заголовка із джерелом. За допомогою змінної оточення NETRA_HTTP_X_SOURCE_HEADER_NAME можна встановити ім'я заголовка, яке буде автоматично встановлюватися. За допомогою NETRA_HTTP_X_SOURCE_VALUE можна встановити значення, в яке буде встановлюватися X-Source заголовок на всі вихідні запити.

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

Роутінг трафіку та нутрощі Netramesh

Netramesh складається з двох основних компонентів. Перший netra-init встановлює мережеві правила для перехоплення трафіку. Він використовує iptables redirect правила для перехоплення всього або частини трафіку на sidecar, який є другим головним компонентом Netramesh. Можна налаштувати, які саме порти потрібно перехоплювати на вхідні та вихідні сесії TCP: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS.

Також в інструменті є цікава можливість - імовірнісний роутинг. Якщо використовувати Netramesh виключно для збору tracing span'ів, то в production оточенні можна заощадити ресурси та включити ймовірнісний роутинг за допомогою змінних NETRA_INBOUND_PROBABILITY и NETRA_OUTBOUND_PROBABILITY (Від 0 до 1). Значення за промовчанням дорівнює 1 (перехоплюється весь трафік).

Після успішного перехоплення netra sidecar приймає нове з'єднання та використовує SO_ORIGINAL_DST опцію сокету отримання початкової точки призначення. Потім Netra відкриває нове з'єднання до початкової IP-адреси і встановлює двостороннє TCP-спілкування між сторонами, слухаючи весь трафік. Якщо порт визначено як HTTP, Netra намагається парсить його і трейсить. Якщо парсинг HTTP виявляється неуспішним, Netra робить фоллбек на TCP і прозоро проксіює байти.

Побудова графа залежностей

Після отримання великої кількості tracing інформації у Jaeger, хочеться отримати повний графік взаємодій у системі. Але якщо ваша система досить навантажена і за день накопичуються мільярди tracing span'ів, зробити їхню агрегацію стає не таким простим завданням. Є офіційний спосіб для цього: spark-dependencies. Проте він займе годинник для побудови повного графа і змусить викачати з Jaeger весь dataset за минулу добу.

Якщо ви використовуєте Elasticsearch для зберігання tracing span'ів, можна скористатися простий утилітою на Golang, яка побудує такий же граф за хвилини, використовуючи особливості та можливості Elasticsearch.

Netramesh – легковажне service mesh рішення

Як використовувати Netramesh

Netra можна просто додати до будь-якого сервісу, який працює під управлінням будь-якого оркестратора. Можна подивитися приклад тут.

На даний момент у Netra немає можливості автоматичного впровадження sidecar'а до сервісів, але є плани на реалізацію.

Майбутнє Netramesh

Головною метою Netramesh є досягнення мінімальних витрат на ресурси та висока продуктивність, надаючи основні можливості для observability та контролю міжсервісної взаємодії.

У майбутньому Netramesh отримає підтримку інших протоколів прикладного рівня, крім HTTP. Найближчим часом з'явиться можливість L7 роутингу.

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

Джерело: habr.com

Додати коментар або відгук