Intégration d’OpenTelemetry à l’architecture de référence des applications modernes – Point d’avancement

NGINX | 08 mars 2022

Lorsque nous avons présenté l’architecture de référence NGINX pour les applications modernes (MARA) l’automne dernier lors de Sprint 2.0, nous avons souligné notre volonté de ne pas en faire un simple « jouet », comme certaines architectures, mais plutôt une solution « robuste, testée et prête à être déployée dans des applications de production exécutées dans des environnements Kubernetes ». Pour un tel projet, les outils d’observabilité sont absolument indispensables. Tous les membres de l’équipe MARA ont pu constater par eux-mêmes à quel point le manque de visibilité sur l’état et les performances rend le développement et la livraison d’applications particulièrement frustrants. Nous sommes immédiatement convenus que MARA devait inclure des outils de débogage et de traçage dans un environnement de production.

Un autre principe directeur de la MARA est de privilégier les solutions open source. Dans cet article, nous expliquons comment notre recherche d’un outil d’observabilité multifonctionnel et open source nous a conduits à OpenTelemetry, puis nous détaillons les compromis, les choix de conception, les techniques et les technologies que nous avons utilisés pour intégrer OpenTelemetry à une application de microservices développée avec Python, Java et NGINX.

Nous espérons que le partage de notre expérience vous aidera à éviter certains écueils et à accélérer votre propre adoption d’OpenTelemetry. Veuillez noter que cet article est un état des lieux à un instant donné : nous pensons que les technologies abordées gagneront en maturité d’ici un an. Par ailleurs, même si nous soulignons les lacunes actuelles de certains projets, nous sommes extrêmement reconnaissants pour tout le travail accompli dans le domaine de l’open source et avons hâte de suivre son évolution.

Notre application

Comme application à intégrer à une solution d’observabilité, nous avons choisi Bank of Sirius, notre fork de l’application exemple Bank of Anthos de Google. Il s’agit d’une application web reposant sur une architecture de microservices, qui peut être déployée au moyen d’une infrastructure en tant que code. Cette application peut être améliorée de nombreuses façons en matière de performances et de fiabilité, mais elle est suffisamment mature pour être raisonnablement considérée comme une application existante. À ce titre, nous pensons qu’elle constitue un bon exemple pour montrer comment intégrer OpenTelemetry à une application, car, en théorie, le traçage distribué fournit de précieuses informations sur les lacunes de l’architecture d’une application.

Comme le montre le schéma, les services sur lesquels repose l’application sont relativement simples.

Image fournie par Google

Pourquoi nous avons choisi OpenTelemetry

Notre parcours pour choisir OpenTelemetry a été assez sinueux et s’est déroulé en plusieurs étapes.

Créer une liste de fonctionnalités souhaitées

Avant d’évaluer les différents outils d’observabilité open source disponibles, nous avons déterminé les aspects de l’observabilité qui nous importaient. En nous appuyant sur nos expériences passées, nous avons établi la liste suivante.

  • Journalisation – Dans le sens où nous employons ce terme, il s’agit de la génération, par les applications, d’ensembles classiques de messages séparés par des sauts de ligne ; l’application Bank of Sirius structure ses journaux au format Bunyan
  • Traçage distribué – Données temporelles et métadonnées pour chaque composant de l’ensemble de l’application, à l’image de ce que proposent les fournisseurs de solutions de gestion des performances applicatives (APM)
  • Métriques – Mesures recueillies sur une période donnée et représentées graphiquement sous forme de séries chronologiques
  • Agrégation et notification des exceptions/erreurs – Un ensemble agrégé des exceptions et erreurs les plus fréquentes, dans lequel il doit être possible d’effectuer des recherches afin de déterminer quelles erreurs applicatives sont les plus courantes
  • Contrôles d’intégrité – Sondes périodiques envoyées aux services afin de déterminer s’ils fonctionnent correctement au sein de l’application
  • Introspection de l’état d’exécution – Un ensemble d’API accessibles uniquement aux administrateurs et fournissant des informations sur l’état d’exécution de l’application
  • Vidages de tas/mémoire – Instantanés complets de l’état d’exécution d’un service ; pour nous, le facteur important est la facilité avec laquelle ces vidages peuvent être obtenus à la demande ou en cas de plantage d’un service

Comparaison des fonctionnalités des outils avec la liste de souhaits

