08

👁️ Observabilité

Logs, Metrics, Traces - Les 3 Piliers

"Déployer en production sans observabilité revient à piloter un avion de ligne dans l'obscurité, sans radar. Logs, métriques et traces sont vos yeux quand les systèmes tombent en panne à 3h du matin."

🏎️ Analogie : Le Tableau de Bord F1

Le Monitoring vous dit "Le moteur fume" (C'est cassé).
L'Observabilité vous permet de demander "Pourquoi ?" en regardant la télémétrie complète (Pression d'huile, température, historique des 10 dernières minutes).

⚠️ Rappel : Metrics First !

Ne basez pas vos alertes critiques sur l'analyse de texte de vos Logs. Règle d'or : Les Metrics pour alerter, les Logs pour débugger. Traiter 10 000 lignes de logs par seconde coûte très cher en CPU et stockage ; lire 1 chiffre de metric coûte presque rien.

📖 Observabilité : Illuminer la Boîte Noire

Dans un système monolithique simple, un log suffit. Dans un cluster Kubernetes avec 200 microservices, c'est impossible. L'observabilité est la capacité de comprendre l'état interne d'un système uniquement en regardant ses sorties (Metrics, Logs, Traces).

On passe du monitoring Réactif ("Tiens, ça a crashé") à l'observabilité Proactive ("La latence monte sur le service X, ça va bientôt saturer").

💡 Le saviez-vous ? Les 4 Golden Signals

Google a défini dans son livre SRE les 4 signaux d'or du monitoring que tout dashboard devrait avoir :

  1. Latency : Temps de réponse (ms).
  2. Traffic : Nombre de requêtes/sec.
  3. Errors : Taux de succès vs échec.
  4. Saturation : Utilisation des ressources (CPU, RAM, Disque).

🏛️ A. Les 3 Piliers

📜

Logs

Quoi ? Événements textuels horodatés
Usage : Debugging, audit trail
Outils: ELK, Loki, CloudWatch

📈

Metrics

Quoi ? Chiffres agrégés dans le temps
Usage : Alerting, dashboards
Outils: Prometheus, Datadog

🕵️

Traces

Quoi ? Suivi d'une requête multi-services
Usage : Latency, bottlenecks
Outils: Jaeger, OpenTelemetry

🛠️ B. Stacks Populaires

📊 Les 3 Piliers : Quand utiliser quoi ?

📜 Logs (Événements)

  • Granularité : Haute (détails précis).
  • Coût : Élevé (stockage massif).
  • Usage : "Pourquoi cette requête spécifique a échoué ?"

🕵️ Traces (Parcours)

  • Granularité : Contextuelle (multi-services).
  • Coût : Moyen (échantillonnage nécessaire).
  • Usage : "Où est passée la seconde de latence entre A et B ?"

📊 Cas d'étude : Uber - Gérer 500 millions de métriques

500M Métriques persistées
9.5M Points écrits / sec
0 Downtime toléré pour le monitoring

Contexte : En grandissant, Uber a saturé toutes les solutions de métriques existantes (Graphite, Prometheus). Ils avaient besoin d'une base de données de séries temporelles (TSDB) capable de scaler horizontalement à l'infini.

Transformation :

  1. M3 Project : Ils ont créé leur propre stack open-source "M3" pour agréger et stocker les métriques.
  2. Distributed Tracing : Création de Jaeger (donné à la CNCF) pour suivre chaque course Uber à travers des milliers de microservices Go et Java.
  3. Adaptive Sampling : On ne garde pas 100% des traces (trop cher), mais 100% des traces en erreur et 1% des traces réussies.

Résultat : Uber peut détecter une anomalie sur le prix ou la disponibilité des chauffeurs en moins de 10 secondes partout dans le monde.

🔄 À Retenir - Observability Mastery

  • Don't Log Everything : Le stockage des logs coûte cher. Filtrez à la source.
  • Tagging is King : Une métrique ou un log sans tags (env, service, version) est inutile.
  • Dashboards Actionnables : Un dashboard doit répondre à "Est-ce que ça marche ?" pas "Regarde ces jolis graphs".
  • OpenTelemetry : Utilisez le standard ouvert OTel pour ne pas être bloqué chez un fournisseur (Lock-in).

🌐 C. OpenTelemetry - Le Standard Unifié

OpenTelemetry (OTel) est le standard CNCF (graduated project) pour l'instrumentation, la collecte et l'export de télémétrie (logs, metrics, traces). C'est la fusion de OpenTracing + OpenCensus.

Problème résolu : Avant OTel, chaque vendor (Datadog, New Relic, Jaeger) avait son propre SDK. Changer de vendor = réécrire toute l'instrumentation. OTel = vendor-neutral.

App (K8s Pod) /metrics stdout logs OTel Collector Receive Process & Export Prometheus Metrics (TSDB) Loki / ELK Logs Jaeger Distributed Traces Grafana Push/Pull

Architecture d'observabilité unifiée via OpenTelemetry. Le Collector centralise, transforme et route la télémétrie vers les backends appropriés.

