Monitorizarea site-urilor clienților cu Prometheus și Grafana
În primii ani de freelancing, strategia mea de monitorizare era „aștept să-mi spună clientul că a picat ceva”. Nu mai era acceptabil cam de când am avut mai mult de cinci site-uri de îngrijit. Acum fiecare server de care mă ocup rulează Prometheus și Grafana și știu de probleme înainte să devină tichete de suport.
Iată setup-ul.
Arhitectura
Un server de monitorizare rulează Prometheus, Grafana și Alertmanager. Fiecare server de client rulează node_exporter (metrici de sistem) și blackbox_exporter (verificări de uptime). Aplicațiile PHP expun metrici proprii la un endpoint /metrics.
# monitoring-server docker-compose.yml
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=90d'
grafana:
image: grafana/grafana:latest
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
alertmanager:
image: prom/alertmanager:latest
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
ports:
- "9093:9093"
volumes:
prometheus_data:
grafana_data:
Configurarea Prometheus
# prometheus.yml
global:
scrape_interval: 30s
evaluation_interval: 30s
rule_files:
- "alerts/*.yml"
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
scrape_configs:
- job_name: 'node'
static_configs:
- targets:
- 'client1.example.com:9100'
- 'client2.example.com:9100'
relabel_configs:
- source_labels: [__address__]
regex: '([^:]+).*'
target_label: instance
- job_name: 'blackbox'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://client1.example.com
- https://client2.example.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox_exporter:9115
Regulile de alertare care chiar contează
# alerts/websites.yml
groups:
- name: websites
rules:
- alert: SiteDown
expr: probe_success == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} is unreachable"
- alert: SlowResponse
expr: probe_duration_seconds > 3
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} responding slowly ({{ $value | humanizeDuration }})"
- alert: SSLExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 14
labels:
severity: warning
annotations:
summary: "SSL certificate for {{ $labels.instance }} expires in less than 14 days"
- alert: HighDiskUsage
expr: (node_filesystem_size_bytes - node_filesystem_free_bytes) / node_filesystem_size_bytes > 0.85
for: 10m
labels:
severity: warning
annotations:
summary: "Disk usage above 85% on {{ $labels.instance }}"
Metrici PHP proprii în Yii2
Instalează clientul PHP pentru Prometheus și expune un endpoint:
composer require promphp/prometheus_client_php
// controllers/MetricsController.php
class MetricsController extends Controller
{
public function behaviors(): array
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
['allow' => true, 'ips' => ['monitoring.server.ip']],
],
],
];
}
public function actionIndex(): string
{
$registry = CollectorRegistry::getDefault();
// Track active jobs in queue
$gauge = $registry->getOrRegisterGauge('app', 'queue_size', 'Jobs in queue');
$gauge->set(Queue::find()->where(['status' => 'pending'])->count());
$renderer = new RenderTextFormat();
Yii::$app->response->format = Response::FORMAT_RAW;
Yii::$app->response->headers->set('Content-Type', RenderTextFormat::MIME_TYPE);
return $renderer->render($registry->getMetricFamilySamples());
}
}
Ce se schimbă când ai asta
În prima săptămână după ce am pus sistemul pe picioare, am prins un disc care se umplea pe serverul unui client, înainte să facă probleme. Săptămâna următoare am prins un certificat SSL pe care cineva uitase să-l reînnoiască. Niciunul dintre clienți n-a știut vreodată că a fost ceva. Exact asta e ideea.
Alertele pe Slack îmi arată problemele în două minute, pe telefon, fără să trebuiască să le raporteze cineva. Pentru un freelancer cu mai mulți clienți, asta e diferența dintre a reacționa după fapt și a lucra profesionist.