Bien entendu, nous ne nous attendions pas à ce qu’un seul outil ou une seule approche open source réunisse toutes ces fonctionnalités, mais cette liste nous a au moins fourni une base idéale pour comparer les outils disponibles. Nous avons consulté la documentation de chaque outil afin de déterminer lesquelles des sept fonctionnalités de notre liste de souhaits il prend en charge. Le tableau résume nos conclusions.

Technologie Journalisation Traçage distribué Métriques Agrégation des erreurs Contrôles d’intégrité Introspection à l’exécution Vidages de tas/mémoireELK + Elastic APM ✅ ✅ ✅ ✅ ✅ ❌ ❌Grafana ✅ ✅ ✅ ✅ ✅ ❌ ❌Graylog ✅ ❌ ❌ ❌ ❌ ❌ ❌Jaeger ❌ ✅ ❌ ✅ ❌ ❌ ❌OpenCensus ❌ ❌ ✅ ❌ ❌ ❌ ❌OpenTelemetry Bêta ✅ ✅ ✅ ✅ * ❌ ❌Prometheus ❌ ❌ ✅ ❌ ❌ ❌ ❌StatsD ❌ ❌ ✅ ❌ ❌ ❌ ❌Zipkin ❌ ✅ ❌ ✅ ❌ ❌ ❌

* En tant qu’extension

La création de ce tableau nous a brutalement ramenés à la réalité. Les différents outils présentent de telles différences en matière de fonctionnalités et d’usages prévus qu’il nous était impossible de les considérer comme appartenant tous à la même catégorie : c’était comme comparer des pommes et des grille-pain !

À titre d’exemple, ELK (Elasticsearch-Logstash-Kibana, ainsi que Filebeat) et Zipkin remplissent des fonctions si fondamentalement différentes que tenter de les comparer ne fait qu’ajouter à la confusion. Malheureusement, la « dérive des objectifs » ne fait que brouiller davantage les pistes : sans doute en réponse aux demandes des utilisateurs, des fonctionnalités secondaires par rapport à la vocation principale de l’outil ont été ajoutées, créant ainsi des chevauchements avec d’autres outils. À première vue, ELK assure le stockage et la visualisation des journaux, tandis que Zipkin permet le traçage distribué. Mais dès que l’on examine d’un peu plus près la gamme de produits Elastic, on découvre rapidement Elastic APM, qui prend en charge le traçage distribué et offre même une compatibilité avec Jaeger.

Au-delà du problème de la dérive fonctionnelle, de nombreux outils peuvent être intégrés les uns aux autres, ce qui permet différentes combinaisons de collecteurs, d’agrégateurs, de tableaux de bord, etc. Certaines technologies sont compatibles entre elles, tandis que d’autres ne le sont pas.

Mener une enquête qualitative

Ainsi, ce tableau comparatif ne nous donnait pas une vision suffisamment précise pour éclairer notre choix. Nous devions mener une étude qualitative des objectifs, des principes directeurs et des orientations futures possibles de chaque projet, en partant du principe que plus les valeurs d’un projet sont proches des nôtres, plus nous avons de chances de rester compatibles au fil du temps. En consultant les pages des projets, nous avons immédiatement remarqué cela sur la page OpenCensus.

OpenCensus et OpenTracing ont fusionné pour former OpenTelemetry, qui constitue la prochaine version majeure d’OpenCensus et d’OpenTracing. OpenTelemetry assurera la rétrocompatibilité avec les intégrations OpenCensus existantes, et nous continuerons à fournir des correctifs de sécurité pour les bibliothèques OpenCensus existantes pendant deux ans.

C’était un élément déterminant à nos yeux. Nous savions que nous ne pouvions pas garantir la pérennité de notre choix, mais nous voulions au moins nous assurer qu’il bénéficierait durablement d’un solide soutien de la communauté. Grâce à cette information, nous pouvions rayer OpenCensus de notre liste de candidats. Utiliser Jaeger n’était probablement pas non plus une bonne idée, car il s’agit de l’implémentation de référence du projet OpenTracing, désormais officiellement obsolète : l’essentiel des nouvelles contributions sera intégré à OpenTelemetry.

Nous nous sommes ensuite renseignés sur l’OpenTelemetry Collector.

