Files
oc-datacenter/infrastructure/monitor/PROMETHEUS_ANALYSIS.md

101 lines
6.7 KiB
Markdown
Raw Normal View History

# Analyse de `infrastructure/prometheus.go`
## Ce que fait le fichier
Ce fichier implémente un service de monitoring qui interroge une instance **Prometheus** pour collecter des métriques de conteneurs Kubernetes associés à une réservation (Booking).
### Structures de données
| Struct | Role |
|---|---|
| `MetricsSnapshot` | Snapshot de métriques associé à une origine (source). **Note : cette struct locale est déclarée mais jamais utilisée** — le code utilise en réalité `models.MetricsSnapshot` de oc-lib. |
| `Metric` | Paire nom/valeur d'une métrique. **Même remarque** — le code utilise `models.Metric`. |
| `PrometheusResponse` | Mapping de la réponse JSON de l'API Prometheus `/api/v1/query`. |
### Métriques collectées (`queriesMetrics`)
| # | Requête PromQL | Mesure |
|---|---|---|
| 1 | `rate(container_cpu_usage_seconds_total{namespace}[1m]) * 100` | Utilisation CPU (%) |
| 2 | `container_memory_usage_bytes{namespace}` | Mémoire utilisée (bytes) |
| 3 | `container_fs_usage_bytes / container_fs_limit_bytes * 100` | Utilisation disque (%) |
| 4 | `DCGM_FI_DEV_GPU_UTIL{namespace}` | Utilisation GPU (NVIDIA DCGM) |
| 5 | `rate(container_fs_reads_bytes_total[1m])` | Débit lecture disque (bytes/s) |
| 6 | `rate(container_fs_writes_bytes_total[1m])` | Débit écriture disque (bytes/s) |
| 7 | `rate(container_network_receive_bytes_total[1m])` | Bande passante réseau entrante (bytes/s) |
| 8 | `rate(container_network_transmit_bytes_total[1m])` | Bande passante réseau sortante (bytes/s) |
| 9 | `rate(http_requests_total[1m])` | Requêtes HTTP/s |
| 10 | `rate(http_requests_total{status=~"5.."}[1m]) / rate(http_requests_total[1m]) * 100` | Taux d'erreur HTTP 5xx (%) |
Métriques commentées (non actives) : `system_load_average`, `system_network_latency_ms`, `app_mean_time_to_repair_seconds`, `app_mean_time_between_failure_seconds`.
### Méthodes
#### `queryPrometheus(promURL, expr, namespace) Metric`
- Construit une requête GET vers `/api/v1/query` de Prometheus.
- Injecte le namespace dans l'expression PromQL via `fmt.Sprintf`.
- Parse la réponse JSON et extrait la première valeur du premier résultat.
- Retourne `-1` si aucun résultat.
#### `Call(book, user, peerID, groups) (Booking, map[string]MetricsSnapshot)`
- Charge la ressource de calcul (`ComputeResource`) liée au booking.
- Pour chaque instance de la ressource, cherche le `LiveDatacenter` correspondant.
- Lance en **goroutine** (parallèle) l'exécution de toutes les requêtes PromQL pour chaque datacenter ayant un `MonitorPath`.
- Attend toutes les goroutines (`sync.WaitGroup`), puis retourne les métriques groupées par instance.
#### `Stream(bookingID, interval, user, peerID, groups, websocket)`
- Boucle de monitoring en continu jusqu'à `ExpectedEndDate` du booking ou signal de kill.
- A chaque tick (`interval`), appelle `Call()` dans une goroutine.
- Envoie les métriques en temps réel via **WebSocket**.
- Accumule les métriques en mémoire et les persiste dans le booking tous les `max` (100) cycles.
- Supporte un mécanisme de kill via la variable globale `Kill`.
---
## Problemes et points d'attention
### Bugs potentiels
1. **Race condition dans `Stream`** — Les variables `mets`, `bookIDS`, `book` sont partagées entre la boucle principale et les goroutines lancées à chaque tick, **sans synchronisation** (pas de mutex). Si `interval` est court, plusieurs goroutines peuvent écrire simultanément dans `mets` et `bookIDS`.
2. **Race condition sur `Kill`** — La variable globale `Kill` est lue dans la boucle sans verrouiller `LockKill`. Le mutex n'est utilisé que pour l'écriture.
3. **Structs locales inutilisées**`MetricsSnapshot` et `Metric` (lignes 22-31) sont déclarées localement mais le code utilise `models.MetricsSnapshot` et `models.Metric`. Code mort à nettoyer.
4. **Requête PromQL avec double placeholder** — La requête filesystem (ligne 47) contient deux `%s` mais `queryPrometheus` ne fait qu'un seul `fmt.Sprintf(expr, namespace)`. Cela provoque un **`%!s(MISSING)`** dans la requête. Il faut passer le namespace deux fois ou réécrire la fonction.
5. **Pas de timeout HTTP**`http.Get()` utilise le client par défaut sans timeout. Un Prometheus lent peut bloquer indéfiniment.
6. **Pas de gestion d'erreur sur `WriteJSON`** — Si le WebSocket est fermé côté client, l'écriture échoue silencieusement.
### Améliorations possibles
#### Fiabilité
- **Ajouter un `context.Context`** à `queryPrometheus` et `Call` pour supporter les timeouts et l'annulation.
- **Utiliser un `http.Client` avec timeout** au lieu de `http.Get`.
- **Protéger les accès concurrents** dans `Stream` avec un `sync.Mutex` sur `mets`/`bookIDS`.
- **Remplacer la variable globale `Kill`** par un `context.WithCancel` ou un channel, plus idiomatique en Go.
#### Métriques supplémentaires envisageables
- `container_cpu_cfs_throttled_seconds_total` — Throttling CPU (le container est bridé).
- `kube_pod_container_status_restarts_total` — Nombre de restarts (instabilité).
- `container_memory_working_set_bytes` — Mémoire réelle utilisée (exclut le cache, plus précis que `memory_usage_bytes`).
- `kube_pod_status_phase` — Phase du pod (Running, Pending, Failed...).
- `container_oom_events_total` ou `kube_pod_container_status_last_terminated_reason` — Détection des OOM kills.
- `kubelet_volume_stats_used_bytes` / `kubelet_volume_stats_capacity_bytes` — Utilisation des PVC.
- `DCGM_FI_DEV_MEM_COPY_UTIL` — Utilisation mémoire GPU.
- `DCGM_FI_DEV_GPU_TEMP` — Température GPU.
- `node_cpu_seconds_total` / `node_memory_MemAvailable_bytes` — Métriques au niveau du noeud (vue globale).
#### Architecture
- **Range queries** (`/api/v1/query_range`) — Actuellement seul l'instant query est utilisé. Pour le streaming sur une période, `query_range` permettrait de récupérer des séries temporelles complètes et de calculer des moyennes/percentiles.
- **Labels dans les résultats** — Actuellement seule la première série est lue (`Result[0]`). On perd l'information si plusieurs pods/containers matchent. Agréger ou renvoyer toutes les séries.
- **Noms de métriques lisibles** — Mapper les expressions PromQL vers des noms humains (`cpu_usage_percent`, `memory_bytes`, etc.) au lieu de stocker l'expression brute comme nom.
- **Health check Prometheus** — Ajouter une méthode pour vérifier que Prometheus est accessible (`/-/healthy`).
---
## Résumé
Le fichier est **fonctionnel** pour un cas d'usage basique (collecte one-shot + streaming WebSocket), mais présente des **race conditions** dans `Stream`, un **bug sur la requête filesystem** (double `%s`), et du **code mort**. Les améliorations prioritaires sont la correction des accès concurrents et l'ajout de timeouts HTTP.