📊 Architecture OTel

  • Instrumentation : SDKs dans l'app (auto ou manuel)
  • Collector : Agent qui reçoit, transforme, exporte
  • Exporters : Envoie vers Jaeger, Prometheus, Datadog, etc.
  • Context Propagation : Trace ID suit la requête partout
otel-collector.yaml
receivers:
  otlp:      # Reçoit traces/metrics via gRPC
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:     # Agrège avant export
  memory_limiter:
    limit_mib: 512

exporters:
  jaeger:
    endpoint: jaeger:14250
  prometheus:
    endpoint: prometheus:9090
Auto-instrumentation (Python)
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

# Setup tracer
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# Export vers Collector
otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317")
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)
🔄 Sampling Strategies
Head-based : Décision au début de la trace (1% de tout)
Tail-based : Décision à la fin (100% des erreurs, 1% du succès)
Tail-based est plus intelligent mais plus coûteux en compute.

🔬 D. eBPF for Observability

eBPF (extended Berkeley Packet Filter) permet d'exécuter du code dans le kernel Linux sans modifier le kernel. C'est révolutionnaire pour l'observabilité : zero instrumentation.

🦅 Pixie (eBPF-based Observability)

Observabilité automatique de K8s sans changer une ligne de code.

  • Capture automatique HTTP/gRPC/MySQL/Postgres requests
  • Cartes de dépendances générées automatiquement
  • Flamegraphs CPU sans profiler SDK
  • Deploy : kubectl apply -f pixie.yaml

🔍 Avantages eBPF

  • Zero overhead : Pas de sidecars, pas de SDK
  • Language-agnostic : Fonctionne avec Java, Go, Rust, C++
  • Kernel-level visibility : Voit TOUT (syscalls, network packets)
  • Security : Falco utilise eBPF pour détecter intrusions
⚠️ Limite eBPF
eBPF ne capture pas le contexte business (user ID, transaction ID). Pour cela, vous devez quand même instrumenter manuellement avec OTel ou des logs structurés.

🎯 E. SLI/SLO/Error Budgets

📏 SLI (Indicator)

Quoi ? Métrique quantifiable de la qualité du service.

Exemples : Latency P99, Success rate, Throughput

Mesure CE QUI SE PASSE réellement.

🎯 SLO (Objective)

Quoi ? Target pour un SLI sur une période.

Exemples : "99.9% des requêtes < 200ms sur 30 jours"

Définit CE QU'ON VEUT atteindre.

📜 SLA (Agreement)

Quoi ? Contrat légal avec pénalités financières.

Exemples : "99.95% uptime ou remboursement"

Engage la RESPONSABILITÉ juridique.

💰 Error Budget

Si SLO = 99.9% uptime, alors Error Budget = 0.1% downtime autorisé = 43 minutes/mois.

  • Budget consommé → Freeze features, focus stabilité
  • Budget restant → OK pour déployer features risquées
  • Alignement Dev/SRE : même métrique, même objectif
Formule : Error Budget = (1 - SLO) × période
Ex: (1 - 0.999) × 30 jours × 24h × 60min = 43.2 minutes
SLO Burn Rate Alerting
# Alerte si on consomme Error Budget trop vite
alert: ErrorBudgetBurn
expr: |
  (
    1 - sum(rate(http_requests_total{status=~"2.."}[1h]))
        / sum(rate(http_requests_total[1h]))
  ) > (14.4 * (1 - 0.999))
  # 14.4x = consomme budget en 2h au lieu de 30j
for: 5m
labels:
  severity: critical
annotations:
  summary: "Burning error budget 14x faster"

💸 F. Cardinality & Cost Management

Cardinality = nombre de combinaisons uniques de tags (labels). C'est LE problème #1 qui fait exploser les coûts de monitoring.

🚨 Explosion de Cardinality
Métrique : http_requests{service, method, path, user_id, request_id}
Si 10 services × 5 methods × 1000 paths × 10M users × infinité request_id = 🔥💸
Solution : JAMAIS mettre user_id ou request_id en tag. Utiliser des logs pour ça.

✅ Cardinality Best Practices

  • Low cardinality tags OK : env, service, region, status_code
  • High cardinality tags NO : user_id, request_id, timestamp
  • Agrégation intelligente : /users/123 → /users/:id
  • Monitoring de cardinality : Alerter si spike
Stratégie Coût Granularité Use Case
100% des métriques $$$$ Max Dev/Staging uniquement
Sampling (10%) $$ Moyen Production standard
Tail-based (erreurs only) $ Intelligent Production optimisé
Retention 7j vs 90j $ vs $$$ - Compliance requirements

🕸️ G. Distributed Tracing Avancé

Trace Context Propagation (W3C)
# Headers HTTP propagés automatiquement
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
             │  │                                │                │
             │  └─ Trace ID (128-bit)           │                └─ Sampled?
             │                                   └─ Span ID (64-bit)
             └─ Version

# Chaque service ajoute son span à la trace
GET /api/orders → create_span("fetch_order") {
  call payment_service  # Passe traceparent
  call inventory_service # Passe traceparent
}