L’OpenTelemetry Collector propose une implémentation indépendante des fournisseurs permettant de recevoir, de traiter et d’exporter des données de télémétrie. En outre, il évite d’avoir à exécuter, exploiter et maintenir plusieurs agents/collecteurs pour prendre en charge des formats de données de télémétrie open source (par exemple, Jaeger, Prometheus, etc.) et les envoyer vers plusieurs back-ends open source ou commerciaux.

Le collecteur OpenTelemetry fait office d’agrégateur et nous permet de combiner différentes méthodes de collecte et d’instrumentation de l’observabilité avec différents backends. Concrètement, notre application peut collecter des traces avec Zipkin et des métriques provenant de Prometheus, que nous pouvons ensuite envoyer à un backend configurable et visualiser avec Grafana. De nombreuses autres variantes de cette architecture sont possibles. Nous pouvons donc tester différentes approches afin de déterminer quelles technologies conviennent le mieux à notre cas d’usage.

Intégrer OpenTelemetry pour aller de l’avant

En résumé, nous avons été séduits par le collecteur OpenTelemetry, car il nous permet en théorie de passer d’une technologie d’observabilité à une autre. Malgré le manque de maturité relatif du projet, nous avons décidé de nous lancer avec audace et d’utiliser OpenTelemetry uniquement avec des intégrations open source, car la seule façon de comprendre cet écosystème est d’utiliser concrètement ces technologies.

Cependant, certaines fonctionnalités font défaut à OpenTelemetry Collector. Nous devons donc nous appuyer sur d’autres technologies pour les assurer. Les sections suivantes résument nos choix et les raisons qui les ont motivés.

Mise en œuvre de la journalisation

La journalisation est un aspect de l’observabilité d’une simplicité trompeuse, qui conduit rapidement à des décisions complexes. Elle est simple, car il suffit de collecter les journaux générés par vos conteneurs, mais complexe, car il faut ensuite décider où stocker les données, comment les acheminer vers cet espace de stockage, comment les indexer pour les rendre exploitables et combien de temps les conserver. Pour être utiles, les fichiers journaux doivent pouvoir être facilement interrogés selon un nombre suffisant de critères afin de répondre aux besoins des différents utilisateurs.

Nous avons étudié la prise en charge de la journalisation par le collecteur OpenTelemetry et, à l’heure où nous écrivons ces lignes, celle-ci est encore très expérimentale. Nous avons décidé d’utiliser ELK pour la journalisation à court terme, tout en continuant à étudier d’autres options.

En l’absence de raison impérieuse de ne pas le faire, nous avons choisi par défaut d’utiliser les outils Elasticsearch, ce qui nous a permis de répartir le déploiement entre des nœuds d’ingestion, de coordination, maîtres et de données. Pour faciliter le déploiement, nous avons utilisé un chart Bitnami. Pour acheminer les données, nous avons déployé Filebeat dans le cadre d’un DaemonSet Kubernetes. Nous avons ajouté une fonctionnalité de recherche en déployant Kibana et en tirant parti des index, recherches, visualisations et tableaux de bord préchargés.

Il est assez vite apparu que, même si cette solution fonctionnait, la configuration par défaut était plutôt gourmande en ressources, ce qui rendait son exécution difficile dans un environnement aux ressources plus limitées tel que K3S ou Microk8s. L’ajout de la possibilité d’ajuster le nombre de réplicas pour chaque composant a permis de résoudre ce problème, mais a entraîné certaines défaillances potentiellement dues à un épuisement des ressources ou à des volumes de données excessifs.

Loin d’en être déçus, nous y voyons une occasion d’évaluer les performances de notre système de journalisation avec différentes configurations, ainsi que d’étudier d’autres solutions telles que Grafana Loki et Graylog. Il se peut que nous constations que les solutions de journalisation plus légères n’offrent pas toutes les fonctionnalités dont certains utilisateurs ont besoin et qu’ils peuvent trouver dans des outils plus gourmands en ressources. Compte tenu de la nature modulaire de MARA, nous créerons probablement des modules supplémentaires pour ces solutions afin d’offrir davantage de choix aux utilisateurs.

Mise en œuvre du traçage distribué

Au-delà du choix de l’outil offrant les fonctionnalités de traçage souhaitées, nous devions réfléchir à la manière de mettre en œuvre la solution et aux technologies à y intégrer.

