Пошук уроків, статей та іншого контенту
Розгляньте метрики системи й застосунку, способи їх збору, агрегації та вибору корисних вимірювань.
Метрика — це числове вимірювання, яке допомагає зрозуміти стан системи або застосунку в певний момент чи за певний проміжок часу.
Приклади:
кількість HTTP-запитів;
час відповіді API;
кількість помилок;
використання CPU;
обсяг зайнятої пам’яті;
кількість активних користувачів;
довжина черги повідомлень.
Метрики потрібні, щоб:
швидко виявляти проблеми;
оцінювати продуктивність;
знаходити тенденції;
перевіряти вплив змін у коді;
планувати ресурси;
контролювати дотримання вимог до доступності та швидкодії.
Важливо відрізняти метрики від логів і трасувань:
метрики показують числову картину стану системи;
логи містять окремі події та деталі;
трасування показує шлях конкретного запиту через компоненти системи.
У цій темі зосередимося саме на метриках.
Counter — лічильник, який збільшується і не зменшується протягом життєвого циклу процесу.
Приклади:
кількість отриманих запитів;
кількість помилок;
кількість оброблених повідомлень;
кількість успішних платежів.
З абсолютного значення лічильника зазвичай не роблять висновків. Кориснішою є швидкість його зростання:
помилок за хвилину;
запитів за секунду;
оброблених повідомлень за секунду.
Gauge — значення, яке може як збільшуватися, так і зменшуватися.
Приклади:
поточна кількість активних запитів;
використання пам’яті;
температура сервера;
кількість елементів у черзі;
кількість підключених користувачів.
Gauge показує стан у конкретний момент.
Histogram збирає розподіл значень, найчастіше часу відповіді або розміру запиту.
Замість одного середнього значення можна оцінити:
медіану;
90-й перцентиль;
95-й перцентиль;
99-й перцентиль;
частку запитів, швидших за певний поріг.
Наприклад, середній час відповіді може бути 100 мс, але частина запитів триватиме 5 секунд. Середнє значення приховає цю проблему, а перцентиль її покаже.
Summary також використовується для вимірювання розподілу значень, але зазвичай обчислює перцентилі на стороні застосунку.
На початковому етапі достатньо запам’ятати практичне правило:
для кількості подій використовуйте counter;
для поточного стану — gauge;
для часу, розміру та інших розподілів — histogram.
Метрики застосунку описують поведінку бізнес-логіки та API.
Корисні приклади:
кількість HTTP-запитів;
кількість відповідей із помилками;
час обробки запитів;
кількість успішних і невдалих операцій;
кількість повідомлень у черзі;
час виконання важливої операції;
кількість підключень до бази даних.
Для HTTP-сервісу часто використовують три групи показників:
Потрібно знати:
скільки запитів надходить;
до яких маршрутів;
із якими HTTP-методами;
з якими кодами відповідей.
Наприклад, корисною є метрика кількості запитів із мітками:
http_requests_total{method="GET", route="/users", status="200"}Варто вимірювати не лише загальну кількість помилок, а й частку помилкових запитів:
error_rate = кількість помилок / загальна кількість запитівЧастка помилок зазвичай корисніша за абсолютне число. Десять помилок можуть бути критичною проблемою, якщо за цей час було лише сто запитів, але можуть бути непомітними серед мільйона успішних запитів.
Затримка — це час від отримання запиту до відправлення відповіді.
Її потрібно аналізувати для важливих маршрутів і операцій. Наприклад:
GET /products — 95-й перцентиль 180 мс;
POST /orders — 95-й перцентиль 700 мс;
GET /reports — 95-й перцентиль 4 секунди.
Окремо вимірюйте успішні та помилкові запити, якщо помилки можуть мати іншу тривалість.
Метрики інфраструктури показують, чи вистачає системі ресурсів.
Основні групи:
завантаження процесора;
використання оперативної пам’яті;
вільний простір на диску;
мережевий трафік;
кількість мережевих помилок;
кількість процесів і потоків;
стан контейнерів або віртуальних машин.
Для бази даних додатково важливі:
кількість активних з’єднань;
кількість помилок з’єднання;
час виконання запитів;
кількість очікуваних або заблокованих операцій;
розмір сховища.
Інфраструктурні метрики відповідають на питання «чи достатньо ресурсів?», а метрики застосунку — «чи працює функціональність і як її бачить користувач?». Для діагностики потрібні обидва типи.
Застосунок сам збільшує лічильники, оновлює поточні значення та вимірює тривалість операцій.
Наприклад, під час обробки HTTP-запиту можна:
збільшити лічильник запитів;
збільшити кількість активних запитів;
запам’ятати час початку;
після завершення записати статус і тривалість;
зменшити кількість активних запитів.
Перевага цього способу — можна вимірювати саме бізнесові події, про які операційна система нічого не знає.
Спеціальний агент може збирати системні показники:
CPU;
пам’ять;
диск;
мережу;
стан процесів.
Експортер може перетворювати статистику сторонньої системи у формат, придатний для збору. Наприклад, окремий експортер може надавати метрики бази даних.
Існують два поширені підходи:
pull — система моніторингу періодично запитує метрики в застосунку;
push — застосунок або агент надсилає метрики до системи моніторингу.
Для pull-підходу застосунок часто має спеціальний endpoint, наприклад /metrics. Він не повинен виконувати бізнесову операцію — лише повертати поточні метрики.
Нижче наведено самодостатній Node.js-сервер без сторонніх бібліотек. Він:
обробляє endpoint /hello;
рахує загальну кількість запитів;
рахує помилки;
вимірює час відповіді;
відстежує активні запити;
надає метрики через /metrics.
const http = require('node:http');
const metrics = {
requestsTotal: 0,
errorsTotal: 0,
activeRequests: 0,
durationsMs: [],
};
function observeDuration(durationMs) {
metrics.durationsMs.push(durationMs);
// Зберігаємо лише останні 1000 вимірювань.
if (metrics.durationsMs.length > 1000) {
metrics.durationsMs.shift();
}
}
function percentile(values, percentileValue) {
if (values.length === 0) {
return 0;
}
const sorted = [...values].sort((a, b) => a - b);
const index = Math.ceil((percentileValue / 100) * sorted.length) - 1;
return sorted[Math.max(0, index)];
}
function formatMetrics() {
const durations = metrics.durationsMs;
return [
'# HELP app_requests_total Загальна кількість HTTP-запитів',
'# TYPE app_requests_total counter',
`app_requests_total ${metrics.requestsTotal}`,
'# HELP app_errors_total Загальна кількість помилок',
'# TYPE app_errors_total counter',
`app_errors_total ${metrics.errorsTotal}`,
'# HELP app_active_requests Поточна кількість активних запитів',
'# TYPE app_active_requests gauge',
`app_active_requests ${metrics.activeRequests}`,
'# HELP app_request_duration_ms_95 95-й перцентиль тривалості запитів у мілісекундах',
'# TYPE app_request_duration_ms_95 gauge',
`app_request_duration_ms_95 ${percentile(durations, 95).toFixed(2)}`,
'',
].join('\n');
}
const server = http.createServer(async (request, response) => {
const startedAt = process.hrtime.bigint();
metrics.requestsTotal += 1;
metrics.activeRequests += 1;
try {
if (request.url === '/metrics') {
response.writeHead(200, {
'Content-Type': 'text/plain; version=0.0.4',
});
response.end(formatMetrics());
return;
}
if (request.url === '/hello') {
await new Promise((resolve) => setTimeout(resolve, 50));
response.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
});
response.end(JSON.stringify({ message: 'Привіт!' }));
return;
}
metrics.errorsTotal += 1;
response.writeHead(404, {
'Content-Type': 'application/json; charset=utf-8',
});
response.end(JSON.stringify({ error: 'Не знайдено' }));
} catch (error) {
metrics.errorsTotal += 1;
response.writeHead(500, {
'Content-Type': 'application/json; charset=utf-8',
});
response.end(JSON.stringify({ error: 'Внутрішня помилка' }));
} finally {
const finishedAt = process.hrtime.bigint();
const durationMs = Number(finishedAt - startedAt) / 1_000_000;
observeDuration(durationMs);
metrics.activeRequests -= 1;
}
});
server.listen(3000, () => {
console.log('Сервер запущено на http://localhost:3000');
});Запустіть файл командою:
node server.jsПісля цього:
відкрийте http://localhost:3000/hello;
відкрийте http://localhost:3000/unknown, щоб отримати помилку;
відкрийте http://localhost:3000/metrics, щоб переглянути зібрані значення.
Цей приклад демонструє основну ідею, але в реальній системі метрики зазвичай зберігаються не в пам’яті процесу. Після перезапуску сервера наведені значення буде втрачено. Також один процес не підходить для аналізу кількох екземплярів застосунку без подальшої агрегації.
Метрика стає значно кориснішою, якщо її можна розділити за невеликою кількістю важливих ознак. Такі ознаки називають мітками або labels.
Наприклад:
http_requests_total{
method="GET",
route="/users",
status="200"
}За такими мітками можна отримати:
усі запити до /users;
лише запити зі статусом 500;
кількість GET-запитів;
кількість помилок для конкретного маршруту.
Мітки мають бути стабільними та мати обмежену кількість можливих значень.
Зазвичай безпечно використовувати:
HTTP-метод;
нормалізований маршрут;
код відповіді;
середовище production або staging;
назву сервісу;
регіон.
Не варто використовувати як мітки:
ідентифікатор користувача;
email;
URL із довільними параметрами;
текст помилки;
ідентифікатор запиту;
timestamp;
довільний ідентифікатор замовлення.
Якщо кожне значення мітки унікальне, кількість окремих часових рядів швидко зростає. Це називають високою кардинальністю. Вона збільшує споживання пам’яті та ускладнює аналіз метрик.
Для маршрутів використовуйте шаблон маршруту:
/users/:idа не конкретний URL:
/users/184729Агрегація — це об’єднання вимірювань за певним критерієм.
Наприклад, окремі екземпляри сервісу можуть мати такі значення:
instance-1: 120 запитів
instance-2: 80 запитів
instance-3: 100 запитівЗагальна кількість запитів:
120 + 80 + 100 = 300Для різних типів метрик використовують різні операції:
counter зазвичай підсумовують;
gauge можуть підсумовувати, брати середнє, мінімум або максимум — залежно від змісту;
для latency аналізують розподіл і перцентилі, а не лише середнє значення.
Наприклад, кількість активних запитів у всіх екземплярах можна підсумувати. Але температуру серверів частіше аналізують окремо або за допомогою максимуму.
Не потрібно вимірювати абсолютно все. Надмірна кількість метрик ускладнює зберігання, пошук і підтримку.
Для кожної метрики поставте собі питання:
Яку проблему вона допоможе виявити?
Хто буде її використовувати?
Яка дія буде виконана після погіршення значення?
Який нормальний діапазон значень?
За який період потрібно аналізувати дані?
Якщо на ці питання немає відповіді, метрика, імовірно, не є корисною.
Для HTTP-сервісів зручно почати з трьох груп:
Rate — швидкість надходження запитів;
Errors — кількість або частка помилок;
Duration — тривалість обробки запитів.
Приклад мінімального набору:
запити за секунду для кожного важливого маршруту;
частка відповідей із кодами 4xx і 5xx;
50-й, 95-й або 99-й перцентиль часу відповіді.
Ці показники швидко дають відповідь на три основні питання:
скільки роботи виконує сервіс;
чи є проблеми;
наскільки швидко він працює.
Технічні метрики:
час відповіді;
кількість помилок;
активні запити;
використання пам’яті.
Бізнесові метрики:
кількість створених замовлень;
кількість успішних оплат;
кількість зареєстрованих користувачів;
кількість скасованих операцій.
Технічна метрика може бути нормальною, але бізнесова операція — невдалою. Наприклад, endpoint відповідає за 100 мс, але всі платежі відхиляються. Тому для важливих сценаріїв потрібно вимірювати і технічний результат, і бізнесовий.
Одна й та сама метрика може виглядати по-різному залежно від періоду.
Наприклад:
за останню хвилину було 20 помилок;
за останню годину — 25 помилок;
за добу — 100 помилок.
Короткі періоди допомагають швидко помітити сплеск. Довгі періоди показують тенденції та повторювані проблеми.
Під час аналізу корисно порівнювати:
поточний період із попереднім;
робочі години з нічним часом;
поточний день із тим самим днем минулого тижня;
значення до та після розгортання нової версії.
Водночас період має відповідати природі показника. Кілька секунд можуть бути достатніми для виявлення різкого зростання помилок, але недостатніми для оцінки щоденної кількості замовлень.
Для метрики можна визначити пороги:
нормальне значення;
попередження;
критичне значення.
Наприклад:
частка помилок менше 1% — нормальна;
від 1% до 5% — потрібна увага;
понад 5% — критична ситуація.
Пороги мають відповідати реальній поведінці системи. Якщо встановити їх надто низько, команда отримуватиме багато зайвих сповіщень. Якщо надто високо — проблема може залишитися непоміченою.
Корисне сповіщення повинно містити:
назву проблеми;
affected-сервіс або маршрут;
поточне значення;
період вимірювання;
приблизний вплив.
Збирання великої кількості чисел не гарантує спостережуваності. Кожна метрика має допомагати відповісти на конкретне питання або прийняти рішення.
Середнє значення може приховати повільні запити. Додавайте перцентилі, особливо 95-й і 99-й, якщо важлива поведінка повільної частини запитів.
Ідентифікатори користувачів, замовлень і запитів створюють надто багато часових рядів. Такі дані краще шукати в логах або трасуваннях.
Завжди чітко визначайте одиниці:
секунди або мілісекунди;
байти або мегабайти;
запити за секунду або запити за хвилину.
Назва метрики також має підказувати одиницю, наприклад request_duration_ms.
Кількість активних з’єднань — це gauge. Загальна кількість створених з’єднань — counter. Неправильний тип ускладнює інтерпретацію даних.
Якщо кожен URL із параметром записується як окремий маршрут, кількість часових рядів швидко зростає. Групуйте такі запити за шаблоном маршруту.
Нормальні CPU і пам’ять не означають, що користувачі успішно виконують операції. Вимірюйте також запити, помилки, затримки та важливі бізнесові події.
Метрики — це числові вимірювання стану системи або застосунку.
Основні типи метрик: counter, gauge та histogram.
Для HTTP-сервісів важливі кількість запитів, помилки та тривалість відповідей.
Інфраструктурні метрики показують стан ресурсів, а метрики застосунку — поведінку сервісу та бізнесових операцій.
Метрики можна збирати безпосередньо в застосунку або за допомогою агентів та експортерів.
Мітки допомагають групувати дані, але унікальні значення створюють проблему високої кардинальності.
Для агрегації counter зазвичай підсумовують, а latency аналізують за розподілом і перцентилями.
Корисна метрика має допомагати виявити проблему або прийняти рішення.
Мінімальний набір для сервісу можна побудувати за принципом RED: швидкість запитів, помилки та тривалість.