diff --git a/README.md b/README.md index db40185..76eeec9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ tenté, non simulé. | `tripoli-edge` (Rust, Phase 2) | Fait et déployé en prod — `tripoli-edge/`, service systemd, derrière Caddy, vérifié via `https://tripoli.cloud/healthz` et `/metrics` | | `tripoli-api` (Rust, Phase 3) | Fait et déployé en prod — `tripoli-api/`, auth par session (Argon2), upload/download, PostgreSQL dédié (Docker), vérifié via `https://tripoli.cloud/api/...` | | `tripoli-netctl` (Rust, Phase 4) | Fait — `tripoli-netctl/`, labo pédagogique (netns + veth via `rtnetlink`), testé sur le serveur (création, ping traversant la veth, destruction propre), **volontairement pas branché sur le trafic de prod** | -| Observabilité dédiée (Phase 5) | Pas commencé (une stack Prometheus/Grafana existe déjà sur le serveur, partagée avec les autres projets) | +| Observabilité (Phase 5) | Fait — pas de stack dédiée (réutilise le Prometheus/Grafana partagé existant), scrape tripoli-edge + alertes (down, disque) + dashboard versionné (`monitoring/`), vérifié via l'API Prometheus/Grafana | | Bird/BGP (Phase 6, bonus) | Pas commencé | ## Dépôt diff --git a/monitoring/README.md b/monitoring/README.md new file mode 100644 index 0000000..872d91f --- /dev/null +++ b/monitoring/README.md @@ -0,0 +1,52 @@ +# Observabilité + +Pas de Prometheus/Grafana dédiés à Tripoli Cloud : une instance de +chacun existe déjà sur ce serveur, partagée avec les 5 autres projets +qui y tournent. En déployer une seconde aurait dupliqué de la RAM sur +une machine à 3.7 Go déjà partagée, pour un bénéfice nul - même +raisonnement que pour Caddy en Phase 2. + +## Ce qui a été ajouté à l'instance existante + +- **Scrape job `tripoli_edge`** (`prometheus-scrape-snippet.yml`, appliqué + à `/opt/monitoring/prometheus/prometheus.yml` sur le serveur) : + `172.17.0.1:9000/metrics` - même passerelle docker0 que Caddy utilise + pour joindre tripoli-edge. +- **Règles d'alerte** (`tripoli-cloud-alerts.yml`, montée dans le + conteneur Prometheus) : + - `TripoliEdgeDown` : `up{job="tripoli_edge"} == 0` pendant 2 minutes. + - `HostDiskAlmostFull` : moins de 10% d'espace libre sur `/` pendant + 10 minutes - une alerte au niveau de la machine, pas seulement de + Tripoli Cloud, puisqu'il n'y a qu'un seul disque pour tous les + projets hébergés. +- **Sonde blackbox externe** : `https://tripoli.cloud/healthz` ajoutée + à la liste existante du job `blackbox_http` (vérifie que le site + répond réellement depuis l'extérieur, pas seulement que Prometheus + arrive à le scraper en interne). +- **Dashboard Grafana** (`grafana-dashboard-tripoli-edge.json`, + `uid: tripoli-cloud-edge`) : état up/down, requêtes en cours, latence + moyenne, requêtes par classe de statut, et l'espace disque de l'hôte. + Importé via l'API Grafana (`POST /api/dashboards/db`), pas par copier- + coller dans l'UI - c'est ce qui permet de le garder versionné ici et + de le réimporter à l'identique si besoin. + +## Ce qui n'a pas été fait + +Les règles d'alerte **existent et se déclenchent** dans Prometheus (vérifiable +via `/api/v1/rules`), mais rien ne les **notifie** encore par email - pas +d'Alertmanager déployé, et pas de policy de notification Grafana +configurée pour ces alertes spécifiquement. Grafana a déjà un SMTP +fonctionnel (utilisé par d'autres projets sur cette instance) ; y +brancher les alertes de Tripoli Cloud est le prochain pas naturel, pas +fait dans cette passe pour éviter de modifier la politique de +notification globale de l'instance partagée sans plus de tests. + +## Vérifié + +``` +$ curl .../api/v1/targets -> tripoli_edge: up, blackbox tripoli.cloud/healthz: up +$ curl .../api/v1/rules -> groupe "tripoli-cloud" : TripoliEdgeDown, HostDiskAlmostFull +$ curl .../api/v1/query?query=up{job="tripoli_edge"} -> 1 +``` + +Dashboard accessible sur `https://grafana.belkacemi.me/d/tripoli-cloud-edge/`. diff --git a/monitoring/grafana-dashboard-tripoli-edge.json b/monitoring/grafana-dashboard-tripoli-edge.json new file mode 100644 index 0000000..2353a9c --- /dev/null +++ b/monitoring/grafana-dashboard-tripoli-edge.json @@ -0,0 +1,102 @@ +{ + "title": "Tripoli Cloud - tripoli-edge", + "uid": "tripoli-cloud-edge", + "tags": ["tripoli-cloud"], + "timezone": "browser", + "schemaVersion": 39, + "version": 1, + "time": { "from": "now-6h", "to": "now" }, + "refresh": "30s", + "panels": [ + { + "id": 1, + "title": "tripoli-edge up", + "type": "stat", + "gridPos": { "h": 4, "w": 4, "x": 0, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "cfxoej9jr3ncwb" }, + "targets": [ + { "expr": "up{job=\"tripoli_edge\"}", "refId": "A" } + ], + "fieldConfig": { + "defaults": { + "mappings": [ + { "type": "value", "options": { "0": { "text": "DOWN", "color": "red" }, "1": { "text": "UP", "color": "green" } } } + ], + "thresholds": { "mode": "absolute", "steps": [{ "color": "red", "value": null }, { "color": "green", "value": 1 }] } + } + } + }, + { + "id": 2, + "title": "Requêtes en cours (in-flight)", + "type": "stat", + "gridPos": { "h": 4, "w": 4, "x": 4, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "cfxoej9jr3ncwb" }, + "targets": [ + { "expr": "tripoli_edge_in_flight_requests", "refId": "A" } + ] + }, + { + "id": 3, + "title": "Latence moyenne (ms)", + "type": "stat", + "gridPos": { "h": 4, "w": 4, "x": 8, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "cfxoej9jr3ncwb" }, + "targets": [ + { "expr": "tripoli_edge_request_duration_ms_avg", "refId": "A" } + ] + }, + { + "id": 4, + "title": "Espace disque libre sur / (hôte partagé)", + "type": "gauge", + "gridPos": { "h": 4, "w": 4, "x": 12, "y": 0 }, + "datasource": { "type": "prometheus", "uid": "cfxoej9jr3ncwb" }, + "targets": [ + { + "expr": "100 * node_filesystem_avail_bytes{mountpoint=\"/\",fstype!=\"tmpfs\"} / node_filesystem_size_bytes{mountpoint=\"/\",fstype!=\"tmpfs\"}", + "refId": "A" + } + ], + "fieldConfig": { + "defaults": { + "unit": "percent", + "min": 0, + "max": 100, + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "red", "value": null }, + { "color": "orange", "value": 10 }, + { "color": "green", "value": 25 } + ] + } + } + } + }, + { + "id": 5, + "title": "Requêtes par classe de statut (par seconde)", + "type": "timeseries", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "cfxoej9jr3ncwb" }, + "targets": [ + { + "expr": "rate(tripoli_edge_responses_total[5m])", + "legendFormat": "{{class}}", + "refId": "A" + } + ] + }, + { + "id": 6, + "title": "Requêtes totales (par seconde)", + "type": "timeseries", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, + "datasource": { "type": "prometheus", "uid": "cfxoej9jr3ncwb" }, + "targets": [ + { "expr": "rate(tripoli_edge_requests_total[5m])", "legendFormat": "req/s", "refId": "A" } + ] + } + ] +} diff --git a/monitoring/prometheus-scrape-snippet.yml b/monitoring/prometheus-scrape-snippet.yml new file mode 100644 index 0000000..eba5e72 --- /dev/null +++ b/monitoring/prometheus-scrape-snippet.yml @@ -0,0 +1,19 @@ +# Bloc ajouté à /opt/monitoring/prometheus/prometheus.yml sur le serveur +# (fichier partagé avec les 5 autres projets surveillés par cette même +# instance - donc pas commité dans son intégralité ici, même logique +# que le Caddyfile de tripoli-edge). +# +# rule_files pointe vers tripoli-cloud-alerts.yml (ce dossier), monté +# dans le conteneur Prometheus via un ajout au docker-compose.yml +# existant. + +rule_files: + - /etc/prometheus/tripoli-cloud-alerts.yml + +scrape_configs: + - job_name: tripoli_edge + static_configs: + - targets: ['172.17.0.1:9000'] # passerelle docker0, même pattern que Caddy -> tripoli-edge + + # Ajouté à la liste existante du job blackbox_http : + # - https://tripoli.cloud/healthz diff --git a/monitoring/tripoli-cloud-alerts.yml b/monitoring/tripoli-cloud-alerts.yml new file mode 100644 index 0000000..b540810 --- /dev/null +++ b/monitoring/tripoli-cloud-alerts.yml @@ -0,0 +1,25 @@ +groups: + - name: tripoli-cloud + rules: + - alert: TripoliEdgeDown + expr: up{job="tripoli_edge"} == 0 + for: 2m + labels: + severity: critical + project: tripoli-cloud + annotations: + summary: "tripoli-edge est down" + description: "Prometheus n'arrive plus à scraper tripoli-edge (job tripoli_edge) depuis 2 minutes." + + # Alerte au niveau de la machine, pas seulement de Tripoli Cloud : + # un seul disque, partagé avec 6 autres projets - un disque plein + # affecterait tout le monde, pas que ce projet. + - alert: HostDiskAlmostFull + expr: node_filesystem_avail_bytes{mountpoint="/",fstype!="tmpfs"} / node_filesystem_size_bytes{mountpoint="/",fstype!="tmpfs"} < 0.10 + for: 10m + labels: + severity: warning + project: tripoli-cloud + annotations: + summary: "Disque du serveur presque plein" + description: "Moins de 10% d'espace libre sur / depuis 10 minutes."