Tout d’abord, nous voulions nous assurer qu’aucune instrumentation ne nuirait à la qualité de service de l’application elle-même. Nous avions tous déjà travaillé avec des systèmes dont les performances chutaient de manière prévisible une fois par heure lors de l’exportation des journaux, et nous ne souhaitions aucunement revivre cette expérience. L’architecture du collecteur OpenTelemetry était particulièrement intéressante à cet égard, car il est possible d’exécuter une instance par hôte. Chaque collecteur reçoit les données des clients et des agents de toutes les applications exécutées sur l’hôte (conteneurisées ou non). Il agrège et, éventuellement, compresse les données avant de les envoyer vers un système de stockage, ce qui nous semblait idéal.

Ensuite, nous avons évalué la prise en charge d’OpenTelemetry dans les différents langages de programmation et frameworks que nous utilisons dans l’application. À ce stade, les choses se sont quelque peu compliquées. Bien que nous n’utilisions que les deux langages de programmation et les frameworks associés présentés dans le tableau, le niveau de complexité s’est révélé étonnamment élevé.

Langage Framework Nombre de servicesJava Spring Boot 3Python Flask 3

Pour ajouter un traçage au niveau du langage, nous avons d’abord essayé les agents d’instrumentation automatique d’OpenTelemetry, mais nous avons trouvé leurs résultats surchargés et difficiles à interpréter. Nous sommes convaincus que cela s’améliorera à mesure que les bibliothèques d’instrumentation automatique gagneront en maturité, mais pour l’instant, nous avons écarté les agents OpenTelemetry et décidé d’intégrer le traçage directement dans notre code.

Avant de nous lancer dans l’implémentation directe du traçage dans le code, nous avons d’abord configuré OpenTelemetry Collector pour qu’il envoie toutes les données de traçage vers une instance locale de Jaeger, où nous pouvions plus facilement visualiser le résultat. Cela s’est révélé très utile, car nous pouvions expérimenter avec la représentation visuelle des données de traçage tout en déterminant comment intégrer pleinement OpenTelemetry. Par exemple, si nous constations qu’une bibliothèque cliente HTTP n’incluait pas de données de traçage lors d’appels à un service dépendant, nous ajoutions immédiatement ce problème à notre liste de correctifs. Jaeger offre une vue claire de l’ensemble des différents spans au sein d’une même trace :

Traçage distribué pour Python

L’ajout du traçage à notre code Python a été relativement simple. Nous avons ajouté deux fichiers sources Python auxquels tous nos services font référence, et mis à jour les fichiers requirements.txt correspondants afin d’y inclure les dépendances opentelemetry-instrumentation-* pertinentes. Nous avons ainsi pu utiliser la même configuration de traçage pour tous les services Python, inclure l’identifiant de trace de chaque requête dans les messages de journalisation et intégrer cet identifiant aux requêtes adressées aux services dépendants.

Traçage distribué pour Java

Nous nous sommes ensuite intéressés aux services Java. Utiliser directement les bibliothèques Java d’OpenTelemetry dans un nouveau projet est relativement simple : il suffit d’importer les bibliothèques nécessaires et d’utiliser directement l’API de traçage. Toutefois, si vous utilisez Spring comme nous, vous devrez faire des choix supplémentaires.

Spring dispose déjà d’une API de traçage distribué, « Spring Cloud Sleuth ». Elle fournit une façade à l’implémentation sous-jacente du traçage distribué qui, comme indiqué dans la documentation, remplit les fonctions suivantes :

  • Ajoute les identifiants de trace et de span au MDC de Slf4J, afin que vous puissiez extraire, dans un agrégateur de journaux, tous les journaux associés à une trace ou à un span donné.
  • Instrumente les points d’entrée et de sortie courants des applications Spring (filtre de servlet, RestTemplate, tâches planifiées, canaux de messages, client Feign).
  • Si spring-cloud-sleuth-zipkin est disponible, … [génère et transmet] des traces compatibles avec Zipkin via HTTP. Par défaut, il les envoie à un service de collecte Zipkin sur localhost (port 9411). Configurez l’emplacement du service à l’aide de spring.zipkin.baseUrl.

L’API nous permet également d’ajouter des traces aux tâches annotées avec « @Scheduled ».

