Maîtriser les coûts avec l'échantillonnage : configuration du SDK et du Collector
Décidez quand échantillonner dans le SDK et quand utiliser le tail sampling dans un Collector à deux niveaux.
Conserver chaque trace devient vite coûteux, et la plupart sont des requêtes routinières que personne n’ouvrira jamais. L’échantillonnage conserve un sous-ensemble représentatif ou intéressant. OpenTelemetry offre deux endroits pour décider : dans le SDK au démarrage d’une trace (head sampling) et dans le Collector une fois la trace terminée (tail sampling). Les deux relèvent de la pure configuration. Ce guide présente chacun et comment choisir.
Head sampling dans le SDK
La décision est prise lors de la création du span racine ; elle est donc peu coûteuse et ne nécessite aucune infrastructure supplémentaire. Elle ne peut pas savoir si la requête échouera ou sera lente, car cela ne s’est pas encore produit.
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
Les noms de samplers définis par la spécification incluent always_on, always_off, traceidratio, parentbased_always_on (la valeur par défaut), parentbased_always_off et parentbased_traceidratio. Utilisez les variantes parentbased_ : elles suivent la décision d’échantillonnage du parent de la requête entrante, de sorte qu’une trace est conservée ou abandonnée en entier à travers les services. Un simple traceidratio dans chaque service prendrait des décisions indépendantes et produirait des traces brisées.
Dans le fichier de configuration déclarative, la même idée se présente ainsi :
tracer_provider:
sampler:
parent_based:
root:
trace_id_ratio_based:
ratio: 0.1
Consultez la documentation de votre SDK pour connaître les noms de samplers qu’il prend en charge dans le format de fichier.
Tail sampling dans le Collector
Le tail sampling attend qu’une trace soit complète, puis applique des politiques. Il vous permet de conserver toutes les erreurs et toutes les traces lentes plus une petite part du reste, ce que le head sampling ne peut pas faire. Le coût est plus d’infrastructure et de mémoire.
processors:
tail_sampling:
decision_wait: 10s
num_traces: 50000
expected_new_traces_per_sec: 100
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: keep-slow
type: latency
latency:
threshold_ms: 2000
- name: sample-the-rest
type: probabilistic
probabilistic:
sampling_percentage: 5
Comment cela fonctionne :
decision_waitest le temps d’attente du processor après avoir vu le premier span d’une trace avant de décider. Il doit être supérieur à la durée de votre trace la plus lente, sinon vous déciderez sur des traces partielles.num_tracesest le nombre de traces conservées en mémoire pendant l’attente. Dimensionnez-le à peu près sur votre taux d’arrivée de traces multiplié pardecision_wait, avec de la marge.- Les
policiessont évaluées pour chaque trace. Une trace est conservée si n’importe quelle politique dit de la conserver ; les trois ci-dessus signifient donc « conserver les erreurs, conserver les lentes, et conserver aussi 5 pour cent de tout ». - D’autres types de politiques incluent
string_attribute(correspondre à une valeur comme un niveau de client),numeric_attribute,always_sample,and(combiner des politiques) etcomposite(allouer des taux entre politiques).
tail_sampling se trouve dans la distribution contrib, et ses noms d’options évoluent ; consultez donc son README pour votre version.
Le piège : une trace doit atteindre un seul Collector
Le tail sampling ne fonctionne que si tous les spans d’une trace arrivent à la même instance de Collector. Avec plusieurs réplicas derrière un simple répartiteur de charge, les spans d’une trace se dispersent et les décisions sont prises sur des fragments. La solution standard est de procéder en deux niveaux :
- Le niveau 1 (couche de répartition de charge) utilise l’exporter
loadbalancingpour router les spans par ID de trace vers une instance cohérente du niveau 2. - Le niveau 2 (couche d’échantillonnage) exécute
tail_samplinget exporte les traces conservées.
Niveau 1 :
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
exporters:
loadbalancing:
routing_key: traceID
protocol:
otlp:
tls:
insecure: true
resolver:
dns:
hostname: otel-sampler-headless.observability.svc.cluster.local
port: 4317
service:
pipelines:
traces:
receivers: [otlp]
exporters: [loadbalancing]
Niveau 2 :
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 20
tail_sampling:
decision_wait: 10s
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: sample-the-rest
type: probabilistic
probabilistic:
sampling_percentage: 10
batch:
exporters:
otlp/backend:
endpoint: tempo:4317
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp/backend]
Utilisez un Service headless afin que le DNS liste les pods du niveau 2, et notez que lorsque des pods sont ajoutés ou retirés, le routage se déplace et quelques traces en cours peuvent être décidées sur des données incomplètes. Garder les couches séparées isole aussi les pannes : un problème dans l’échantillonnage ne bloque pas l’ingestion.
Combiner head et tail
Une configuration courante et sensée :
- Un head sampler modeste dans le SDK (par exemple 50 pour cent) pour réduire le volume à la source pour les services à très fort trafic.
- Du tail sampling dans le Collector pour garantir que les erreurs et les requêtes lentes survivent.
Soyez prudent : tout ce qui est abandonné en tête ne peut pas être récupéré en queue. Si vous devez conserver toutes les erreurs, n’échantillonnez pas ces services de manière agressive en tête.
Les métriques survivent à l’échantillonnage si vous les dérivez d’abord
Abandonner des traces abandonne aussi les chiffres que vous pourriez en calculer, comme les taux de requêtes et les percentiles de latence. Générez les métriques avant l’étape d’échantillonnage avec le connector spanmetrics, afin que les tableaux de bord restent exacts :
connectors:
spanmetrics:
service:
pipelines:
traces/all:
receivers: [otlp]
exporters: [spanmetrics, loadbalancing]
metrics/derived:
receivers: [spanmetrics]
processors: [batch]
exporters: [otlphttp/prometheus]
Ici, chaque span alimente le connector, tandis que seul l’ensemble échantillonné continue vers le stockage.
Choisir
| Objectif | Approche |
|---|---|
| Réduire le coût simplement, sans nouvelle infrastructure | Head sampling avec parentbased_traceidratio |
| Conserver toutes les erreurs et requêtes lentes | Tail sampling dans un Collector à deux niveaux |
| Conserver des taux et percentiles exacts | spanmetrics avant l’échantillonnage |
| Peu de services, faible volume | Pas d’échantillonnage ; y revenir quand le coût compte |
Vérifier
- Au niveau 2, comparez le nombre de spans en entrée et en sortie à l’aide des métriques propres du Collector (port 8888) pour confirmer que le ratio correspond à vos politiques.
- Recherchez une requête échouée connue et assurez-vous que toute sa trace est présente.
- Surveillez la mémoire au niveau 2, car il conserve les traces pendant
decision_wait. Si elle augmente, réduiseznum_tracesou ajoutez des réplicas, et gardezmemory_limiterdevant.
Guides populaires
-
OpenTelemetry expliqué : SDK, OTLP et Collector Comprenez les éléments en jeu et les trois endroits où vit la configuration avant d'écrire du YAML. -
Le modèle de configuration du Collector : composants, pipelines et providers Apprenez les règles du YAML du Collector : nommage, pipelines, connectors, fusion de fichiers et variables d'environnement. -
Configurer les SDK avec les variables d'environnement OTEL_* Définissez le nom du service, les exporters, l'échantillonnage et les limites pour n'importe quel langage avec des variables standard. -
Instrumenter des applications sans modifier le code : Java, Python et Node.js Attachez des agents OpenTelemetry uniquement avec des variables d'environnement et des options de ligne de commande. -
Préparer le Collector pour la production : limites mémoire, batching et retries Configurez processors, files d'attente et stockage persistant pour que la télémétrie survive aux surcharges et aux pannes. -
Déployer le Collector sur Kubernetes avec Helm Installez le chart officiel, utilisez les presets pour les logs et les métriques, et surchargez la configuration via des fichiers values. -
Dépanner le Collector et les SDK Une checklist systématique pour quand la télémétrie n'arrive pas : exporter debug, health checks, métriques internes et erreurs courantes.