Розподілене трасування: ми все робили не так

Прим. перев.: Автор цього матеріалу - Cindy Sridharan, інженер з компанії imgix, що займається питаннями розробки API і, зокрема, тестування мікросервісів. У цьому матеріалі вона ділиться своїм розгорнутим баченням актуальних проблем у галузі розподіленого трасування, де, на її думку, спостерігається нестача по-справжньому ефективних інструментів для вирішення нагальних завдань.

Розподілене трасування: ми все робили не так
[Ілюстрація запозичена з іншого матеріалу про розподілене трасування.]

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

Головна труднощі з розподіленим трасуванням — це не збір даних, не стандартизація форматів розповсюдження та подання результатів і не визначення того, коли, де і як робити вибірку. Я зовсім не намагаюся уявити тривіальними ці «проблеми із засвоюваністю» — насправді, існують досить значущі технічні та (якщо ми розглядаємо по-справжньому Open Source'ні стандарти та протоколи) політичні виклики, які необхідно подолати, щоб ці проблеми можна було вважати вирішеними.

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

Таке несхоже трасування

Розподілене трасування включає кілька розрізнених компонентів:

  • оснащення додатків та middleware засобами контролю;
  • передача розподіленого контексту;
  • збір трасування;
  • зберігання трасування;
  • їх вилучення та візуалізація.

Безліч розмов про розподілене трасування зводяться до того, щоб розглядати її як якусь унарну операцію, єдиною метою якої є допомога у повній діагностиці системи. Багато в чому це пов'язано з тим, як історично формувалися уявлення про розподілене трасування. У записи у блозіЗроблено, коли відкривалися вихідники Zipkin, згадувалося, що він [Zipkin] робить Twitter швидше. Перші комерційні пропозиції для трасування також просувалися як інструменти APM.

Прим. перев.: Щоб подальший текст сприймався краще, визначимо два базові терміни згідно документації проекту OpenTracing:

  • Проліт - Базовий елемент розподіленого трасування. Є описом якогось робочого процесу (наприклад, запиту до бази даних) з назвою, часом початку та закінчення, тегами, логами та контекстом.
  • Span'и зазвичай містять посилання на інші span'и, що дозволяє об'єднувати безліч span'ів у Трасування - Візуалізацію життя запиту в процесі його переміщення по розподіленій системі.

Trace містять неймовірно цінні дані, здатні допомогти в таких завданнях, як: тестування в production, проведення тестів аварійного відновлення, тестування з впровадженням помилок і т.д. Насправді деякі компанії вже використовують трасування для таких цілей. Почнемо з того що універсальна передача контексту має й інші застосування крім простого перенесення span'ів у систему зберігання:

  • Наприклад, Uber використовує результати трасування для розмежування тестового трафіку та production-трафіку.
  • Facebook використовує дані trace'ів для аналізу критичного шляху та для перемикання трафіку під час регулярних тестів аварійного відновлення.
  • Також соцмережа застосовує блокноти Jupyter, що дозволяють розробникам виконувати довільні запити на результатах трасування.
  • Прихильники LDFI (Lineage Driven Failure Injection) використовують розподілені trace'и для тестування із впровадженням помилок.

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

Коли справа все ж доходить до сценарію налагодження, первинним інтерфейсом залишається діаграма traceview (хоча деякі також називають її «Діаграмою Ганта» або "каскадною діаграмою"). Під traceview я маю на увазі всі span'и та супутні метадані, які разом складають trace. Кожна система трасування з відкритим вихідним кодом, а також кожне комерційне рішення для трасування пропонує заснований на traceview інтерфейс для візуалізації, деталізації та фільтрації trace'ів.

Проблема з усіма системами трасування, з якими мені довелося ознайомитись на даний момент, полягає в тому, що підсумкова візуалізація (traceview) практично повністю відбиває особливості процесу генерації trace'а. Навіть коли пропонуються альтернативні візуалізації: карти інтенсивності (heatmap), топології сервісів, гістограми затримок (latency) — зрештою вони все одно зводяться до traceview.

У минулому я нарікала на те, що більшість «інновацій» у галузі трасування щодо UI/UX, здається, обмежуються включенням додаткових метаданих у trace, вкладенням у них інформації з високою кардинальністю (high-cardinality) або надання можливості деталізувати конкретні span'и або виконувати запити між- та всередині-trace. При цьому traceview залишається основним засобом візуалізації. Поки зберігатиметься подібний стан речей, розподілене трасування (в кращому разі) займатиме 4-е місце як налагоджувальний інструмент, слідом за метриками, логами та stack trace'ами, а в гіршому — виявиться марною втратою грошей і часу.

Проблема з traceview

Призначення traceview - надавати повну картину пересування окремого запиту по всіх компонентах розподіленої системи, до яких він має відношення. Деякі більш просунуті системи трасування дозволяють деталізувати окремі span'и та переглядати розбивку за часом всередині одного процесу (коли span'и мають функціональні межі).

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

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