En d’autres termes, Spring Cloud Sleuth nous permet à lui seul d’obtenir nativement des traces au niveau des points de terminaison des services HTTP, ce qui constitue un avantage appréciable. Comme notre projet utilise déjà Spring, nous avons décidé de tout conserver au sein de cet écosystème et de tirer parti des fonctionnalités proposées. Cependant, nous avons rencontré quelques problèmes lors de l’intégration de l’ensemble avec Maven :

  • Le module Spring Cloud Sleuth Autoconfigure est toujours disponible en version intermédiaire.
  • Le module Spring Cloud Sleuth Autoconfigure dépend d’une ancienne version alpha de opentelemetry-instrumentation-api. Il n’existe actuellement aucune version 1.x récente et non alpha de cette bibliothèque.
  • Le projet doit récupérer le modèle objet de projet (POM) parent spring-cloud-build depuis les dépôts Spring Snapshot en raison de la manière dont Spring Cloud Sleuth définit ses références de dépendances pour les versions intermédiaires.

Cela a quelque peu compliqué la définition de notre projet Maven, car nous avons dû configurer Maven pour qu’il récupère les dépendances depuis les dépôts Spring ainsi que depuis Maven Central, ce qui indiquait clairement à quel point la prise en charge d’OpenTelemetry dans Spring Cloud en était encore à ses débuts. Nous avons néanmoins poursuivi nos efforts et créé un module de télémétrie commun afin de configurer le traçage distribué à l’aide de Spring Cloud Sleuth et d’OpenTelemetry, ainsi que diverses fonctions utilitaires et extensions liées à la télémétrie.

Dans le module de télémétrie commun, nous étendons les fonctionnalités de traçage fournies par les bibliothèques Spring Cloud Sleuth et OpenTelemetry en proposant :

  • Compatibles avec Spring, les classes « Autoconfiguration » configurent le traçage et les fonctionnalités étendues du projet, et chargent des attributs de ressource de trace supplémentaires.
  • Implémentations NoOp des interfaces, afin de pouvoir désactiver le traçage au démarrage en injectant des instances NoOp dans toutes les interfaces de Spring Cloud Sleuth.
  • Un intercepteur de nommage des traces pour normaliser les noms des traces.
  • Un gestionnaire d’erreurs qui consigne les erreurs via slf4j et le traçage de Spring Cloud Sleuth.
  • Une implémentation améliorée des attributs de traçage qui encode des informations supplémentaires dans chaque trace émise, notamment la version du service, l’ID de l’instance de service, l’ID de la machine, le nom du pod, l’ID du conteneur, le nom du conteneur et le nom de l’espace de noms.
  • Un inspecteur d’instructions de traçage qui injecte des identifiants de trace dans les commentaires précédant les instructions SQL émises par Hibernate. Apparemment, cela peut désormais être réalisé avec SQLCommenter, mais nous n’avons pas encore effectué la migration.

De plus, nous avons implémenté un client HTTP compatible avec Spring reposant sur Apache HTTP Client, car nous souhaitions disposer de davantage de métriques et de possibilités de personnalisation pour les appels HTTP entre services. Dans cette implémentation, les identifiants de trace et de segment sont transmis sous forme d’en-têtes lors des appels aux services dépendants, ce qui permet de les inclure dans les données de traçage. En outre, cette implémentation fournit des métriques sur le pool de connexions HTTP, qui sont agrégées par OpenTelemetry.

En définitive, la mise en place du traçage avec Spring Cloud Sleuth et OpenTelemetry a été laborieuse, mais nous pensons que le jeu en valait la chandelle. Nous espérons que ce projet et cet article éclaireront la voie à celles et ceux qui souhaitent s’engager dans cette direction.

Traçage distribué pour NGINX

Pour obtenir des traces reliant tous les services pendant l’intégralité du cycle de vie d’une requête, nous avons dû intégrer OpenTelemetry à NGINX. Nous avons utilisé le module OpenTelemetry pour NGINX (encore en version bêta) à cette fin. Comme il pouvait s’avérer difficile d’obtenir des fichiers binaires opérationnels du module compatibles avec toutes les versions de NGINX, nous avons créé un dépôt GitHub contenant des images de conteneurs qui intègrent des modules NGINX non pris en charge. Nous effectuons des compilations chaque nuit et distribuons le fichier binaire du module au moyen d’images Docker faciles à importer.

Nous n’avons pas encore intégré ce processus au processus de compilation de NGINX Ingress Controller dans le projet MARA, mais nous prévoyons de le faire prochainement.

