Ни для кого не секрет что для устойчивой и надежной работы node.js приложений необходимо проводить мониторинг их работы и делать полезные выводы глядя на их метрики. Это означает, что вы способны получать информацию о состоянии до возникновения проблем, таким образом, предотвращая сбои.
В этой статье я хотел бы рассказать о способе сбора статистики из node.js приложений, которые запущены в PM2, и экспорт этих данных в Prometheus.
Когда вы просто запускаете node.js приложение через команду node app.js или же через PM2 командой pm2 start app.js, то новое приложение поднимается в единственном экземпляре. Сбор и экспорт каких либо метрик в этом случае будет не затруднительным. Для этого устанавливаем пакет prom-client и добавляем нужные нам метрики, например, количество обращений к нашему приложению.
import client from 'prom-client'; import { createServer } from 'http'; const registry = new client.Registry(); const PREFIX = `nodejs_app_`; export const metricRequestsTotal = new client.Counter({ name: `${PREFIX}request_counter`, help: 'Show total request count', registers: [registry], }); // Старт вашего приложения // ... nodejsapp.get('/*', async (req, res) => { metricRequestsTotal.inc(); }); // Запуск сервера для отдачи метрик const promServer = createServer(async (req, res) => { res.setHeader('Content-Type', registry.contentType); res.end(await registry.metrics()); return; }); promServer.listen(9100, () => console.log('Prom server started') );
В данном примере запускается ваше приложение и вместе с ним стартует экспорт метрик на дополнительном порту 9100. Можно использовать какой-то отдельный URL и на основном порту приложения, но он не должен быть публичным для пользователей.
Такой способ будет работать до тех пор, пока вам не нужно будет запустить несколько инстансов приложения в режиме кластера. Для сбора метрик в таком режиме у prom-client есть хороший пример, где происходит агрегация метрик. Но как быть, если вы запускаете приложение через менеджер процессов PM2 в котором у вас нет доступа к запущенному кластеру?
В одной из моих предыдущих статей я рассказал о модуле pm2-prom-module который позволяет собирать общую статистику по приложениям из PM2, но не мог собирать внутреннюю статистику из каждого приложения. Теперь же, начиная с версии 2.0, такого ограничения нет и вся статистика из PM2 и внутри приложения отдается через один модуль.
Для обмена данными между приложением nodejs и pm2-prom-module необходимо установить npm пакет pm2-prom-module-client в ваше приложение. В итоге пример выше будет выглядеть вот таким образом:
import client from 'prom-client'; import { initMetrics } from 'pm2-prom-module-client'; const registry = new client.Registry(); const PREFIX = `nodejs_app_`; const metricRequestCounter = new client.Counter({ name: `${PREFIX}request_counter`, help: 'Show total request count', registers: [registry], }); // Регистрируем registry для отправки данных в модуль initMetrics(registry); // ... nodejsapp.get('/*', async (req, res) => { // ... metricRequestCounter?.inc(); // ... });
Теперь pm2-prom-module будет отдавать не только PM2 статистику, но и внутреннюю статистику из ваших приложений.
/// Общая статистика PM2 # HELP pm2_free_memory Show available host free memory # TYPE pm2_free_memory gauge pm2_free_memory{serviceName="my-app"} 377147392 # HELP pm2_cpu_count Show available CPUs count # TYPE pm2_cpu_count gauge pm2_cpu_count{serviceName="my-app"} 4 # HELP pm2_available_apps Show available apps to monitor # TYPE pm2_available_apps gauge pm2_available_apps{serviceName="my-app"} 2 /// Статистика из приложений # HELP nodejs_app_request_counter Show total request count # TYPE nodejs_app_request_counter counter nodejs_app_request_counter{app="app1",serviceName="my-app"} 10 nodejs_app_request_counter{app="app2",serviceName="my-app"} 17
По-умолчанию вся статистика агрегируется по всем запущенным инстансам, но если вы хотите детальную статистику по каждому инстансу, то это можно включить через параметр конфигурации модуля:
pm2 set pm2-prom-module:aggregate_app_metrics false
Этот модуль, в отличие от некоторых других, позволяет собирать метрики по каждому приложению отдельно и они не пересекаются.
В целом, статистика, которая отдается PM2 достаточна для базового мониторинга приложений, но в нашем случае, например, нужно было больше детальных данных по времени рендера страниц или загрузки некоторых блоков данных. Для таких целей мы использовали гистограмму:
// ... export const metricRequestTime = new client.Histogram({ name: 'nodejs_app_page_execute', help: 'Time to processing request', registers: [registry], buckets: [0.1, 0.2, 0.3, 0.5, 0.7, 1, 2, 3, 5], labelNames: ['code', 'page'], }); // ... const endMetricRequestTime = metricRequestTime.startTimer(); // ... endMetricRequestTime({ code: 200, page: 'Homepage });
В данном примере мы используем 2 дополнительных параметра code (код ответа) и page (идентификатор страницы) благодаря которым предоставляется дополнительная статистика, например, можно построить графики в Grafana:
-
Какие страницы чаще всего запрашивают
sum(rate(nodejs_app_page_execute_count{serviceName="$serviceName",code="200"}[5m])) by (page) -
Количество ошибок в ответах сервиса (коды ответов)
sum(rate(nodejs_app_page_execute_count{serviceName="$serviceName"}[5m])) by (code) -
Время загрузки каждой страницы указывая, например, 99 перцентиль
histogram_quantile(0.99, sum(rate(nodejs_app_page_execute_bucket{serviceName="$serviceName", code="200"}[1m])) by (page, le)) -
И множество других графиков по каждой отдельной странице
Кстати, я не советую использовать URL страницы как значение page — это увеличит во много раз использование памяти и размер ответа в Prometheus и как следствие — неразбериху в графиках. Лучше определять какая страница загрузилась и использовать ее идентификатор.
В итоге мы получаем единственную точку сбора и отдачи статистики по всем приложениям запущенным в PM2 и систему мониторинга построенную на базе Prometheus и функциональные дашбоарды в Grafana.
Спасибо, что дочитали до конца.
ссылка на оригинал статьи https://habr.com/ru/articles/794046/
Добавить комментарий