Infrastructure

OpenTelemetry Collector : valider le tail sampling avant de perdre les traces d’incident

Un runbook de production pour qualifier le tail sampling OpenTelemetry avec affinité des traces, spans tardifs, pression mémoire, couverture des règles, preuves Azure Monitor, canari et rollback.

19 sept. 2026 opentelemetrycollectortail-samplingazure-monitorapplication-insightsobservabilitytracingkqlautomationcanaryrunbookrollbackproduction

Une équipe observabilité veut réduire l’ingestion des traces sans rendre les incidents plus opaques. Conserver toutes les traces en erreur ou lentes et seulement un échantillon du trafic nominal paraît plus sûr qu’un taux fixe. Après le déploiement, certaines erreurs arrivent pourtant sans leur requête amont, les traces longues disparaissent et la mémoire des Collectors monte pendant les pics. La règle semble correcte ; le chemin de trace ne l’est pas.

Le cas fil rouge est une plateforme Kubernetes qui envoie ses données OTLP à Azure Monitor et Application Insights via des Collectors en mode gateway. L’équipe remplace un head sampling simple par le processeur OpenTelemetry Collector tail_sampling. Ce runbook décide si le candidat peut passer en production, doit rester en shadow mode ou doit être rollbacké avant de supprimer les preuves nécessaires au prochain incident.

Figer le contrat de preuve avant le pourcentage

Le tail sampling est une politique d’exploitation, pas un simple réglage de coût. Définissez ce qui doit rester diagnosticable avant de choisir les seuils.

yaml contrat-tail-sampling.yml
change: tail-sampling-v3
scope:
services: [checkout-api, payment-worker]
environment: production
exporter: azure-monitor

must_keep:
- trace_avec_statut_erreur
- trace_superieure_a_2000_ms
- canari_synthetique
- trace_explicitement_debuggee

baseline:
pourcentage_traces_succes: 5

required_evidence:
- root_et_children_partagent_trace_id
- trace_erreur_conserve_requete_amont
- span_tardif_herite_decision_initiale
- drops_collector_expliques
- volume_ingestion_dans_budget

rollback: restaurer-pipelines-v2-et-redemarrer-pool-canari

Conservez la version courante du Collector, le digest de configuration, le nombre de réplicas, les débits de traces reçues et exportées ainsi que la marge mémoire. Une facture Azure Monitor plus basse n’est pas un succès si les traces d’échec deviennent fragmentaires ou absentes.

Prouver l’affinité des traces avant de lire les règles

Le tail sampler regroupe les spans par trace_id pendant qu’il attend sa décision. Tous les spans d’une trace doivent donc atteindre le même Collector d’échantillonnage. Un Service en round-robin devant plusieurs processeurs stateful peut scinder la trace : chaque réplica voit une histoire incomplète et prend une décision localement valide.

Utilisez deux niveaux explicites quand la gateway doit scaler horizontalement :

text topologie-tail-sampling.txt
Applications et agents
-> Collectors d'entree stateless
-> load-balancing exporter indexe par trace ID
-> Collectors de tail sampling stateful
-> Azure Monitor / Application Insights

Invariant
Chaque span du meme trace_id atteint le meme sampling shard

Panne a refuser
Un Service Kubernetes distribue directement les spans entre tail samplers

Ne déduisez pas l’affinité d’une trace complète observée une seule fois. Émettez une trace synthétique avec un root span, plusieurs children et un child retardé, puis inspectez la télémétrie receiver et exporter sur chaque réplica. Rejouez pendant un scale-out et un redémarrage. Si le même trace_id apparaît comme une nouvelle trace sur plusieurs samplers, aucun réglage de règle ne corrigera la topologie.

Dimensionner la fenêtre avec les traces observées

decision_wait définit combien de temps le processeur accumule une trace avant évaluation. Une fenêtre courte réduit la mémoire mais peut décider avant l’arrivée d’un child lent. Une fenêtre longue conserve davantage de spans et augmente l’état en mémoire. Mesurez le retard réel du chemin, y compris les files et batch exporters, plutôt que de recopier une valeur par défaut en production.

Construisez la distribution du délai entre le premier et le dernier span observé. Testez les requêtes normales, les dépendances lentes, les traitements asynchrones et un redémarrage du Collector. Choisissez une fenêtre candidate couvrant les cas du contrat, puis prouvez son coût mémoire au débit maximal de nouvelles traces.