Mise en œuvre de la collecte de métriques

Après avoir intégré le traçage avec OpenTelemetry, nous nous sommes concentrés sur les métriques. Il n’existait aucune métrique pour nos applications basées sur Python, et nous avons décidé de reporter leur ajout pour le moment. Pour les applications Java, le code source d’origine de Bank of Anthos, qui utilise Micrometer conjointement avec Stackdriver de Google Cloud, prend en charge les métriques. Cependant, après avoir créé Bank of Sirius à partir d’un fork de Bank of Anthos, nous avons supprimé ce code, car il ne nous permettait pas de configurer un backend de métriques. Néanmoins, la présence de points d’instrumentation pour les métriques témoignait déjà de la nécessité d’une intégration appropriée des métriques.

Afin de trouver une solution configurable et pragmatique, nous avons d’abord examiné la prise en charge des métriques dans les bibliothèques Java d’OpenTelemetry et dans Micrometer. En recherchant des comparaisons entre ces technologies, nous avons trouvé plusieurs résultats qui énuméraient les lacunes d’OpenTelemetry en tant qu’API de métriques dans la JVM, même si, au moment de la rédaction de cet article, les métriques OpenTelemetry étaient encore en version alpha. Micrometer est une couche de façade mature pour les métriques dans la JVM, comparable à slf4j, qui fournit une API commune servant d’interface à une implémentation configurable des métriques, plutôt que de constituer elle-même une telle implémentation. Fait intéressant, il s’agit de l’API de métriques par défaut de Spring.

À ce stade, nous avons pris en compte les éléments suivants :

  • Le collecteur OpenTelemetry peut ingérer des métriques provenant de pratiquement n’importe quelle source, notamment Prometheus, StatsD et le protocole natif OpenTelemetry Protocol (OTLP)
  • Micrometer prend également en charge un grand nombre de backends de métriques, notamment Prometheus et StatsD
  • Le SDK OpenTelemetry Metrics pour la JVM permet d’envoyer des métriques via OTLP

Après quelques expérimentations, nous avons décidé que l’approche la plus pragmatique consistait à utiliser la façade Micrometer avec une implémentation Prometheus sous-jacente, et à configurer le collecteur OpenTelemetry afin qu’il utilise l’API Prometheus pour récupérer les métriques de l’application. De nombreux articles nous avaient appris que l’absence de certains types de métriques dans OpenTelemetry pouvait poser problème, mais comme nos cas d’usage n’en avaient pas besoin, ce compromis était acceptable.

Nous avons découvert une particularité intéressante de l’OpenTelemetry Collector : bien que nous l’ayons configuré pour recevoir les traces via OTLP et les métriques via l’API Prometheus, il peut également être configuré pour envoyer ces deux types de données à un récepteur de données externe à l’aide d’OTLP ou de tout autre protocole pris en charge. Nous avons ainsi pu facilement tester notre application avec LightStep.

Dans l’ensemble, l’implémentation des métriques en Java a été assez simple, car nous les avons écrites conformément à l’API Micrometer, pour laquelle de nombreux exemples et tutoriels sont disponibles. Le plus difficile, tant pour les métriques que pour le traçage, a probablement été de configurer correctement le graphe des dépendances Maven dans le fichier pom.xml de telemetry-common.

Mise en œuvre de l’agrégation des erreurs

Le projet OpenTelemetry n’a pas pour mission à proprement parler d’agréger les erreurs et ne propose pas une mise en œuvre du marquage des erreurs aussi élégante que des solutions telles que Sentry ou Honeybadger.io. Néanmoins, nous avons décidé d’utiliser OpenTelemetry pour agréger les erreurs à court terme plutôt que d’ajouter un autre outil. Avec un outil comme Jaeger, nous pouvons rechercher error=true afin de trouver toutes les traces présentant une erreur. Cela nous donne au moins une idée des problèmes qui surviennent fréquemment. À l’avenir, nous envisagerons peut-être d’ajouter une intégration avec Sentry.

Mise en œuvre des contrôles d’intégrité et de l’introspection à l’exécution

Dans le contexte de notre application, les contrôles d’intégrité permettent à Kubernetes de savoir si un service fonctionne correctement ou s’il a terminé sa phase de démarrage. Si le service n’est pas opérationnel, Kubernetes peut être configuré pour arrêter ou redémarrer les instances. Dans notre application, nous avons décidé de ne pas utiliser les contrôles d’intégrité d’OpenTelemetry, car nous avons estimé que la documentation était insuffisante.

