Valentin Cojocaru Web developer · Cluj-Napoca EN

Valentin
Cojocaru

Construiesc aplicații web și mă ocup de serverul, DNS-ul și baza de date din spatele lor.

blog

DevOps 9 min de citit

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.

#Docker #Linux #Prometheus #Grafana #Monitoring