Fondamentaux de l'observabilité¶
Monitoring vs Observabilité¶
| Aspect | Monitoring | Observabilité |
|---|---|---|
| Approche | Vérifier des conditions connues | Explorer des problèmes inconnus |
| Question | "Le service est-il up ?" | "Pourquoi ce service est-il lent pour cet utilisateur ?" |
| Données | Metriques predefinies, seuils | Metriques + logs + traces correles |
| Alerting | Seuils statiques | SLO-based, burn rate |
| Limitation | Ne détecte que les problèmes anticipes | Permet d'investiguer les problèmes imprevus |
Le monitoring est un sous-ensemble de l'observabilité. Un système bien instrumente est observable — on peut poser n'importe quelle question apres coup sans avoir a ajouter de l'instrumentation.
Les 3 piliers¶
Metriques¶
Valeurs numeriques agreges dans le temps. Trois types fondamentaux :
| Type | Usage | Exemple |
|---|---|---|
| Compteur (counter) | Valeur qui ne fait qu'augmenter | Nombre total de requêtes HTTP |
| Jauge (gauge) | Valeur instantanee qui monte et descend | Utilisation mémoire en octets |
| Histogramme (histogram) | Distribution de valeurs dans des buckets | Latence des requêtes (p50, p95, p99) |
# Taux de requetes par seconde (derniere 5min)
rate(http_requests_total[5m])
# 99e percentile de latence
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# Utilisation CPU
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
Logs¶
Événements discrets horodates, idealement structures en JSON :
{
"timestamp": "2026-04-16T14:23:45.123Z",
"level": "error",
"service": "api-gateway",
"trace_id": "abc123def456",
"message": "upstream timeout",
"upstream": "catalog-service:8080",
"duration_ms": 30012,
"status_code": 504
}
Logs structures
Toujours emettre des logs en JSON structure. Les logs en texte libre sont difficiles a filtrer et a correler. Utiliser un schema coherent entre tous les services (mêmes noms de champs).
Traces distribuees¶
Une trace représente le parcours complet d'une requête à travers plusieurs services :
graph TD
T["Trace abc123def456"]
T --> GW["api-gateway — 312ms"]
GW --> Auth["auth-service — 45ms"]
GW --> Catalog["catalog-service — 250ms"]
Catalog --> DB["database — 30ms"]
Catalog --> Cache["cache — 5ms"] Chaque segment est un span. Les spans sont relies par un trace_id commun et des relations parent/enfant.
Méthode USE¶
La méthode USE (Brendan Gregg) s'applique à chaque ressource du système :
| Lettre | Mesure | Question |
|---|---|---|
| U — Utilization | Pourcentage de temps occupe | Quel pourcentage de CPU/mémoire/disque est utilisé ? |
| S — Saturation | File d'attente | Y a-t-il du travail en attente ? (queue depth, swap) |
| E — Errors | Taux d'erreurs | Des erreurs se produisent-elles ? (ECC, disk errors) |
# USE pour le CPU d'un noeud
# Utilization
avg by(instance) (rate(node_cpu_seconds_total{mode!="idle"}[5m])) * 100
# Saturation (load average normalise)
node_load1 / count by(instance) (node_cpu_seconds_total{mode="idle"})
# Errors
rate(node_cpu_guest_seconds_total[5m])
Méthode RED¶
La méthode RED (Tom Wilkie) s'applique à chaque service :
| Lettre | Mesure | Question |
|---|---|---|
| R — Rate | Requêtes par seconde | Combien de requêtes le service traite-t-il ? |
| E — Errors | Taux d'erreurs | Quel pourcentage de requêtes échouent ? |
| D — Duration | Latence | Combien de temps prend chaque requête ? |
# RED pour un service HTTP
# Rate
sum(rate(http_requests_total{service="catalog"}[5m]))
# Errors
sum(rate(http_requests_total{service="catalog", status=~"5.."}[5m]))
/
sum(rate(http_requests_total{service="catalog"}[5m]))
# Duration (p99)
histogram_quantile(0.99,
sum by(le) (rate(http_request_duration_seconds_bucket{service="catalog"}[5m]))
)
USE vs RED
USE pour les ressources infrastructure (CPU, mémoire, disque, réseau). RED pour les services applicatifs. Les deux méthodes sont complementaires.
SLI, SLO, SLA¶
| Concept | Définition | Exemple |
|---|---|---|
| SLI (Service Level Indicator) | Metrique mesuree | Pourcentage de requêtes < 200ms |
| SLO (Service Level Objective) | Objectif interne | 99.9% des requêtes < 200ms sur 30 jours |
| SLA (Service Level Agreement) | Engagement contractuel | 99.5% de disponibilité, pénalités financieres |
La relation : SLI alimente le SLO, le SLO est plus strict que le SLA pour conserver une marge.
# SLI : pourcentage de requetes reussies en moins de 200ms
sum(rate(http_request_duration_seconds_bucket{le="0.2", status!~"5.."}[30d]))
/
sum(rate(http_requests_total[30d]))
Lien avec Exploitation
Pour une mise en œuvre operationnelle des SLI/SLO et de l'alerting, voir le tutoriel Exploitation > Superviser et alerter qui detaille les stratégies d'alerte basées sur le burn rate et l'error budget.
OpenTelemetry¶
OpenTelemetry (OTel) est le standard CNCF pour la collecte de telemetrie. Il unifie la collecte des 3 piliers :
graph TD
subgraph App["Application"]
M["OTel SDK (metrics)"]
L["OTel SDK (logs)"]
T["OTel SDK (traces)"]
end
M --> Collector["OTel Collector<br/>(ou Alloy)"]
L --> Collector
T --> Collector
Collector --> Mimir["Mimir (metriques)"]
Collector --> Loki["Loki (logs)"]
Collector --> Tempo["Tempo (traces)"] Avantages d'OpenTelemetry¶
- Vendeur-neutre : instrumenter une fois, envoyer vers n'importe quel backend
- Auto-instrumentation : bibliotheques pour Java, Python, Go, Node.js, .NET
- Propagation de contexte :
trace_idpropage automatiquement entre les services - Standard CNCF : adoption massive, pérennité assuree
Structured logging¶
Les logs doivent suivre un schema structure coherent entre tous les services :
{
"timestamp": "2026-04-16T14:23:45.123Z",
"level": "info",
"service": "catalog-api",
"version": "1.4.2",
"trace_id": "abc123def456",
"span_id": "789ghi",
"message": "requete traitee",
"http.method": "GET",
"http.path": "/api/v1/products",
"http.status_code": 200,
"duration_ms": 42
}
Champs obligatoires
Chaque log doit contenir au minimum : timestamp, level, service, message. Pour les services HTTP, ajouter trace_id, http.method, http.status_code, duration_ms.