Files
ENI-projet-piscine/docs/architecture/60-observabilite.md
Johan LEROY e53c7e441c feat(infra): déploie Garage par environnement, secrets par le .env, fumée S3 en CI
Reprend l'amorce de la PR 164 et l'intègre à la stack : service `garage` (dxflrs/garage v2.4.1,
`--single-node --default-bucket`) dans docker-compose.yml, garage.toml versionné sans secret,
ports sur 127.0.0.1 décalés par environnement dans provision-host.sh, garde des six clés
GARAGE_* dans le Makefile, cible Prometheus avec jeton, tests de fumée déplacés dans
tests/garage et joués par le job compose d'infra.yml contre le vrai conteneur, SSE-C compris.
Variables APP_S3_* et APP_READING_RETENTION_DAYS posées sur airflow-scheduler pour le DAG
`retention`. ADR 0019.

Closes #24
2026-09-24 10:30:04 +02:00

6.1 KiB

Observabilité

Ce document décrit ce qu'on voit du système en fonctionnement : métriques, tableaux de bord, alertes et journaux. Décision dans l'ADR 0016, mode d'emploi dans monitoring/README.md.

Brique Sert à Statut
Métriques de l'API Débit, erreurs et latences par route Fait
Collecte et alertes Prometheus, neuf règles testées, Alertmanager vers Mailpit Fait
Tableaux de bord Grafana : API, données et modèle, infrastructure Fait
Métriques d'Airflow StatsD ou OpenTelemetry des DAGs Cible
Journaux centralisés Loki ou équivalent Cible

Vue d'ensemble

flowchart LR
  subgraph projet["Projet Compose de la prod"]
    api["backend<br/>/metrics"]
    db[("db<br/>TimescaleDB")]
    mail["mailpit"]

    garage["garage<br/>:3903/metrics"]

    subgraph sup["Profil monitoring"]
      prom["prometheus<br/>15 s, 15 jours"]
      am["alertmanager"]
      graf["grafana"]
      pge["postgres-exporter"]
      node["node-exporter"]
      cad["cadvisor"]
    end
  end

  hote["Hôte : VM ENI<br/>recette et prod"]

  prom -->|"Bearer APP_METRICS_TOKEN"| api
  prom --> pge & node & cad
  prom -->|"Bearer GARAGE_METRICS_TOKEN"| garage
  pge -->|"rôle supervision"| db
  node -.->|"/proc, /sys"| hote
  cad -.->|"cgroups"| hote
  prom -->|"règles franchies"| am -->|"SMTP"| mail
  graf --> prom
  graf -->|"rôle supervision, SQL"| db

Tout vit dans le projet Compose de la prod, sur son réseau. La recette n'a pas de supervision propre. node-exporter et cAdvisor voient pourtant tout l'hôte : la mémoire de la VM et de chaque conteneur couvre donc aussi la recette, qu'on distingue au préfixe enervision-rec-.

Ce que mesure chaque source

Source Métriques utiles Où les lire
API (prometheus-fastapi-instrumentator) http_requests_total par route et classe de statut, http_request_duration_seconds par route (seaux 50 ms à 2,5 s), http_request_duration_highr_seconds global, mémoire du processus Tableau « API »
postgres-exporter pg_up, connexions par état, max_connections, transactions validées, taille des bases Tableau « Infrastructure »
node-exporter Processeur, mémoire disponible, espace disque de / Tableau « Infrastructure »
cAdvisor Mémoire (working_set) et processeur par conteneur Tableau « Infrastructure »
TimescaleDB, en SQL Fraîcheur des relevés par site, relevés ingérés par heure, alertes par sévérité, drift_report Tableau « Données et modèle »
Garage (/metrics du port admin, jeton GARAGE_METRICS_TOKEN) api_s3_request_counter, block_bytes_written, garage_local_disk_avail, cluster_healthy Prometheus seulement, aucun tableau dédié ; CibleInjoignable couvre son indisponibilité (ADR 0019)

Deux choix de l'instrumentation se lisent dans ces courbes :

  • Les sondes /health/* et /metrics ne sont pas comptées. La sonde Docker frappe toutes les 30 s : incluse, elle ferait baisser la latence moyenne et gonfler le débit d'une API au repos.
  • Les seaux par route encadrent 500 ms, seuil de charge de l'ADR 0015. Grafana lit ainsi le même p95 que k6 pendant un tir.

Alertes

Groupe Alertes Sévérité
API Indisponible 2 min, 5xx au-delà de 5 %, p95 au-delà d'une seconde critical, critical, warning
Base PostgreSQL injoignable 2 min, connexions au-delà de 80 % critical, warning
Hôte Mémoire au-delà de 90 %, disque sous 10 %, processeur au-delà de 90 % warning, critical, warning
Supervision Un exporteur muet 5 min warning
  • Tests des règles. Chaque règle a un cas dans monitoring/prometheus/tests/, joué par promtool test rules dans le job Infra de la CI. Une règle qui ne se déclenche plus, ou se déclenche à tort, casse la CI avant d'atteindre la prod.
  • Envoi. Alertmanager groupe les alertes par nom et sévérité et les envoie par courriel via Mailpit, qui les capture sans rien relayer. Un critical est rappelé toutes les heures, un warning toutes les douze. Un critical masque le warning de la même cible.

Sécurité

  • Aucune interface exposée. Prometheus, Alertmanager et Grafana n'écoutent que sur 127.0.0.1, et rien ne passe par le proxy (ADR 0007). Accès par tunnel SSH.
  • /metrics gardé par jeton. Il n'est pas routé par nginx, et Prometheus y présente APP_METRICS_TOKEN, que l'API exige dès qu'il est posé. Le jeton lui parvient en secret Compose, jamais en clair dans sa configuration.
  • Base en lecture seule. Grafana et postgres-exporter lisent la base par le rôle supervision, en lecture seule, limité aux tables métier (db/roles/supervision.sql). Ils n'ont ni app_user, ni jetons, ni journal d'audit.
  • Grafana verrouillé. Il refuse de démarrer sans GRAFANA_ADMIN_PASSWORD. Inscription, accès anonyme et appels sortants (statistiques d'usage, vérification de mises à jour) y sont désactivés.
  • cAdvisor en privileged. Il tourne ainsi pour lire les cgroups, avec des montages en lecture seule et sans port publié.

Journaux

Les journaux restent ceux de Docker : docker compose logs, make stack-logs, make monitoring-logs. L'API écrit du JSON dès APP_ENV=prod, caviardé des jetons et des mots de passe (voir 20-backend.md). Aucune agrégation centralisée n'est en place.

Ce qui manque

Manque Conséquence assumée
Métriques d'Airflow (StatsD, OpenTelemetry) Un DAG qui échoue ne se voit que dans Airflow ; le tableau « Données » le trahit indirectement par des relevés qui vieillissent
Alerte sur la fraîcheur des relevés Visible dans Grafana, mais aucune règle Prometheus ne la porte : il faudrait une métrique calculée par l'API ou un exportateur SQL
Journaux centralisés Un incident se diagnostique conteneur par conteneur
Destinataire réel des alertes Mailpit capture tout : les alertes se lisent dans son interface, elles ne réveillent personne