num_traces constitue aussi un seuil d’arrêt. Quand le nombre de traces en attente dépasse la capacité, les plus anciennes peuvent quitter la fenêtre avant le moment attendu. expected_new_traces_per_sec aide l’allocation ; il ne crée pas de capacité et ne remplace pas un test de charge. Suivez ensemble spans acceptés et refusés, décisions de sampling, latence du processeur et mémoire du processus.

Rendre les règles lisibles et testables

Commencez par peu de règles. L’ordre et l’interaction de nombreux matchers se comprennent moins bien qu’une table de décision explicite.

yaml otelcol-tail-sampling-canari.yaml
processors:
tail_sampling/canary:
  decision_wait: 30s
  num_traces: 50000
  expected_new_traces_per_sec: 1000
  decision_cache:
    sampled_cache_size: 100000
    non_sampled_cache_size: 100000
  policies:
    - name: keep-errors
      type: status_code
      status_code:
        status_codes: [ERROR]
    - name: keep-slow
      type: latency
      latency:
        threshold_ms: 2000
    - name: keep-canaries
      type: string_attribute
      string_attribute:
        key: test.kind
        values: [tail-sampling-canary]
    - name: successful-baseline
      type: probabilistic
      probabilistic:
        sampling_percentage: 5

service:
pipelines:
  traces/canary:
    receivers: [otlp]
    processors: [memory_limiter, k8sattributes, tail_sampling/canary, batch]
    exporters: [azuremonitor]

Ces valeurs forment un candidat, pas un dimensionnement universel. Tout enrichissement requis par une règle doit s’exécuter avant le tail sampling. Les processeurs dépendant du contexte de requête original se placent également avant lui, car les spans réassemblés ressortent dans de nouveaux batches.

Évitez une règle implicite du type « garder tout ce qui paraît inhabituel ». Nommez les attributs, les valeurs autorisées et leur propriétaire. N’injectez pas d’entrée utilisateur non bornée, d’identifiant de compte ou de secret dans les attributs uniquement pour rendre le sampling sélectif.

Tester les décisions, pas seulement la syntaxe

Un Collector qui démarre prouve seulement que le YAML est accepté. Rejouez une matrice déterministe sur toute la topologie.

text evaluation-tail-sampling.txt
Cas                               Resultat attendu
Succes rapide                     La baseline peut garder ou jeter la trace complete
HTTP 500 avec dependance child    Root, dependance et erreur restent ensemble
Succes lent au-dessus de 2000 ms  Trace complete conservee par keep-slow
Canari explicite                  Toujours conserve avec les attributs attendus
Span d'erreur retarde             Herite la decision, ne forme jamais un fragment
Trace plus longue que la fenetre  Comportement connu et mesure, sans faux succes
Ajout d'un replica                Trace maintenue sur un seul sampling shard
Redemarrage d'un replica          Perte bornee et visible dans la telemetrie Collector
Pic de trafic                     Memoire sous le seuil d'arret
Service inconnu                   Defaut declare, pas une ouverture accidentelle

Les cas positifs prouvent la conservation des traces obligatoires. Les cas négatifs prouvent que le trafic ordinaire baisse réellement et qu’un attribut mal formé ne peut pas tout laisser passer. Conservez les trace IDs générés et les nombres de spans attendus comme preuves de release.

Qualifier les spans tardifs et les caches de décision

Un span tardif peut arriver après l’acceptation ou le rejet de sa trace. Sans décision conservée, le processeur peut le traiter comme une nouvelle trace et l’évaluer à nouveau. Le résultat est un fragment ou une décision contradictoire.

Dimensionnez les caches des décisions sampled et non-sampled à partir du volume et de la cardinalité des spans tardifs observés, puis testez les deux chemins. Le cache doit survivre au retard habituel et contenir le rythme des décisions, tout en restant dans le budget mémoire. Il ne remplace pas la correction d’exporters systématiquement en retard.

Faites envoyer au canari son dernier span d’erreur après la fenêtre de décision normale. Vérifiez qu’il hérite du résultat initial tant que la décision est en cache. Rejouez ensuite au-delà de l’horizon prévu et documentez le comportement. Si ce second cas peut masquer un incident réel, adaptez l’architecture ou le point d’échantillonnage avant toute promotion.

Corréler les décisions du Collector avec Azure Monitor

La santé du Collector indique si le pipeline a traité des données. Azure Monitor indique si le contrat de diagnostic est arrivé. Les deux preuves sont nécessaires.

