Мониторинг: Prometheus и Grafana
Prometheus — система сбора и хранения временных рядов метрик (CPU, memory, HTTP requests); Grafana — инструмент визуализации этих метрик в виде дашбордов и алертов.
Зачем нужно
Без мониторинга нет видимости в работу продакшн-системы: не знаешь, нормальна ли нагрузка, растёт ли error rate, не заканчивается ли диск. Prometheus + Grafana — open-source стандарт для метрик: Prometheus собирает данные, Grafana строит красивые дашборды и отправляет алерты.
Где используется
- Мониторинг Node.js API: запросы в секунду, latency, error rate
- Системные метрики серверов (CPU, RAM, disk) через node_exporter
- Мониторинг Kubernetes-кластера через kube-state-metrics
- Алерты при деградации производительности
Основной контент
Запуск через docker-compose
# docker-compose.monitoring.yml
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- promdata:/prometheus
ports:
- "9090:9090"
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.retention.time=30d
grafana:
image: grafana/grafana:latest
volumes:
- grafanadata:/var/lib/grafana
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
depends_on:
- prometheus
node-exporter: # системные метрики хоста
image: prom/node-exporter:latest
network_mode: host
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
volumes:
promdata:
grafanadata:
prometheus.yml — конфигурация сбора метрик
global:
scrape_interval: 15s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: [localhost:9090]
- job_name: node-exporter
static_configs:
- targets: [node-exporter:9100]
- job_name: myapp
static_configs:
- targets: [api:3000]
metrics_path: /metrics # эндпоинт приложения
Метрики в Node.js (prom-client)
npm install prom-client
const client = require('prom-client');
// Системные метрики (CPU, memory) — автоматически
const register = new client.Registry;
client.collectDefaultMetrics({ register });
// Кастомные метрики
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
registers: [register],
});
const httpRequestsTotal = new client.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status'],
registers: [register],
});
// Express middleware
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer;
res.on('finish', () => {
end({ method: req.method, route: req.route?.path || req.path, status: res.statusCode });
httpRequestsTotal.inc({ method: req.method, route: req.route?.path || req.path, status: res.statusCode });
});
next;
});
// Эндпоинт для Prometheus
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics);
});
PromQL — язык запросов
# Запросов в секунду
rate(http_requests_total[5m])
# Error rate (4xx + 5xx)
rate(http_requests_total{status=~"[45].."}[5m]) / rate(http_requests_total[5m])
# P99 latency
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# CPU usage
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
Частые ошибки
- Хранить Prometheus данные без persistence volume — все метрики теряются при перезапуске контейнера
- Не настроить retention period — данные накапливаются и заполняют диск
- Слишком маленький
scrape_interval(< 10s) — нагружает приложение частыми запросами к/metrics - Не защитить
/metricsendpoint — Nginx должен ограничивать доступ к нему (только внутренняя сеть)
Связанные темы
- _MOC DevOps
- Alerting -- настройка оповещений
- Health Checks -- проверка состояния
- Логирование -- ELK stack
- APM -- Application Performance Monitoring