Однак traceview є саме це. Так, деякі системи трасування пропонують стислі traceview, коли число span'ів у trace настільки велике, що їх неможливо відобразити в рамках однієї візуалізації. Однак через великий обсяг інформації, що міститься навіть у такій урізаній візуалізації, інженери однаково змушені "просіювати" її, вручну звужуючи вибірку до набору сервісів-джерел проблем. На жаль, на цій ниві машини значно швидше людини, менш схильні до помилок, а їх результати більш повторювані.

Ще одна причина, через яку я вважаю метод traceview неправильним, пов'язана з тим, що він погано підходить для налагодження на основі гіпотез. У своїй основі налагодження - це ітеративний процес, що починається з гіпотези, за якою слідують перевірка різних спостережень та фактів, отриманих від системи за різними векторами, висновки/узагальнення та подальша оцінка істинності гіпотези.

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

На жаль, traceview не можна назвати інструментом з інтерактивним інтерфейсом. Найкраще, на що можна сподіватися при його використанні, — виявити джерело підвищених затримок і переглянути всілякі теги та логи, пов'язані з ним. Це не допомагає інженеру виявити закономірності у трафіку, такі як специфіка розподілу затримок, або виявити кореляції між різними вимірами. Узагальнений аналіз trace'ів може допомогти обійти деякі з цих проблем. Справді, є приклади успішного аналізу з використанням машинного навчання з виявлення аномальних span'ів та ідентифікації підмножини тегів, які можуть бути пов'язані з аномальною поведінкою. Тим не менш, мені поки не зустрічалися переконливі візуалізації знахідок, зроблених за допомогою машинного навчання або аналізу даних, застосованих до span'ів, які значно відрізнялися б від traceview або DAG (спрямованого ациклічного графа).

Span'и надто низькорівневі

Фундаментальна проблема з traceview в тому, що span'и є занадто низькорівневими примітивами як аналізу затримок (latency), так аналізу вихідних причин. Це все одно, що аналізувати окремі команди процесора в спробі усунути виняток, знаючи, що існують набагато більш високорівневі інструменти на кшталт backtrace, працювати з якими значно зручніше.

Більше того, я візьму на себе сміливість стверджувати таке: в ідеалі нам зовсім не потрібна повна картина події, що відбулися під час життєвого циклу, яку представляють сучасні інструменти для трасування. Натомість потрібна якась форма абстракції вищого рівня, що містить відомості про те, що пішло не так (за аналогією з backtrace), разом із деяким контекстом. Замість спостерігати весь trace, я волію бачити його частина, де відбувається щось цікаве чи незвичайне. В даний час пошук здійснюється вручну: інженер отримує trace і самостійно аналізує span'и у пошуках чогось цікавого. Підхід, коли люди витріщаються на span'и в окремих trace'ах в надії виявити підозрілу активність, абсолютно не масштабується (особливо коли їм доводиться осмислювати всі метадані, закодовані в різних span'ах, такі як span ID, ім'я методу RPC, тривалість span я, логи, теги і т.д.).

Альтернативи traceview

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

Я не візуальний дизайнер і не фахівець в галузі UX, однак у наступному розділі хочу поділитися кількома ідеями про те, як можуть виглядати такі візуалізації.

Фокус на конкретних сервісах

В умовах, коли галузь консолідується навколо ідей SLO (service level objectives) та SLI (service level indicators), Здається розумним, що окремі команди повинні в першу чергу стежити за відповідністю їхніх сервісів цим цілям. З цього виходить що орієнтована на сервіс візуалізація найкраще підходить для таких команд.

Trace'и, особливо без вибірки, є джерелом інформації про кожен компонент розподіленої системи. Цю інформацію можна згодувати хитрому обробнику, який постачатиме користувачам орієнтовані обслуговування знахідки.Вони можуть бути виявлені заздалегідь - ще до того, як користувач глянув на trace'и:

  1. Діаграми розподілу затримок тільки для запитів, що сильно виділяються. (outlier requests);
  2. Діаграми розподілу затримок для випадків, коли SLO цілі сервісу не досягаються;
  3. Найбільш «загальні», «цікаві» та «дивні» теги в запитах, які найчастіше повторюються;
  4. Розбивка затримок для випадків, коли залежно сервісу не досягають поставлених SLO-цілей;
  5. Розбивка затримок з різних нижчих (downstream) сервісів.

На деякі з цих питань вбудовані метрики просто не можуть відповісти, змушуючи користувачів ретельно вивчати span'и. У результаті ми маємо надзвичайно ворожий до користувача механізм.

