Why Monitoring?
- Detect issues early: Fix problems before users notice
- Understand performance: Identify bottlenecks
- Make informed decisions: Base decisions on data
Prometheus Architecture
Prometheus collects and stores metrics as time series data.
Key Components
- Prometheus Server: Scrapes and stores metrics
- Alertmanager: Handles alerting
- Pushgateway: For short-lived jobs
- Exporters: Expose metrics for various services
Installation
yaml
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
volumes:
prometheus_data:
grafana_data:Prometheus Configuration
yaml
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'myapp'
static_configs:
- targets: ['myapp:3000']
metrics_path: '/metrics'Metrics Types
Counter
javascript
// Only increases, never decreases
const requestCounter = new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'status']
});
requestCounter.inc({ method: 'GET', status: '200' });Gauge
javascript
// Can go up or down
const activeConnections = new Gauge({
name: 'active_connections',
help: 'Number of active connections'
});
activeConnections.set(42);
activeConnections.inc();
activeConnections.dec();Histogram
javascript
// Observes values and counts them in configurable buckets
const requestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration',
buckets: [0.1, 0.5, 1, 2, 5]
});
requestDuration.observe(0.3);Summary
javascript
// Similar to histogram but calculates quantiles on client side
const responseSize = new Summary({
name: 'http_response_size_bytes',
help: 'HTTP response size'
});
responseSize.observe(1024);Node.js Integration
javascript
import express from 'express';
import promClient from 'prom-client';
const app = express();
const register = new promClient.Registry();
// Default metrics (CPU, memory, etc.)
promClient.collectDefaultMetrics({ register });
// Custom metrics
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration',
registers: [register]
});
// Metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
// Use in middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestDuration.observe(duration);
});
next();
});Grafana Dashboards
Adding Prometheus Data Source
- 1Open Grafana
- 2Configuration → Data Sources → Add data source
- 3Select Prometheus
- 4Set URL to http://prometheus:9090
- 5Save & Test
Sample Dashboard Queries
promql
# Request rate
rate(http_requests_total[5m])
# Error rate
rate(http_requests_total{status=~"5.."}[5m])
# P95 latency
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# CPU usage
rate(process_cpu_seconds_total[5m]) * 100
# Memory usage
process_resident_memory_bytes / 1024 / 1024Alerting
Alertmanager Configuration
yaml
# alertmanager.yml
global:
resolve_timeout: 5m
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 10s
group_interval: 10s
repeat_interval: 12h
receiver: 'default'
routes:
- match:
severity: critical
receiver: 'pagerduty'
receivers:
- name: 'default'
email_configs:
- to: 'team@example.com'
- name: 'pagerduty'
pagerduty_configs:
- service_key: '<service-key>'Alert Rules
yaml
# alerts.yml
groups:
- name: api
interval: 30s
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value }} errors/sec"
- alert: HighLatency
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "High latency detected"
description: "P95 latency is {{ $value }} seconds"Best Practices
- 1Use meaningful labels: Make queries flexible
- 2Set appropriate scrape intervals: Balance accuracy and performance
- 3Monitor Prometheus: Ensure monitoring system is healthy
- 4Use recording rules: Pre-compute expensive queries
- 5Test alerts: Verify alerts work as expected
- 6Document dashboards: Help team understand metrics
- 7Review metrics regularly: Remove unused metrics
Recording Rules
yaml
groups:
- name: api_rules
interval: 30s
rules:
- record: job:http_requests_total:rate5m
expr: sum(rate(http_requests_total[5m])) by (job)
- record: job:request_errors:rate5m
expr: sum(rate(http_requests_total{status=~"5.."}[5m])) by (job)Conclusion
Prometheus and Grafana provide powerful monitoring capabilities. Start with core metrics and expand based on needs.