Comparez d’abord les décisions par règle aux débits receiver et exporter. Une hausse des drops processeur, spans refusés, files saturées ou actions du memory limiter invalide un ratio de sampling apparemment propre. Si le build du Collector sait attribuer une décision à une règle, activez cette fonction d’abord sur le canari et vérifiez les attributs et leur cardinalité avant généralisation.

Dans Application Insights, cherchez les operation IDs des canaris conservés et comptez les couches attendues :

kusto 01-valider-canaris-tail-sampling.kql
let StartUtc = datetime(<start-utc>);
let EndUtc = datetime(<end-utc>);
let CanaryOperationIds = dynamic(["<fast-trace-id>", "<error-trace-id>", "<slow-trace-id>"]);
union isfuzzy=true
(requests | project timestamp, operation_Id, itemType="request", name, success),
(dependencies | project timestamp, operation_Id, itemType="dependency", name, success),
(exceptions | project timestamp, operation_Id, itemType="exception", name=type, success=false)
| where timestamp between (StartUtc .. EndUtc)
| where operation_Id in (CanaryOperationIds)
| summarize Items=count(), Types=make_set(itemType), Failed=countif(success == false) by operation_Id
| order by operation_Id asc

Cette requête aide la validation ; elle ne constitue pas un schéma universel. Adaptez tables et champs au mode d’ingestion utilisé. La preuve de release doit relier chaque trace ID généré, le nombre de spans attendu, la décision du Collector et le résultat Azure Monitor.

Comparez aussi dans le temps le pourcentage conservé et le volume ingéré. Les métriques ne sont pas échantillonnées comme les traces : gardez des métriques de service et des probes synthétiques comme signal de contrôle indépendant. Sinon, une règle peut rendre la vue des traces artificiellement saine en supprimant les échecs qui permettraient de la contredire.

Déployer avec un shadow path et un canari borné

Dupliquez une petite tranche non sensible vers un pipeline candidat ou ne routez que des services canaris nommés. N’envoyez pas le flux dupliqué vers la même destination facturée sans limite : utilisez une ressource Application Insights de test, un debug exporter strictement borné ou des preuves locales à durée courte.

Exécutez la matrice en charge normale, au pic, pendant un rolling restart et lors de l’ajout d’un réplica. Comparez candidat et chemin courant par trace ID, pas uniquement par pourcentage agrégé. La promotion exige des traces représentatives d’erreur et de latence complètes, pas seulement des volumes proches.

Étendez un service à la fois. Gardez immuables la configuration et l’artefact de déploiement précédents. Le switch de rollback doit restaurer l’ancien pipeline de traces indépendamment d’un déploiement applicatif.

Décider promotion, maintien ou rollback

Promouvez lorsque l’affinité survit au scaling, que la fenêtre mesurée couvre les cas obligatoires, que la mémoire reste bornée, que les spans tardifs suivent une décision cohérente, que la matrice passe et qu’Azure Monitor contient les canaris complets attendus. Continuez à surveiller drops Collector et coût d’ingestion après chaque cohorte de services.

Maintenez le shadow mode quand le pourcentage paraît correct mais que la complétude, l’attribution des décisions ou la mémoire au pic restent ambiguës. Le test suivant doit réduire cette ambiguïté, pas simplement ajouter du trafic.

Rollbackez immédiatement lorsqu’une trace se répartit entre plusieurs samplers, qu’une trace d’erreur ou lente obligatoire disparaît, que le memory limiter commence à jeter les preuves ou qu’un span tardif crée un fragment contradictoire. Restaurez le pipeline précédent, redémarrez uniquement le pool canari concerné si nécessaire, puis vérifiez de nouveaux trace IDs de bout en bout. Un rollback ne reconstitue pas la télémétrie déjà supprimée.

Conclusion

Le tail sampling est sûr lorsque son chemin de décision est aussi observable que les traces qu’il filtre. Le contrat couvre routage, état, temps, mémoire, sémantique des règles, export et requête dans la destination.

La décision de production devient alors concrète : promouvoir avec affinité prouvée, spans tardifs testés, état borné et canaris complets dans Azure Monitor ; maintenir tant qu’une preuve reste ambiguë ; revenir au pipeline précédent dès la première perte inexpliquée. La maîtrise des coûts reste utile parce qu’elle préserve les traces dont l’exploitation aura réellement besoin.