У зв'язку з цим постає питання: як щодо комплексних взаємодій між різноманітними сервісами, контрольованими різними командами? Хіба traceview Чи не вважається найбільш підходящим інструментом для висвітлення такої ситуації?

Мобільні розробники, власники stateless-сервісів, власники керованих stateful-сервісів (на зразок баз даних) та власники платформ можуть бути зацікавлені в іншому поданні розподіленої системи; traceview — це надто універсальне рішення для цих докорінно відмінних потреб. Навіть у дуже складній мікросервісній архітектурі власникам сервісу не потрібні глибокі знання більш ніж двох-трьох upstream- та downstream-сервісів. По суті, в більшості сценаріїв користувачам достатньо відповідати на питання, що стосуються обмеженого набору сервісів.

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

Підхід, що просувається мною, - це повна протилежність до підходу «згори вниз», заснованому на traceview, коли аналіз починається з усього trace'а, а потім поступово спускається до окремих span'ів. Навпаки, підхід знизу вгору починається з аналізу невеликої ділянки, близької до потенційної причини інциденту, а потім простір пошуку розширюється при необхідності (з можливим залученням інших команд для аналізу ширшого спектра сервісів). Другий підхід краще пристосований швидкої перевірки початкових гіпотез. Після отримання конкретних результатів можна буде переходити до більш цілеспрямованого та детального аналізу.

Побудова топології

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

Побудова топології сервісів може допомогти при з'ясуванні, який сервіс демонструє сплеск частоти помилок чи збільшення затримки, що призводять до помітного погіршення роботи сервісу. Говорячи про побудову топології, я маю на увазі не карту сервісів, що відображає кожен наявний у системі сервіс і відому своїми картами архітектур у формі зірки смерті. Таке уявлення нічим не краще, ніж traceview на основі спрямованого ациклічного графа. Натомість мені хотілося б бачити топологію сервісів, що динамічно генерується, засновану на певних атрибутах, таких як частота помилок, час відгуку або будь-якому заданому користувачем параметрі, який допомагає прояснити ситуацію з конкретними підозрілими сервісами.

Давайте звернемося до прикладу. Уявімо собі гіпотетичний новинний сайт. Сервіс головної сторінки (front page) обмінюється даними з Redis, із сервісом рекомендацій, з рекламним сервісом та відеосервісом. Відеосервіс бере відеоролики з S3, а метадані з DynamoDB. Сервіс рекомендацій отримує метадані з DynamoDB, завантажує дані з Redis та MySQL, пише повідомлення в Kafka. Рекламний сервіс отримує дані з MySQL і пише повідомлення Kafka.

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

Розподілене трасування: ми все робили не так
Схема сервісів гіпотетичного новинного сайту

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

Розподілене трасування: ми все робили не так
Динамічна топологія, що відображає лише «цікаві» сервіси

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

Порівняльне відображення

Ще однією корисною візуалізацією буде порівняльне відображення. В даний час trace'и не надто добре підходять для порівняння пліч-о-пліч, тому зазвичай порівнюються span'и. А основна ідея цієї статті якраз і полягає в тому, що span'и надто низькорівневі, щоб отримувати найціннішу інформацію з результатів трасування.

Порівняння двох trace'ів не потребує принципово нових візуалізацій. Насправді досить чогось на кшталт гістограми, що представляє ту ж інформацію, що й traceview. Дивно, але навіть цей простий метод може дати набагато більше плодів, ніж просте вивчення двох trace'ів окремо. Ще потужнішою стала б можливість візуалізувати порівняння trace'ів в сукупності. Було б дуже корисно побачити, як нещодавно розгорнута зміна конфігурації бази даних із включенням GC (складання сміття) впливає на час відгуку downstream-сервісу в масштабі кількох годин. Якщо те, що я описую тут, здається схожим на А/В-аналіз впливу інфраструктурних змін у безлічі сервісів за допомогою результатів трасування, то ви не надто далекі від істини.

Висновок

Я не ставлю під сумнів корисність самого трасування. Щиро вірю, що немає іншого методу збирати настільки ж багаті, казуальні та контекстуальні дані, як ті, що містяться у trace'і. Однак, я також вважаю, що всі рішення для трасування використовують ці дані надзвичайно неефективно. Доки інструменти для трасування будуть зациклені на traceview-представленні, вони будуть обмежені в можливості максимально використовувати цінну інформацію, яку можна витягти з даних, що містяться в trace'ах. Крім того, існує ризик подальшого розвитку абсолютно недружнього та неінтуїтивного візуального інтерфейсу, який сильно обмежить здатність користувача усувати помилки у додатку.

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

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

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

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

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