Lorsque nous avons présenté le projet NGINX Modern Apps Reference Architecture (MARA) l'automne dernier au Sprint 2.0, nous avons souligné notre intention qu'il ne s'agisse pas d'un « jouet » comme certaines architectures, mais plutôt d'une solution « solide, testée et prête à être déployée dans des applications de production en direct exécutées dans des environnements Kubernetes ». Pour un tel projet, les outils d’observabilité sont une exigence absolue. Tous les membres de l’équipe MARA ont pu constater de visu à quel point le manque de visibilité sur l’état et les performances transforme le développement et la livraison des applications en un exercice de frustration. Nous sommes immédiatement parvenus à un consensus sur le fait que le MARA doit inclure une instrumentation pour le débogage et le traçage dans un environnement de production.
Un autre principe directeur du MARA est la préférence pour les solutions open source. Dans cet article, nous décrivons comment notre recherche d'un outil d'observabilité multifonctionnel et open source nous a conduit à OpenTelemetry , puis détaillons les compromis, les décisions de conception, les techniques et les technologies que nous avons utilisées pour intégrer OpenTelemetry à une application de microservices construite avec Python, Java et NGINX.
Nous espérons que nos expériences vous aideront à éviter les pièges potentiels et à accélérer votre propre adoption d’OpenTelemetry. Veuillez noter que cet article constitue un rapport d’étape limité dans le temps. Nous prévoyons que les technologies dont nous discutons arriveront à maturité d’ici un an. De plus, même si nous soulignons les lacunes actuelles de certains projets, nous sommes extrêmement reconnaissants pour tout le travail open source effectué et sommes impatients de le voir progresser.
En tant qu'application à intégrer à une solution d'observabilité, nous avons choisi Bank of Sirius , notre fork de l'exemple d'application Bank of Anthos de Google. Il s'agit d'une application Web avec une architecture de microservices qui peut être déployée via une infrastructure en tant que code . Il existe de nombreuses façons d'améliorer cette application en termes de performances et de fiabilité, mais elle est suffisamment mature pour être raisonnablement considérée comme une application brownfield . En tant que tel, nous pensons qu’il s’agit d’un bon exemple pour montrer comment intégrer OpenTelemetry dans une application, car en théorie, le traçage distribué donne des informations précieuses sur les lacunes d’une architecture d’application.
Comme le montre le diagramme, les services qui soutiennent l’application sont relativement simples.
Notre chemin pour sélectionner OpenTelemetry a été assez sinueux et a comporté plusieurs étapes.
Avant d’évaluer les outils d’observabilité open source disponibles, nous avons identifié les aspects de l’observabilité qui nous intéressent. Sur la base de nos expériences passées, nous avons établi la liste suivante.
Bien sûr, nous ne nous attendions pas à ce qu’un seul outil ou une seule approche open source inclue toutes ces fonctionnalités, mais au moins cela nous a donné une base ambitieuse sur laquelle comparer les outils disponibles. Nous avons consulté la documentation de chaque outil pour savoir lesquelles des sept fonctionnalités de notre liste de souhaits il prend en charge. Le tableau résume nos conclusions.
Technologie | Enregistrement | Traçage distribué | Métrique | Agrégation des erreurs | Contrôles de santé | Introspection d'exécution | Vidages de tas/de mémoire |
---|---|---|---|---|---|---|---|
ELK + APM élastique | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
Grafana | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
Journal de bord gris | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Jaeger | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
Recensement ouvert | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
Télémétrie ouverte | Bêta | ✅ | ✅ | ✅ | ✅ * | ❌ | ❌ |
Prométhée | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
StatistiquesD | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
Zipkin | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
* En tant qu'extension
Construire cette table a été un réveil brutal. Les différents outils diffèrent tellement dans leurs capacités et leurs objectifs que nous ne pouvions pas tous les considérer comme membres de la même catégorie – c’était comme comparer des pommes à des grille-pain !
À titre d’exemple, ELK ( Elasticsearch-Logstash-Kibana , plus Filebeat ) et Zipkin font des choses tellement différentes qu’essayer de les comparer est plus déroutant qu’autre chose. Malheureusement, le « dépassement de mission » ne fait que brouiller encore davantage les pistes : sans doute en réponse aux demandes des utilisateurs, des fonctionnalités ont été ajoutées qui sont secondaires par rapport à l'objectif principal de l'outil et créent un chevauchement avec d'autres outils. En surface, ELK effectue le stockage et la visualisation des journaux tandis que Zipkin effectue le traçage distribué. Mais lorsque vous plongez un peu plus profondément dans le portefeuille de produits Elastic, vous tombez rapidement sur Elastic APM , qui prend en charge le traçage distribué et est même compatible avec Jaeger .
Au-delà de la question de la dérive des missions, de nombreux outils peuvent être intégrés les uns aux autres, ce qui donne lieu à différentes combinaisons de collecteurs, d’agrégateurs, de tableaux de bord, etc. Certaines technologies sont compatibles entre elles et d’autres non.
En tant que tel, ce tableau comparatif ne nous a pas donné une image suffisamment précise pour fonder notre choix. Nous devions effectuer une enquête qualitative sur les objectifs, les principes directeurs et l’orientation future possible de chaque projet, en partant du principe que plus les valeurs d’un projet sont similaires aux nôtres, plus nous aurons de chances de rester compatibles au fil du temps. En visitant les pages du projet, nous avons immédiatement remarqué cela sur la page OpenCensus .
OpenCensus et OpenTracing ont fusionné pour former OpenTelemetry, qui sert de prochaine version majeure d'OpenCensus et OpenTracing. OpenTelemetry offrira une compatibilité descendante avec les intégrations OpenCensus existantes, et nous continuerons à apporter des correctifs de sécurité aux bibliothèques OpenCensus existantes pendant deux ans.
C’était une donnée clé pour nous. Nous savions que nous ne pouvions pas garantir que notre choix serait à l’épreuve du temps, mais nous voulions au moins savoir qu’il bénéficierait d’un soutien solide de la communauté à l’avenir. Grâce à ces informations, nous pourrions rayer OpenCensus de notre liste de candidats. Ce n'était probablement pas non plus une bonne idée d'utiliser Jaeger, car c'est l'implémentation de référence du projet OpenTracing , désormais officiellement obsolète – la majeure partie des nouvelles contributions va atterrir dans OpenTelemetry.
Ensuite, nous avons lu sur OpenTelemetry Collector .
OpenTelemetry Collector propose une implémentation indépendante du fournisseur sur la manière de recevoir, de traiter et d'exporter des données de télémétrie. De plus, il élimine le besoin d'exécuter, d'exploiter et de maintenir plusieurs agents/collecteurs afin de prendre en charge les formats de données de télémétrie open source (par exemple, Jaeger, Prometheus, etc.) envoyés à plusieurs back-ends open source ou commerciaux.
Le collecteur OpenTelemetry agit comme un agrégateur qui nous permet de mélanger et d'associer différentes méthodes de collecte et d'instrumentation d'observabilité avec différents backends. Fondamentalement, notre application peut collecter des traces avec Zipkin et des métriques de Prometheus , que nous pouvons ensuite envoyer à un backend configurable et visualiser avec Grafana . Plusieurs autres permutations de cette conception sont possibles, nous pouvons donc essayer différentes approches pour voir quelles technologies conviennent le mieux à notre cas d'utilisation.
Fondamentalement, nous avons été vendus sur OpenTelemetry Collector parce qu’en théorie, il nous permet de basculer entre les technologies d’observabilité. Malgré l’immaturité relative du projet, nous avons décidé de nous lancer avec audace et d’utiliser OpenTelemetry avec uniquement des intégrations open source, car la seule façon de comprendre le paysage est d’utiliser les technologies.
Il manque cependant quelques éléments à OpenTelemetry Collector, nous devons donc nous appuyer sur d'autres technologies pour ces fonctionnalités. Les sections suivantes résument nos choix et le raisonnement qui les sous-tend.
La journalisation est une partie de l’observabilité d’une simplicité trompeuse qui conduit rapidement à des décisions compliquées. C'est simple car vous récoltez simplement la sortie du journal de vos conteneurs, mais compliqué car vous devez ensuite décider où stocker les données, comment les transporter vers ce magasin, comment les indexer pour les rendre utiles et combien de temps les conserver. Pour être utiles, les fichiers journaux doivent être facilement consultables selon suffisamment de critères différents pour répondre aux besoins des différents chercheurs.
Nous avons examiné la prise en charge de la journalisation avec OpenTelemetry Collector et au moment de la rédaction de cet article, elle est encore en version bêta. Nous avons décidé d’utiliser ELK pour l’exploitation forestière à court terme tout en continuant à étudier d’autres options.
En l'absence de raison impérieuse de ne pas le faire, nous avons opté par défaut pour l'utilisation des outils Elasticsearch , ce qui nous a permis de diviser le déploiement en nœuds d'ingestion, de coordination, maîtres et de données . Pour un déploiement plus facile, nous avons utilisé un graphique Bitnami . Pour transporter des données, nous avons déployé Filebeat dans le cadre d'un DaemonSet Kubernetes. La fonctionnalité de recherche a été ajoutée en déployant Kibana et en tirant parti des index, des recherches, des visualisations et des tableaux de bord préchargés.
Assez rapidement, il est devenu évident que même si cette solution fonctionnait, la configuration par défaut était assez gourmande en ressources, ce qui rendait difficile son exécution dans une empreinte de ressources plus petite comme K3S ou Microk8s . L’ajout de la possibilité de régler le nombre de répliques pour chaque composant a résolu ce problème, mais a entraîné des échecs potentiellement dus à l’épuisement des ressources ou à des volumes excessifs de données.
Loin d’être déçus par cela, nous le considérons comme une opportunité de comparer notre système de journalisation avec différentes configurations, ainsi que d’étudier d’autres options telles que Grafana Loki et Graylog . Nous pourrions bien découvrir que les solutions de journalisation plus légères ne fournissent pas l’ensemble complet de fonctionnalités dont certains utilisateurs ont besoin et peuvent obtenir à partir d’outils plus gourmands en ressources. Étant donné la nature modulaire de MARA , nous allons probablement créer des modules supplémentaires pour ces options afin d'offrir plus de choix aux utilisateurs.
Au-delà de déterminer quel outil fournit la fonctionnalité de traçage que nous souhaitons, nous devions réfléchir à la manière de mettre en œuvre la solution et aux technologies qui doivent y être intégrées.
Tout d’abord, nous voulions nous assurer qu’aucune instrumentation n’affecterait négativement la qualité de service de l’application elle-même. Nous avions tous travaillé avec des systèmes où les performances chutaient de manière prévisible une fois par heure pendant l’exportation des journaux, et nous n’avions aucune envie de revivre cette expérience. L’architecture d’OpenTelemetry Collector était convaincante à cet égard car vous pouvez exécuter une instance par hôte. Chaque collecteur reçoit des données des clients et des agents de toutes les différentes applications exécutées sur l'hôte (conteneurisées ou non). Le collecteur agrège et compresse potentiellement les données avant de les envoyer vers un backend de stockage, ce qui semblait idéal.
Ensuite, nous avons évalué la prise en charge d’OpenTelemetry dans les différents langages et frameworks de programmation que nous utilisons dans l’application. Ici, les choses sont devenues un peu plus compliquées. Malgré l’utilisation uniquement des deux langages de programmation et des frameworks associés présentés dans le tableau, le niveau de complexité était étonnamment élevé.
Langue | Cadre | Nombre de services |
---|---|---|
Java | Botte de printemps | 3 |
Python | Ballon | 3 |
Pour ajouter un traçage au niveau de la langue, nous avons d’abord essayé les agents d’instrumentation automatique d’OpenTelemetry, mais nous avons constaté que leur sortie était encombrée et déroutante. Nous sommes sûrs que cela s'améliorera à mesure que les bibliothèques d'instrumentation automatique mûriront, mais pour le moment, nous avons exclu les agents OpenTelemetry et avons décidé d'intégrer le traçage dans notre code.
Avant de passer à une implémentation directe du traçage dans le code, nous avons d’abord câblé le collecteur OpenTelemetry pour générer toutes les données de trace vers une instance Jaeger exécutée localement où nous pouvions voir la sortie plus facilement. Cela a été très utile, car nous avons pu jouer avec la présentation visuelle des données de traçage pendant que nous découvrions comment intégrer pleinement OpenTelemetry. Par exemple, si nous constatons qu’une bibliothèque cliente HTTP n’inclut pas de données de traçage lors des appels à un service dépendant, nous mettons immédiatement le problème sur notre liste de correctifs. Jaeger présente une belle vue de toutes les différentes portées d'une seule trace :
L’ajout de trace à notre code Python était relativement simple. Nous avons ajouté deux fichiers sources Python référencés par tous nos services et mis à jour les fichiers requirements.txt respectifs pour inclure les dépendances opentelemetry-instrumentation-*
pertinentes. Cela signifiait que nous pouvions utiliser la même configuration de traçage pour tous les services Python, ainsi qu'inclure l'ID de trace de chaque requête dans les messages de journal et intégrer l'ID de trace dans les requêtes aux services dépendants.
Ensuite, nous avons tourné notre attention vers les services Java. L’utilisation des bibliothèques Java OpenTelemetry directement dans un projet greenfield est relativement simple : il vous suffit d’importer les bibliothèques nécessaires et d’utiliser directement l’API de traçage . Cependant, si vous utilisez Spring comme nous, vous avez des décisions supplémentaires à prendre.
Spring dispose déjà d'une API de traçage distribuée, Spring Cloud Sleuth . Il fournit une façade sur l'implémentation de traçage distribuée sous-jacente qui effectue les opérations suivantes, comme décrit dans la documentation :
spring-cloud-sleuth-zipkin
est disponible, … [génère et signale] des traces compatibles 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 @Scheduled
.
En d'autres termes, en utilisant Spring Cloud Sleuth seul, nous obtenons des traces au niveau des points de terminaison du service HTTP prêts à l'emploi, ce qui constitue un avantage appréciable. Comme notre projet utilise déjà Spring, nous avons décidé de tout conserver dans ce cadre et d'utiliser les fonctionnalités fournies. Cependant, nous avons découvert quelques problèmes alors que nous connections le tout avec Maven :
opentelemetry-instrumentation-api
. Il n'existe actuellement aucune version récente non alpha 1. x de cette bibliothèque.spring-cloud-build
des référentiels Spring Snapshot en raison de la manière dont Spring Cloud Sleuth code ses références de dépendance pour les versions importantes.Cela a rendu notre définition de projet Maven un peu plus compliquée, car nous avons dû faire en sorte que Maven récupère des données à partir des référentiels Spring ainsi que de Maven Central, une indication claire du stade précoce de la prise en charge d'OpenTelemetry dans Spring Cloud. Néanmoins, nous avons continué à avancer et créé un module de télémétrie commun pour configurer le traçage distribué à l’aide de Spring Cloud Sleuth et OpenTelemetry, avec diverses fonctions d’assistance et extensions liées à la télémétrie.
Dans le module de télémétrie commun, nous étendons la fonctionnalité de traçage fournie par les bibliothèques Spring Cloud Sleuth et OpenTelemetry en fournissant :
d’autoconfiguration
activées par Spring qui configurent le traçage et les fonctionnalités étendues pour le projet et chargent des attributs de ressources de trace supplémentaires.De plus, nous avons implémenté un client HTTP compatible Spring qui s’appuie sur le client HTTP Apache, car nous voulions plus de métriques et de personnalisation lors des appels HTTP entre les services. Dans cette implémentation, les identifiants de trace et d'étendue sont transmis sous forme d'en-têtes lorsque les services dépendants sont appelés, ce qui leur permet d'être inclus dans la sortie de traçage. De plus, cette implémentation fournit des métriques de pool de connexions HTTP qui sont agrégées par OpenTelemetry.
Dans l’ensemble, cela a été un travail de longue haleine de mettre en place le traçage avec Spring Cloud Sleuth et OpenTelemetry, mais nous pensons que cela en valait la peine. Nous espérons que ce projet et cet article aideront à éclairer la voie à d’autres qui souhaitent emprunter cette voie.
Pour obtenir des traces reliant tous les services pendant tout le cycle de vie d'une requête, nous avons dû intégrer OpenTelemetry dans NGINX. Nous avons utilisé à cet effet le module OpenTelemetry NGINX (encore en version bêta). Anticipant qu’il pourrait être potentiellement difficile d’obtenir des binaires fonctionnels du module qui fonctionnent avec toutes les versions de NGINX, nous avons créé un référentiel GitHub d’images de conteneurs qui intègrent des modules NGINX non pris en charge. Nous exécutons des builds nocturnes et distribuons le binaire du module via des images Docker faciles à importer.
Nous n’avons pas encore intégré ce processus dans le processus de construction du contrôleur d’entrée NGINX dans le projet MARA , mais nous prévoyons de le faire bientôt.
Après avoir terminé l’intégration du traçage dans OpenTelemetry, nous nous sommes ensuite concentrés sur les métriques. Il n’existait aucune mesure 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 original de Bank of Anthos , qui utilise Micrometer en conjonction avec Stackdriver de Google Cloud, prend en charge les métriques. Cependant, nous avons supprimé ce code de Bank of Sirius après avoir bifurqué Bank of Anthos, car il ne nous permettait pas de configurer un backend de métriques. Néanmoins, le fait que les crochets métriques soient déjà présents témoigne de la nécessité d’une intégration appropriée des métriques.
Pour trouver une solution configurable et pragmatique, nous avons d’abord examiné la prise en charge des métriques dans les bibliothèques Java OpenTelemetry et Micrometer. À partir d'une recherche de comparaisons entre les technologies, un certain nombre de résultats ont énuméré les lacunes d'OpenTelemetry utilisé comme API de métriques dans la JVM, même si les métriques d'OpenTelemetry sont encore en version alpha au moment de la rédaction. Micrometer est une couche de façade de métriques mature pour la JVM, semblable à slf4j en fournissant un wrapper d'API commun qui met en avant une implémentation de métriques configurables plutôt qu'une implémentation de métriques qui lui est propre. Il est intéressant de noter qu’il s’agit de l’API de métriques par défaut pour Spring.
À ce stade, nous évaluions les faits suivants :
Après quelques expériences, nous avons décidé que l'approche la plus pragmatique était d'utiliser la façade Micrometer avec une implémentation de support Prometheus et de configurer OpenTelemetry Collector pour utiliser l'API Prometheus pour extraire les métriques de l'application. Nous savions, grâce à de nombreux articles, que l’absence de types de métriques dans OpenTelemetry pouvait causer des problèmes, mais nos cas d’utilisation n’ont pas besoin de ces types, donc le compromis était acceptable.
Nous avons découvert une chose intéressante à propos d'OpenTelemetry Collector : même si nous l'avions configuré pour recevoir des traces via OTLP et des métriques via l'API Prometheus, il peut toujours être configuré pour envoyer les deux types de données à un récepteur de données externe en utilisant OTLP ou tout autre protocole pris en charge. Cela nous a permis de tester facilement notre application avec LightStep .
Dans l’ensemble, le codage des métriques en Java était plutôt simple car nous les avons écrites pour être conformes à l’API Micrometer qui propose de nombreux exemples et tutoriels disponibles. La chose la plus difficile, tant pour les métriques que pour le traçage, a probablement été d'obtenir le graphique de dépendance Maven dans le fichier pom.xml pour telemetry-common
.
Le projet OpenTelemetry n'inclut pas l'agrégation d'erreurs dans sa mission en soi, et il ne fournit pas une implémentation aussi élégante du balisage des erreurs que des solutions comme Sentry ou Honeybadger.io . Néanmoins, nous avons décidé d’utiliser OpenTelemetry pour l’agrégation des erreurs à court terme plutôt que d’ajouter un autre outil. Avec un outil comme Jaeger, nous pouvons rechercher error=true
pour trouver toutes les traces avec une condition d'erreur. Cela nous donne au moins une idée de ce qui ne va pas habituellement. À l’avenir, nous pourrions envisager d’ajouter l’intégration de Sentry .
Dans le contexte de notre application, les contrôles de santé permettent à Kubernetes de savoir si un service est sain ou a terminé sa phase de démarrage. Dans les cas où le service n’est pas sain, 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 de santé OpenTelemetry car nous avons trouvé la documentation insuffisante.
Au lieu de cela, pour les services JVM, nous utilisons un projet Spring appelé Spring Boot Actuator qui fournit non seulement des points de terminaison de vérification de l’état, mais également des points de terminaison d’introspection d’exécution et de vidage de tas. Pour les services Python, nous utilisons le module Flask Management Endpoints qui fournit un sous-ensemble de fonctionnalités de Spring Boot Actuator. Actuellement, il fournit uniquement des informations d'application personnalisables et des contrôles de santé.
Spring Boot Actuator se connecte à la JVM et à Spring pour fournir des points de terminaison d'introspection, de surveillance et de contrôle de l'état. De plus, il fournit un cadre permettant d’ajouter des informations personnalisées aux données par défaut qu’il présente à ses points de terminaison. Les points de terminaison fournissent une introspection d'exécution sur des éléments tels que l'état du cache, l'environnement d'exécution, les migrations de bases de données, les contrôles de santé, les informations d'application personnalisables, les métriques, les tâches périodiques, l'état de la session HTTP et les vidages de threads.
Les points de terminaison de vérification de l'état tels qu'implémentés par Spring Boot Actuator ont une configuration modulaire de sorte que l'état d'un service peut être composé de plusieurs vérifications individuelles classées comme actives ou prêtes. Un contrôle de santé complet qui affiche tous les modules de contrôle est également disponible et ressemble généralement à ceci .
Les points de terminaison d’information sont définis dans un document JSON sous la forme d’un seul objet JSON de haut niveau et d’une série de clés et de valeurs hiérarchiques. En règle générale, le document spécifie le nom et la version du service, l'architecture, le nom de l'hôte, les informations sur le système d'exploitation, l'ID du processus, le nom de l'exécutable et les détails sur l'hôte tels qu'un ID de machine ou un ID de service unique.
Vous vous souviendrez peut-être du tableau de comparaison des fonctionnalités des outils par rapport à la liste de souhaits selon lequel aucun des outils ne prend en charge l'introspection d'exécution ou les vidages de tas/core. Cependant, Spring, en tant que framework sous-jacent, prend en charge les deux, même s'il a fallu du travail pour intégrer les fonctionnalités d'observabilité dans l'application. Comme détaillé dans la section précédente, pour l’introspection d’exécution, nous utilisons un module Python en conjonction avec Spring Boot Actuator.
De même, pour les vidages de tas, nous utilisons les points de terminaison de vidage de thread fournis par Spring Boot Actuator pour réaliser un sous-ensemble des fonctionnalités souhaitées. Nous ne pouvons pas obtenir de vidages de mémoire à la demande ni de vidages de tas de la JVM au niveau de granularité idéal, mais nous obtenons certaines des fonctionnalités souhaitées avec peu d’effort supplémentaire. Les vidages de mémoire pour les services Python nécessiteront malheureusement un travail supplémentaire important que nous avons reporté à une date ultérieure.
Après avoir beaucoup pleuré et remis en question nos choix de vie, nous avons décidé d'utiliser les technologies suivantes pour l'observabilité dans le MARA (dans ce qui suit, OTel signifie OpenTelemetry) :
une erreur
Cette implémentation est un instantané dans le temps. Cela va certainement changer et évoluer à mesure que le développement se poursuit. Bientôt, nous allons exécuter l'application via des tests de charge approfondis. Nous espérons en apprendre beaucoup sur les lacunes de notre approche d’observabilité et ajouter des fonctionnalités d’observabilité supplémentaires.
Veuillez essayer l' architecture de référence des applications modernes et l'exemple d'application (Bank of Sirius). Si vous avez des idées sur la façon dont nous pouvons faire mieux, nous accueillons vos contributions sur notre dépôt GitHub !
Cet article fait partie d'une série. Au fur et à mesure que nous ajoutons des fonctionnalités à MARA, nous publions les détails sur le blog :
« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."