TechniquePratique15 min de lecture (+2 technique)
Entities, Profiles, Injector : trois briques suivies le temps d'un incident
Trois briques d'OpenTelemetry souvent mal expliquées, suivies pendant un même incident : une façon de dire quel objet émet les données (Entities), un quatrième signal qui descend jusqu'au code (Profiles) et un mécanisme qui installe les sondes sans toucher aux applications (Injector). Pour chacune : le problème, le fonctionnement, la maturité.
Pour : ingénieurs et SRE · architectes · managers d’équipePrérequis : Savoir ce que sont une trace, un span et le Collector. L'article OpenTelemetry de bout en bout les présente.
Trois noms reviennent dans les annonces d’OpenTelemetry : Entities, Profiles et Injector. Je les vois souvent cités ensemble et rarement expliqués simplement. Plutôt que de les présenter un par un dans l’abstrait, je les suis pendant un même incident, un mardi après-midi. Pour chacun, on verra le problème qu’il règle, comment il fonctionne et où il en est.
Trois briques qui ne jouent pas au même niveau
Section intitulée « Trois briques qui ne jouent pas au même niveau »On entend souvent qu’Entities serait un nouveau nom pour des attributs qui existent déjà, que Profiles serait le profilage de code qu’on pratique depuis des années et que l’Injector serait un agent de plus à déployer. Aucune de ces trois idées n’est juste.
Entities est un modèle de données : une façon de dire, sans ambiguïté, de quel objet viennent les signaux (un service, un pod, une machine) et ce qui fait son identité. Profiles est un nouveau signal, le quatrième après les traces, les métriques et les logs, qu’on peut relier aux trois autres. L’Injector est un mécanisme de déploiement : une petite bibliothèque qui branche les agents existants dans les applications au moment où elles démarrent, sans qu’on y touche.
À mon avis, la plupart des malentendus viennent de là : on compare des choses qui ne se situent pas au même étage.
Le fil rouge : un mardi, on déploie checkout
Section intitulée « Le fil rouge : un mardi, on déploie checkout »Le scénario est illustratif. Une boutique en ligne repose sur plusieurs services, des programmes qui rendent chacun une fonction précise. Le service checkout encaisse les commandes.
À 14 h 02, la version 4.2.0 de checkout part en production. Elle tourne dans un nouveau pod (dans Kubernetes, l’enveloppe qui fait tourner un ou plusieurs conteneurs). L’outil qui stocke les données croit voir un nouveau service : l’historique est coupé en deux. C’est le terrain d’Entities.
À 14 h 10, le temps de réponse grimpe. La trace montre qu’une étape de checkout est lente, sans dire quel morceau de code la ralentit. C’est le terrain de Profiles.
À 14 h 25, la piste mène à pricing, le service qui calcule les prix. C’est un programme Java installé sur une machine virtuelle, en dehors de Kubernetes. Personne ne l’a jamais instrumenté. C’est le terrain de l’Injector.
Entities : savoir de quel objet on parle
Section intitulée « Entities : savoir de quel objet on parle »14 h 02. Le déploiement a changé la version et le pod. Est-ce encore le même checkout ?
Pensez à une carte d’identité. Votre nom et votre date de naissance vous identifient : ils ne changent pas. Votre adresse ou votre coupe de cheveux vous décrivent : elles peuvent changer sans que vous deveniez quelqu’un d’autre. Une administration qui confondrait les deux vous créerait un nouveau dossier à chaque déménagement.
C’est ce qui arrive aujourd’hui à checkout.
Le problème : la fiche ne dit pas ce qui fait l’identité
Section intitulée « Le problème : la fiche ne dit pas ce qui fait l’identité »Chaque signal OpenTelemetry arrive avec une fiche qui décrit sa source. On l’appelle la Resource : c’est une liste d’attributs, des paires nom et valeur comme service.name = checkout. L’article OpenTelemetry de bout en bout la présente en détail.
Au moment du déploiement, la fiche de checkout change sur trois lignes : la version passe de 4.1.9 à 4.2.0 et le pod a un nouvel identifiant et un nouveau nom. Le nom du service et la machine, eux, restent les mêmes.
La Resource fonctionne, mais elle mélange plusieurs objets (un service, un pod, une machine) dans une seule liste. Rien n’y dit quels attributs font l’identité et lesquels ne font que décrire. Chaque outil de stockage devine. S’il se fie à la version ou au pod, il voit deux services différents et un simple déploiement suffit à casser la continuité des courbes.
Pour les équipes techniques : la Resource de checkout avant et après le déploiement
| Attribut | 14 h 01, version 4.1.9 | 14 h 02, version 4.2.0 |
|---|---|---|
service.name | checkout | checkout |
service.version | 4.1.9 | 4.2.0 (modifié) |
k8s.pod.uid | 0c1e… | 9b77… (modifié) |
k8s.pod.name | checkout-5d9f-x2k | checkout-7c4d-m8q (modifié) |
host.id | i-08ab… | i-08ab… |
Le modèle : un type, ce qui identifie, ce qui décrit
Section intitulée « Le modèle : un type, ce qui identifie, ce qui décrit »La spécification définit une entité comme un objet d’intérêt auquel se rattachent les signaux produits, qu’il s’agisse de traces, de métriques, de logs ou de profils. Une entité a trois parties. Un type d’abord, qui ne change pas pendant sa vie : service, pod, machine. Puis des attributs qui l’identifient et ne changent pas non plus, comme le nom et la date de naissance de la carte d’identité. Enfin des attributs qui la décrivent et peuvent évoluer, comme l’adresse.
Pour un service, c’est le nom qui identifie et la version qui décrit. Pour un pod, c’est son identifiant unique qui identifie, son nom qui décrit. À 14 h 02, le nom de checkout n’a pas bougé : c’est donc le même service, dans une nouvelle version. Le pod, lui, est bien une nouvelle entité. Les deux constats sont justes en même temps et l’outil n’a plus à deviner.
Trois choses changent en pratique. L’identité reste stable : une version ou un label change, l’objet reste le même et son historique aussi. La corrélation devient explicite : traces, métriques, logs et profils désignent le même objet, sans règle de correspondance bricolée côté stockage. L’inventaire devient vivant : des événements d’entité peuvent signaler un objet même quand il n’émet aucun signal, au moment où il apparaît, change ou disparaît.
Pour les équipes techniques : la même Resource découpée en entités
| Type | Identifie (ne change pas) | Décrit (peut changer) |
|---|---|---|
service | service.name | service.version |
k8s.pod | k8s.pod.uid | k8s.pod.name, labels |
host | host.id | host.name, host.type |
| processus | identifiant du processus et heure de démarrage | nom de l’exécutable |
Les trois premières lignes reprennent les exemples de la spécification et des conventions sémantiques. La ligne du processus est illustrative : la spécification cite ce couple comme exemple d’identité minimale, sans en faire une définition.
Où en est Entities ?
Section intitulée « Où en est Entities ? »Le modèle de données des entités, les événements d’entité et le modèle de la Resource qui les assemble portent tous le statut Development dans la spécification. C’est le stade le plus précoce, avant toute garantie de stabilité.
Côté protocole, OTLP (le format d’échange d’OpenTelemetry, commun à tous les signaux) permet depuis sa version 1.6.0 d’ajouter à la Resource des références d’entités, elles aussi en Development. Elles disent quels attributs forment l’identité et lesquels décrivent. Les attributs eux-mêmes restent dans la Resource : un outil qui ignore les entités continue de lire la fiche comme avant.
Les relations entre entités, comme « ce pod tourne sur cette machine », sont le chantier en cours. Les événements d’entité savent déjà en transporter, mais la spécification annonce que leur modélisation sera affinée.
Le premier pas que je recommande ne demande aucun outil : pour chacune de vos Resources, notez ce qui identifie et ce qui décrit. L’exercice est utile dès maintenant, quel que soit le calendrier de la spécification.
Pour les équipes techniques : le champ dans le protocole
Les références d’entités sont portées par le champ entity_refs de la Resource, défini dans resource.proto depuis OTLP 1.6.0, au statut Development.
Profiles : descendre jusqu’à la fonction
Section intitulée « Profiles : descendre jusqu’à la fonction »14 h 10. La trace montre une étape lente. Elle ne montre pas le code qui la ralentit.
Une trace ressemble au suivi d’un colis : elle dit à quel relais il a attendu, pas ce qui se passait à l’intérieur de l’entrepôt. Pour le savoir, il faudrait photographier l’entrepôt toutes les quelques millisecondes et compter sur combien de photos chaque poste de travail est occupé. C’est exactement ce que fait un profil.
Après les traces, les métriques et les logs, le profilage devient un signal OpenTelemetry à part entière. Un profil dit où un programme passe son temps, fonction par fonction. Il se construit à partir d’échantillons de la pile d’appels, c’est-à-dire de la liste des fonctions en cours d’exécution à un instant donné, chacune appelée par la précédente.
D’une étape lente à la fonction qui coûte
Section intitulée « D’une étape lente à la fonction qui coûte »La trace découpe la requête en étapes, qu’on appelle des spans. Elle répond à la question « où, entre les étapes ? » :
POST /checkout ████████████████████████████████████████ validateCart ███ applyPricing ██████████████████████████████ saveOrder ██████Le span applyPricing prend presque tout le temps. Pourquoi ? Le profil de ce span répond à l’autre question, « où, dans le code ? ». Chaque ligne est une fonction appelée par celle du dessus et sa largeur montre la part du temps passée dedans. Ce dessin s’appelle un flame graph, un graphe en flammes :
applyPricing ████████████████████████████████████████loadRules ██████computeDiscounts ██████████████████████████████████readCache ████matchPromo ██████████████████████████████Pattern.compile ████████████████████████████Dans cet exemple inventé, la version 4.2.0 a introduit la compilation d’une expression régulière (un motif de recherche dans du texte) à l’intérieur d’une boucle : elle est recompilée à chaque appel.
Ce qui relie les deux vues, c’est que chaque échantillon du profil peut porter les identifiants de la trace et du span en cours. On passe ainsi d’un span à sa pile d’appels. Le profil est rattaché à une Resource, comme les autres signaux : les mêmes attributs aujourd’hui, les mêmes entités demain, le relient au service, au pod et à la machine.
Comment le profil arrive jusqu’à l’écran ?
Section intitulée « Comment le profil arrive jusqu’à l’écran ? »Tout commence par la capture. Un profileur eBPF échantillonne les piles d’appels de toute la machine Linux. eBPF désigne de petits programmes qui tournent dans le noyau Linux et observent sans modifier les applications. Ce profileur vient d’un don d’Elastic au projet et fonctionne comme récepteur du Collector, le relais qui reçoit, transforme et réexpédie les données.
Les piles partent ensuite en OTLP, dans un modèle construit sur pprof, un format de profils répandu venu de l’écosystème Go. Un profil pprof se convertit vers OTLP et inversement sans perte. Le Collector les achemine dans un circuit dédié, qu’il faut activer explicitement. Le stockage, enfin, affiche les graphes en flammes et compare les versions entre elles.
Le signal apporte plusieurs choses. Il donne le « pourquoi » d’une courbe de processeur ou de mémoire, au niveau du code. Il permet de sauter directement d’un span à sa pile d’appels. Son format est ouvert, donc les profils restent lisibles par un autre stockage. Il voit toute la machine, y compris le code que vous n’avez pas écrit.
Pour les équipes techniques : liens et activation dans le Collector
Les identifiants portés par chaque échantillon sont le trace_id et le span_id. Le pipeline des profils du Collector s’active par la feature gate service.profilesSupport.
Ce que veut dire « alpha »
Section intitulée « Ce que veut dire « alpha » »Le signal est entré en alpha publique en mars 2026. L’annonce elle-même en tire les conséquences. Il ne doit pas servir pour des charges de production critiques. Son format peut encore changer de façon incompatible. Le support est à activer dans le Collector, à partir de la version 0.148.0. Enfin, à la date de l’annonce, aucun stockage prêt pour la production n’existait, même si plusieurs éditeurs y travaillent.
Pour un premier essai, je recommande un montage modeste : le profileur eBPF sur une machine de test, un Collector avec le circuit des profils activé et un stockage qui lit les profils OTLP. Vérifiez quelle version du format chaque élément prend en charge, car elle bouge encore.
Injector : instrumenter sans toucher au service
Section intitulée « Injector : instrumenter sans toucher au service »14 h 25. pricing tourne sur une machine virtuelle, sans agent. Pour le voir, il faudrait le modifier.
Pour qu’une application envoie des traces, des métriques et des logs, on lui ajoute en général un agent d’auto-instrumentation : un module qui se greffe sur le programme au démarrage et mesure ce qu’il fait, sans changer son code. Encore faut-il penser à le brancher sur chaque service, dans chaque langage, sur chaque nouvelle machine, puis maintenir tout cela.
Imaginez l’accueil d’un immeuble de bureaux. Plutôt que de demander à chaque visiteur d’apporter son badge, l’hôtesse en remet un à toute personne qui passe la porte. L’Injector joue ce rôle. Il ne remplace pas les agents : il se charge de les brancher automatiquement au démarrage de chaque programme, grâce à une fonction standard de Linux appelée LD_PRELOAD.
Ce qui se passe au démarrage de pricing
Section intitulée « Ce qui se passe au démarrage de pricing »Quand un programme démarre, Linux charge les bibliothèques dont il a besoin. LD_PRELOAD lui demande d’en charger une avant toutes les autres. On peut l’indiquer pour un seul programme ou, pour toute la machine, dans un fichier système.
La bibliothèque de l’Injector entre donc en premier. Elle reconnaît le runtime du programme, c’est-à-dire le logiciel qui exécute l’application : la machine virtuelle Java, Node.js, .NET. Elle ajoute alors la variable d’environnement que ce runtime sait lire pour charger un agent. Le runtime charge l’agent et les traces, métriques et logs partent en OTLP, comme avec un agent installé à la main.
L’Injector lui-même ne produit ni trace, ni métrique, ni log. Il décide seulement quel agent brancher sur quel programme et peut compléter les attributs de la Resource. Pour pricing, service Java, il indique à Java le chemin de l’agent Java d’OpenTelemetry. Le service n’a pas été touché : il reçoit son agent au prochain redémarrage.
Pour les équipes techniques : avant, après et variables ajoutées par runtime
À la main, service par service :
# pricing.service (systemd)ExecStart=/usr/bin/java \ -javaagent:/opt/otel/javaagent.jar \ -jar /opt/pricing/pricing.jarAvec l’Injector, une fois par hôte :
/usr/lib/opentelemetry/libotelinject.so
# pricing.service : inchangéLe chemin de la bibliothèque est celui de la documentation du projet ; la configuration se trouve par défaut dans /etc/opentelemetry/injector/injector.conf. LD_PRELOAD se déclare par une variable d’environnement ou, pour tout l’hôte, dans /etc/ld.so.preload.
libotelinject.so ajoute la variable que le runtime sait lire : JAVA_TOOL_OPTIONS pour Java, NODE_OPTIONS pour Node.js, les variables CORECLR_* et DOTNET_* pour .NET, RUBYOPT pour Ruby. Pour pricing, il ajoute à JAVA_TOOL_OPTIONS le chemin de l’agent Java OpenTelemetry.
Ce que l’Injector ne fait pas
Section intitulée « Ce que l’Injector ne fait pas »Il ne sait rien faire pour les programmes compilés en un seul bloc, qu’on appelle des binaires statiques : sans chargement de bibliothèques au démarrage, pas de préchargement possible. Beaucoup de programmes écrits en Go sont dans ce cas.
Il ne crée pas d’agent pour les langages qui n’en ont pas : il branche un agent existant. Au 10 octobre 2026, le projet prend en charge Java, Node.js, .NET et Ruby ; Python existe mais reste désactivé par défaut.
Il n’agit pas sur les programmes déjà lancés. Tout se joue au démarrage : il faut redémarrer le service pour qu’il soit pris en charge.
Il ne passe pas sur les programmes privilégiés. Pour les exécutables qui démarrent avec des droits élevés (les exécutables setuid), Linux restreint LD_PRELOAD par sécurité.
Il ne prend pas non plus la décision de sécurité à votre place. Une bibliothèque chargée dans tous les programmes d’une machine se valide avec l’équipe sécurité, comme tout ce qu’on inscrit dans le fichier de préchargement du système.
Sa place parmi les façons d’instrumenter sans modifier le code
Section intitulée « Sa place parmi les façons d’instrumenter sans modifier le code »L’Injector n’est pas seul sur ce terrain. Dans Kubernetes, l’Operator d’OpenTelemetry fait un travail voisin : il s’appuie sur un webhook d’admission, un contrôle que Kubernetes appelle à la création de chaque pod et qui peut le modifier pour y ajouter l’agent. L’instrumentation eBPF, elle, pose des sondes dans le noyau Linux, en dehors du programme : elle voit les échanges réseau de n’importe quel langage, mais moins l’intérieur de l’application.
Les trois approches se complètent. L’Operator couvre Kubernetes, l’instrumentation eBPF couvre tous les langages au niveau des échanges réseau, l’Injector couvre les machines Linux et les machines virtuelles avec les agents complets. pricing vit sur une machine virtuelle : l’Operator ne peut rien pour lui, c’est le terrain de l’Injector.
Pour les équipes techniques : Injector, Operator et instrumentation eBPF
| Injector | Operator | Instrumentation eBPF | |
|---|---|---|---|
| Où | hôte ou conteneur Linux | Kubernetes | nœud Linux |
| Comment | LD_PRELOAD, puis l’agent du langage | webhook d’admission qui modifie le pod | sondes dans le noyau, hors du processus |
| Langages | ceux que le projet prend en charge | ceux qui disposent d’un agent | tous, au niveau des protocoles |
Où en est l’Injector ?
Section intitulée « Où en est l’Injector ? »Le dépôt du projet n’affiche pas de niveau de stabilité. Ses versions sont numérotées en 0.x (0.11.0 pour la dernière que j’ai relevée) : attendez-vous à ce que la liste des langages et la configuration bougent encore. Les paquets d’installation pour Debian et Red Hat (DEB et RPM) ne sont plus construits dans ce dépôt mais dans le dépôt opentelemetry-packaging.
Pour un premier essai, je recommande une machine virtuelle hors Kubernetes, avec un service Java ou Node.js, en préproduction.
Ce que chacun y gagne
Section intitulée « Ce que chacun y gagne »Pour les équipes d’exploitation chargées de la fiabilité (les SRE), Entities garde les alertes et les tableaux de bord intacts d’un déploiement à l’autre. Profiles donne la cause au niveau du code pendant l’incident, sans avoir à le reproduire. L’Injector réduit les zones aveugles en dehors de Kubernetes.
Pour les équipes plateforme et DevOps, Entities offre un inventaire commun à tous les outils, sans règles de correspondance maison. Profiles fait passer les quatre signaux par un seul circuit et rend visible le coût en processeur de chaque service. L’Injector instrumente un parc de machines en installant un paquet, sans retoucher chaque déploiement.
Pour les développeurs, Entities permet de retrouver son service et ses versions sans connaître l’infrastructure. Profiles montre la fonction qui coûte, vue en production et pas seulement sur leur poste. L’Injector n’exige rien dans le code ni dans l’image du conteneur.
Ce sont des gains attendus, à vérifier sur votre propre périmètre. Je n’en chiffre aucun : aucune des trois briques n’est stable.
Le même mardi, avec les trois briques
Section intitulée « Le même mardi, avec les trois briques »Rejouons l’après-midi. À 14 h 02, checkout 4.2.0 reste checkout et l’historique n’est pas coupé. À 14 h 10, depuis le span lent, on ouvre la fonction qui coûte. À 14 h 25, pricing est déjà instrumenté sans que personne l’ait modifié.
| Heure | Brique | Maturité au 10 octobre 2026 |
|---|---|---|
| 14 h 02 | Entities | Development |
| 14 h 10 | Profiles | alpha publique |
| 14 h 25 | Injector | versions 0.x |
Les trois se complètent : l’Injector fait entrer traces, métriques et logs sans effort, Profiles ajoute le niveau du code et Entities dit à quel objet tout cela se rapporte. Lundi matin, la tâche la plus utile est aussi la moins chère : écrire, pour vos services, ce qui les identifie et ce qui les décrit.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- OpenTelemetry de bout en bout : la Resource, les conventions sémantiques, le Collector.
- Explorateur de traces : lire une trace span par span avant d’y ajouter un profil.
- Labs OpenTelemetry : les labs publiés sur la forge autour d’OpenTelemetry.
- Gouverner les données d’observabilité : qui décide des attributs et de leur sens.
Consultées le 10 octobre 2026.
- OpenTelemetry, Entity Data Model : définition d’une entité, type, attributs d’identité et de description, exemples, statut Development.
- OpenTelemetry, Entity Events : événements d’état et de suppression, entités sans signal associé, relations.
- OpenTelemetry, Resource Data Model : la Resource comme composition d’entités.
- OpenTelemetry, resource.proto : champ
entity_refs, statut Development, depuis la version 1.6.0. - OpenTelemetry, conventions sémantiques du service : entité
service, identifiée parservice.name. - OpenTelemetry, OpenTelemetry Profiles Enters Public Alpha : statut alpha, mise en garde sur la production, profiler eBPF, conversion pprof, lien avec les traces, Collector 0.148.0, état des stockages.
- OpenTelemetry, spécification des profils : modèle de données construit sur pprof, lien vers un span, Resource partagée.
- OpenTelemetry, The State of Profiling : activation des profils dans le Collector par
service.profilesSupport. - OpenTelemetry, opentelemetry-injector : rôle de l’Injector, chemins, variables ajoutées par runtime, langages pris en charge, versions.
- Linux man-pages, ld.so(8) :
LD_PRELOAD,/etc/ld.so.preload, restrictions en mode d’exécution sécurisé.