À la place, pour les services JVM, nous utilisons un projet Spring appelé « Spring Boot Actuator », qui fournit non seulement des points de terminaison de contrôle d’intégrité, mais aussi des points de terminaison d’introspection à l’exécution et de vidage du tas. Pour les services Python, nous utilisons le module Flask Management Endpoints, qui offre un sous-ensemble des fonctionnalités de Spring Boot Actuator. À l’heure actuelle, il fournit uniquement des informations personnalisables sur l’application et des contrôles d’intégrité.

Spring Boot Actuator s’intègre à la JVM et à Spring pour fournir des points de terminaison d’introspection, de surveillance et de contrôle d’intégrité. Il offre également un framework permettant d’ajouter des informations personnalisées aux données présentées par défaut sur ses points de terminaison. Ceux-ci permettent d’examiner à l’exécution des éléments tels que l’état du cache, l’environnement d’exécution, les migrations de bases de données, les contrôles d’intégrité, les informations personnalisables sur l’application, les métriques, les tâches périodiques, l’état des sessions HTTP et les vidages de threads.

Les points de terminaison de contrôle d’intégrité mis en œuvre par Spring Boot Actuator bénéficient d’une configuration modulaire, de sorte que l’état d’un service peut être évalué à partir de plusieurs contrôles individuels classés soit comme contrôles de vivacité, soit comme contrôles de disponibilité. Un contrôle d’intégrité complet affichant tous les modules de contrôle est également disponible et se présente généralement comme ceci.

Les points de terminaison d’information sont définis dans un document JSON sous la forme d’un objet JSON unique de haut niveau et d’une série de clés et de valeurs hiérarchiques. En règle générale, le document indique le nom et la version du service, l’architecture, le nom d’hôte, les informations sur le système d’exploitation, l’identifiant du processus, le nom de l’exécutable ainsi que des informations détaillées sur l’hôte, telles qu’un identifiant de machine ou un identifiant de service unique.

Implémentation des dumps de tas et des dumps mémoire

Vous vous souvenez peut-être du tableau de la section « Comparaison des fonctionnalités des outils avec la liste de souhaits », qui indique qu’aucun des outils ne prend en charge l’introspection à l’exécution ni les vidages du tas ou de la mémoire. Cependant, Spring, notre framework sous-jacent, prend en charge ces deux fonctionnalités, même s’il a fallu un certain travail pour intégrer les fonctions d’observabilité à l’application. Comme indiqué dans la section précédente, nous utilisons, pour l’introspection à l’exécution, un module Python conjointement avec Spring Boot Actuator.

De même, pour les dumps de tas, nous utilisons les points de terminaison de dump des threads fournis par Spring Boot Actuator afin de bénéficier d’une partie des fonctionnalités souhaitées. Nous ne pouvons obtenir ni dumps mémoire à la demande, ni dumps de tas de la JVM avec le niveau de granularité idéal, mais nous disposons ainsi de certaines des fonctionnalités recherchées moyennant peu d’efforts supplémentaires. Malheureusement, la prise en charge des dumps mémoire pour les services Python nécessitera un travail supplémentaire conséquent, que nous avons reporté à une date ultérieure.

Résumé

Après avoir beaucoup pleuré et remis en question nos choix de vie, nous avons fini par nous accorder sur l’utilisation des technologies suivantes pour l’observabilité dans le MARA (dans la suite, « OTel » désigne OpenTelemetry) :

  • Journalisation (pour tous les conteneurs) – Filebeat → Elasticsearch / Kibana
  • Traçage distribué
    • Java – Spring Cloud Sleuth → exportateur Spring Cloud Sleuth pour OTel → collecteur OTel → exportateurs interchangeables tels que Jaeger, Lightstep, Splunk, etc.
    • Python – Bibliothèques Python OTel → Collecteur OTel → stockage interchangeable
    • NGINX et NGINX Plus (NGINX Ingress Controller n’est pas encore pris en charge) – module OTel de NGINX → collecteur OTel → exportateur interchangeable
  • Collecte de métriques
    • Java – Micrometer via Spring → exportateur Prometheus → collecteur OTel
    • Python – Aucune implémentation pour le moment
    • Python WSGI – GUnicorn StatsD → Prometheus (via StatsD / ServiceMonitor)
    • NGINX – point de terminaison Prometheus → Prometheus (via ServiceMonitor)
  • Agrégation des erreurs – traces distribuées OTel → exportateur interchangeable → fonctionnalité de recherche de l’exportateur permettant de trouver les traces marquées comme error
  • Contrôles d’intégrité
    • Java – Spring Boot Actuator → Kubernetes
    • Python – Module Flask Management Endpoints → Kubernetes
  • Introspection à l’exécution
    • Java – Spring Boot Actuator
    • Python – module Flask Management Endpoints
  • Vidages de tas/mémoire centrale
    • Java – Prise en charge des vidages de threads par Spring Boot Actuator
    • Python – Pas encore pris en charge

