ArchyntArchynt.api
Retour au blogIngénierie

Cartographier les dépendances de services depuis /proc, sans SDK agent

Pas besoin d'instrumenter quoi que ce soit pour savoir quel service parle à quel autre. Le kernel l'a déjà noté.

10 juillet 2026 · 8 min de lecture

« Ajoutez un agent à chaque service » est la réponse que donnent la plupart des outils d'observabilité pour la découverte de services, et c'est une réponse raisonnable si vous êtes prêt à toucher chaque manifeste de déploiement, redémarrer chaque processus, et garder la version du SDK synchronisée entre une dizaine de services possédés par des gens qui ne vous rapportent pas. C'est une réponse bien moins raisonnable si vous essayez de cartographier un système dans les cinq premières minutes d'utilisation d'un produit, avant que quiconque n'ait accepté de changer une seule ligne de configuration.

Le kernel sait déjà quels sockets sont ouverts et à qui ils appartiennent. Il suffit d'aller le lire.

Ce qu'il y a réellement dans /proc

Chaque processus sous Linux a un répertoire sous /proc/<pid>, et à l'intérieur, net/tcp (et net/tcp6 pour IPv6) liste chaque socket TCP visible dans l'espace de noms réseau de ce processus : adresse locale, adresse distante, état de la connexion, et — le champ qui compte ici — un numéro d'inode. Cet inode est le même numéro qu'on retrouve dans /proc/<pid>/fd/ comme cible d'un lien symbolique pour tout descripteur de fichier qui est en fait ce socket. Parcourez les descripteurs de fichiers de chaque processus, faites correspondre les inodes avec la table des sockets, et vous obtenez une carte processus-vers-connexion sans demander la permission à un seul processus.

À partir de là, c'est de l'addition, pas de l'innovation : associez chaque PID à son conteneur (le chemin cgroup ou l'API Docker vous le donne directement), et une liste brute du type « le processus 4821 a une connexion établie vers 10.0.4.12:5432 » devient « le conteneur orders parle au conteneur postgres ». Aucun agent dans orders, aucun agent dans postgres, aucun chemin de code dans l'un ou l'autre conscient d'être observé.

La partie qui a vraiment demandé des itérations

Lire la table une fois, c'est les 80% faciles. Le problème, c'est que /proc/net/tcp est un instantané : une connexion qui s'ouvre et se ferme entre deux polls n'apparaît tout simplement dans aucun des deux échantillons. Un health check qui se déclenche toutes les 45 secondes contre un intervalle de poll de 60 secondes, un appel sortant qui se termine en 200ms, un batch job qui se connecte une fois pendant votre fenêtre de cinq minutes et jamais plus — ce sont toutes de vraies dépendances, et un diff d'instantanés naïf les rate constamment. Sur un service chargé, la majeure partie du trafic intéressant est exactement ce genre de connexion de courte durée, ce qui est frustrant à découvrir une fois que votre vue de topologie vous a déjà convaincu qu'elle était complète.

Notre correctif est une fenêtre de rétention glissante plutôt qu'un simple instantané : une arête vue durant l'un des cinq derniers cycles de poll reste dans le graphe, et n'est retirée qu'une fois véritablement absente sur toute cette plage. Ce n'est pas un algorithme brillant — c'est plus proche de « se souvenir de ce qu'on a vu un peu plus longtemps qu'on ne le pense nécessaire » — mais c'est la différence entre une topologie qui a l'air plausible et une qui est réellement juste.

Où cette approche atteint ses limites

Autant être franc sur les limites. Les setups avec un espace de noms par conteneur (le cas courant en Docker et Kubernetes) veulent dire qu'on lit l'espace de noms réseau propre à chaque conteneur plutôt qu'une table partagée pour l'hôte, ce qui demande plus de comptabilité mais n'est pas fondamentalement plus dur. Le trafic chiffré ne masque pas la connexion — on n'a besoin que des métadonnées TCP ici, pas du payload — mais ça veut dire qu'on sait seulement que deux conteneurs se parlent, pas sur quel protocole ni vers quel endpoint ; c'est à ça que sert la couche de métriques RED basée sur Beyla, et les deux sont conçus pour être lus ensemble plutôt que comme des substituts l'un de l'autre. Et c'est fondamentalement une vue de la couche TCP : les protocoles basés sur UDP nécessitent une toute autre lecture, ce qui est sur la liste, mais pas encore dans l'agent.

Vous voulez voir ça sur votre propre système ?

L'URL d'un dépôt suffit pour le graphe statique. L'agent runtime ne nécessite qu'un seul fichier docker-compose.