Перший раз, коли вам справді потрібна спостережливість, це не тоді, коли ви спокійно дивитеся на приладову панель. Коли користувач пише «оплата йде повільно», графік помилок виглядає нормально, а в журналах ви знайдете лише рядок відключених повідомлень.
OpenTelemetry було створено, щоб уникнути цього моменту: не для того, щоб мати більше графіки, а для того, щоб з’єднати частини. Запит надходить у API, викликає базу даних, проходить через зовнішнього постачальника, публікує завдання в черзі та, можливо, завершує роботу трьох служб пізніше. Без розподіленого трасування ви реконструюєте цю історію вручну. З OpenTelemetry у вас принаймні є карта.
Справа не в trace, а в історії
trace є послідовністю span. У такому вигляді це звучить холодно. На практиці кожен span є частиною історії: POST /checkout, SELECT inventory, call payment provider, publish order.created.
Цінність приходить, коли ви починаєте відповідати на реальні запитання:
- який зовнішній сервіс сповільнюється?
- помилки походять від певної версії?
- проблема стосується всіх чи лише одного орендаря?
- повторна спроба приховує тайм-аут?
- асинхронне завдання починається, але потім вмирає в іншому місці?
Ці питання неможливо вирішити console.log, кинутою поспішно. Дійсно, часто журнал, доданий в екстреній ситуації, допомагає вам сьогодні, а завтра стає шумом.
Як розмістити це в додатку Node.js
Найкраще налаштування просте: додаток створює телеметрію, Collector вирішує, куди її надіслати.
Node.js app -> OpenTelemetry Collector -> backend di observability
Чому б не експортувати безпосередньо до постачальника? Оскільки спочатку це здається швидшим, потім ви розумієте, що кожна служба має різні конфігурації, різні повторні спроби, різні фільтри та немає центральної точки для видалення конфіденційних даних або зміни призначення.
Collector нудний у всіх правильних аспектах. Він отримує OTLP, виконує пакетування, може фільтрувати, може робити вибірку, може додавати загальні атрибути та може експортувати до кількох систем.
Власне інструментування: добре, але недостатньо
У Node.js я б почав з автоматичного інструментування. Це дає вам миттєве бачення HTTP, підтримуваних фреймворків, баз даних і загальних бібліотек.
npm install @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-trace-otlp-http
Потім ви ініціалізуєте SDK перед рештою програми:
import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, }), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();
Однак це бачить структуру, а не ваш продукт. Він знає, що ви зробили запит, але не знає, що запит був у «створити порядок» або «оновити підписку». Для цього вам знадобиться span посібників у точках, де враховується домінування.
const span = tracer.startSpan('checkout.create_order'); try { span.setAttribute('cart.items_count', input.items.length); const order = await createOrder(input); span.setAttribute('order.id', order.id); return order; } catch (error) { span.recordException(error as Error); throw error; } finally { span.end(); }
Я б нікуди не клав посібники span. Я б поклав їх туди, де о третій ночі хотів би зрозуміти, що сталося, не читаючи половини кодової бази.
Три правила, які допоможуть уникнути хаосу
Перше правило: кожна служба повинна мати service.name, середовище та версію. Це здається тривіальним, але без цих атрибутів trace набагато менш корисний. Коли розгортання щось порушує, ви хочете відфільтрувати за версією за дві секунди.
Друге правило: не розміщуйте конфіденційні дані в атрибутах. Електронні листи, токени, цілочисельні дані та адреси не повинні випадково опинитися в системі спостереження. Якщо вам потрібно ідентифікувати користувача, розгляньте внутрішні ідентифікатори, хешування або менш конфіденційні поля.
Правило третє: звертайте увагу на потужність. user.id як атрибут trace може мати сенс. Як ярлик показника він може знищити ваші витрати та продуктивність.
Показники: мало, але добре
Я б почав із дуже практичних показників:
- частоти, похибки та тривалість запитів;
- латентність зовнішніх залежностей;
- кількість тайм-аутів і повторних спроб;
- глибина хвостиків;
- тривалість роботи;
- відсоток помилок на версію.
Решта додається при необхідності. Інформаційні панелі, наповнені графіками, на які ніхто не дивиться, — це меблі, а не можливість спостереження.
Журнали: все ще корисні, але пов’язані
Журнали не зникають. Вони просто стають набагато кориснішими, коли носять trace_id та span_id. Отже, ви можете почати з журналу помилок і відкрити trace або почати з повільного trace і прочитати лише журнали, створені на цьому шляху.
Без кореляції ви шукаєте голки. Завдяки кореляції ви принаймні знаєте, у яку шухляду шукати.
Контрольний список, яким я б скористався, перш ніж сказати "ми охоплені"
- trace фактично перетинає кілька послуг.
- Журнали включають
trace_idтаspan_id. - Collector налаштовано з обмеженнями пакетування та пам’яті.
- Помилки записуються в span. – Існує політика відбору проб.
- Метрики мають керовану потужність.
- Конфіденційні дані фільтруються.
- Сповіщення починаються з симптомів користувача, а не з випадкових графіків.
Висновок
OpenTelemetry самостійно не вирішує виробничі проблеми. Але те, як ви з ними справляєтеся, змінюється. Замість того, щоб сліпо додавати журнали, ви починаєте слідувати фактичному шляху запиту.
Для мене ознака того, що це працює, проста: коли щось стається, команда перестає запитувати «куди ми дивимося?» і починає запитувати "чому цей твір повільний?". Ось де спостережливість стає інструментом, а не набором інформаційних панелей.