Cette implémentation est un instantané à un moment donné. Elle changera et évoluera sans aucun doute à mesure que le développement se poursuivra. Nous soumettrons bientôt l’application à des tests de charge approfondis. Nous nous attendons à en apprendre beaucoup sur les limites de notre approche en matière d’observabilité et à ajouter des fonctionnalités d’observabilité supplémentaires.

Nous vous invitons à essayer l’architecture de référence pour les applications modernes ainsi que l’application exemple (Bank of Sirius). Si vous avez des idées pour nous aider à nous améliorer, vos contributions sont les bienvenues sur notre dépôt GitHub !

Articles connexes

Cet article fait partie d’une série. À mesure que nous enrichissons les fonctionnalités de MARA, nous en publions les détails sur le blog :


Partager

Articles de blog associés

Tendances actuelles des technologies cloud natives, de l’ingénierie de plateforme et de l’IA
NGINX | 13/10/2025

Tendances actuelles des technologies cloud natives, de l’ingénierie de plateforme et de l’IA

Découvrez les principaux enseignements tirés des réponses à l’enquête annuelle F5 NGINX sur les dernières tendances en matière de technologies cloud natives, de sécurité, d’ingénierie de plateforme et d’IA.

Quels contrôleurs Ingress NGINX sont affectés par les vulnérabilités CVE-2022-4886, CVE-2023-5043 et CVE-2023-5044 ?
NGINX | 03/11/2023

Quels contrôleurs Ingress NGINX sont affectés par les vulnérabilités CVE-2022-4886, CVE-2023-5043 et CVE-2023-5044 ?

Lorsque de nouvelles CVE sont signalées, il est facile de ne plus savoir si elles concernent un outil de contrôleur Ingress NGINX particulier, car il existe plusieurs projets. Cet article présente les CVE et explique comment distinguer les différents projets de contrôleurs Ingress afin de dissiper toute confusion.

Votre API est-elle en temps réel ? Testez sa latence avec l’outil rtapi de NGINX
NGINX | 11/03/2020

Votre API est-elle en temps réel ? Testez sa latence avec l’outil rtapi de NGINX

Votre API répond-elle aux requêtes en moins de 30 ms ? Si ce n’est pas le cas, il ne s’agit pas d’une API en temps réel offrant la faible latence exigée par la grande majorité des organisations. Mesurez la latence de votre API avec l’outil rtapi de NGINX et découvrez comment NGINX peut l’aider à offrir une réactivité en temps réel.

Un testeur d’expressions régulières pour NGINX et NGINX Plus
NGINX | 11/06/2019

Un testeur d’expressions régulières pour NGINX et NGINX Plus

Le testeur d’expressions régulières (regex) pour NGINX et NGINX Plus vous évite de tâtonner en vous indiquant si une expression régulière utilisée dans un bloc location ou map correspond aux valeurs comme prévu. Il génère la configuration appropriée et affiche les valeurs des groupes de capture.

Caches partagés avec les clusters de cache NGINX Plus, partie 1
NGINX | 24/01/2017

Caches partagés avec les clusters de cache NGINX Plus, partie 1

Optimisez la capacité de votre cache web en le répartissant entre plusieurs serveurs de cache web NGINX ou NGINX Plus. Découvrez comment procéder dans cet article.

Introduction aux microservices
NGINX | 19/05/2015

Introduction aux microservices

Les microservices suscitent actuellement beaucoup d’intérêt. Cet article de blog est le premier d’une série de sept consacrée à la conception, au développement et au déploiement de microservices.