🏷️ Span Tags & Baggage

  • Tags : Metadata du span (http.method, db.statement)
  • Baggage : Key-value propagés à TOUS les spans enfants
  • Use case baggage : user_tier (premium/free) pour sampling
  • Warning : Baggage augmente taille des headers → latency
Correlation avec logs : Injectez trace_id dans vos logs structurés pour naviguer de la trace vers les logs d'un span spécifique.

🏢 H. Observability Platforms Comparison

Platform Type Forces Faiblesses
Datadog Commercial (SaaS) All-in-one, APM excellent, UX top Très cher (cardinality pricing)
Grafana Stack Open-source Loki (logs), Tempo (traces), Mimir (metrics), gratuit Self-hosted = ops overhead
Elastic (ELK) Open-core Search puissant, mature, large écosystème Ressource hungry (Java heap)
New Relic Commercial (SaaS) Ingestion illimitée, pricing par user Query language moins flexible
Honeycomb Commercial (SaaS) High-cardinality native, best pour debug Focus traces uniquement

📊 Cas d'étude : Shopify - Migration vers OpenTelemetry

1000+ Services instrumentés
6 mois Durée migration complète
-40% Réduction coût observabilité

Contexte : Shopify utilisait un mix de Datadog SDK + custom instrumentation. Changer de vendor = 6 mois de refactoring. Inacceptable.

Migration OTel :

  1. Phase 1 : Deploy OTel Collector en sidecar (sans changer app code)
  2. Phase 2 : Auto-instrumentation pour Ruby/Go (80% du backend)
  3. Phase 3 : Migration des custom spans vers OTel SDK
  4. Phase 4 : Multi-vendor export (Datadog + Honeycomb pour A/B test)

Résultat : Vendor-agnostic. Peut changer de backend observability sans toucher au code. Coût réduit de 40% en optimisant sampling.

📊 Cas d'étude : Airbnb - Cardinality Explosion Incident

10M Métriques uniques créées en 1h
$50K Surcoût Datadog ce mois-là
3h Dashboard Prometheus down

Contexte : Un développeur a ajouté un tag listing_id (7M valeurs uniques) à une métrique de latency.

Impact immédiat :

  1. Prometheus TSDB saturée → toutes les queries timeoutent
  2. Datadog facture 10M nouvelles métriques × $0.05 = $500K/an projeté
  3. Alertes cassées → équipes aveugles pendant 3h

Fix : Tag listing_id supprimé. Remplacé par aggregation dans logs. Ajout de cardinality guard : CI bloque si une métrique a >1000 combinaisons de tags.

Leçon : La cardinality peut détruire votre observability ET votre budget. Reviewez les tags comme du code critique.

🔄 À Retenir - Observability Mastery

  • Don't Log Everything : Le stockage des logs coûte cher. Filtrez à la source.
  • Tagging is King : Une métrique ou un log sans tags (env, service, version) est inutile.
  • Dashboards Actionnables : Un dashboard doit répondre à "Est-ce que ça marche ?" pas "Regarde ces jolis graphs".
  • OpenTelemetry : Utilisez le standard ouvert OTel pour ne pas être bloqué chez un fournisseur (Lock-in).
  • eBPF = Future : Observabilité zero-instrumentation au niveau kernel (Pixie, Falco).
  • SLO/Error Budgets : Alignez Dev et SRE sur des métriques business objectives.
  • Cardinality = Enemy #1 : High-cardinality tags explosent les coûts et cassent les TSDB.
  • Sampling intelligent : Tail-based sampling = 100% erreurs + 1% succès = best ROI.
PromQL Essentiels
# Taux d'erreurs HTTP (5xx)
rate(http_requests_total{status=~"5.."}[5m])
  / rate(http_requests_total[5m])

# Latence P99
histogram_quantile(0.99,
  rate(http_duration_seconds_bucket[5m]))

# CPU usage par pod
sum by(pod)(rate(container_cpu_usage_seconds_total[5m]))

🚨 Alerting Best Practices

  • SLO-based : Alerter sur Error Budget burn rate
  • Pas trop d'alertes : Alert fatigue = tout ignorer
  • Runbooks : Chaque alerte → action claire
  • Escalation : PagerDuty, OpsGenie
Correlation ID : Injectez un ID unique dans logs, metrics et traces pour les relier.

🛑 Troubleshooting : Prometheus ne remonte plus mes métriques

  • Symptôme : Vos graphes Grafana sont vides ou affichent "No Data", et vos requêtes PromQL ne renvoient rien pour un service donné.
  • Diagnostic : En général, l'application a planté, ou l'endpoint /metrics n'est plus accessible (firewall, mauvaise configuration du ServiceMonitor). Allez dans l'UI de Prometheus > Status > Targets.
  • Remède : 1. Si la target est en DOWN : Vérifiez les logs réseau ou l'état du pod de l'application.
    2. Si la target est absente : Vérifiez les annotations (ex: prometheus.io/scrape: "true") ou le manifeste ServiceMonitor/PodMonitor dans Kubernetes.