Tracing distribué sur ISTIO :

Comment bâtir une solution de tracing robuste basée sur Jaeger


 

Après avoir introduit le tracing distribué sur ISTIO, et démystifié comment il fonctionne dans une architecture de microservices, nous allons à présent le mettre en oeuvre dans un environnement de production. Vous trouverez dans le lien suivant mon article qui démystifie le concept du tracing distribué :

Dans la première partie de cet article, nous allons présenter les composants et l’architecture de Jaeger comme solution de tracing, et comment le déployer dans un environnement de développement puis de production. Sur la seconde partie, nous allons activer le tracing distribué sur ISTIO, en intégrant la configuration du Jaeger déployée en production.

Partie I : Mise en place du tracing distribué sur Jaeger

Avant de débuter la mise en place, on va présenter les différents composants d’une architecture Jaeger et voir comment ces composants sont liés entre eux. Puis nous aborderons les différentes stratégies de déploiement de Jaeger. Finalement nous passerons aux déploiements de Jaeger, d’abord dans un environnement de développement, et ensuite dans un environnement de production.

Composants essentiels de Jaeger:

Agent : L’agent Jaeger est déployé soit sous la forme d’un daemonset, ou en sidecar dans un conteneur qui tourne dans le pod de l’application. Il reçoit les spans depuis le client Jaeger, les regroupe, puis les renvoit au collector.

Collector : Le collector reçoit les traces puis les intègre dans un pipeline de validation, d’indexation, et de transformation avant de les stocker sur un plugin de stockage : Cassandra ou Elasticsearch ou Kafka.

Ingester : C’est un service qui récupère les topics de Kafka et les stocke sur le plugin de stockage (Cassandra ou Elasticsearch)

Query : C’est un service qui récupère les traces depuis le plugin de stockage et les transmet à l’UI où elles vont être visualisées.

Les stratégies de déploiement de Jaeger:

Le déploiement de Jaeger est lié à une des trois stratégies de déploiement qui existent : la stratégie AllInOne, la stratégie de production et la stratégie de streaming

  • AllInOne : C’est la stratégie de déploiement par défaut, elle utilise une image all-in-one qui inclut tous les composants Jaeger (agent, collector, query, ingestor, Jaeger UI) dans un seul et unique pod, et utilise la mémoire de ce même pod pour le stockage. Cette stratégie est plutôt utilisée pour faire une démo ou sur un environnement de développement,  elle n’est pas adéquate pour un environnement de production.

  • Production: Cette stratégie est utilisée pour un déploiement dans un environnement de production, où la rétention des traces, l’évolution et la haute disponibilité des composants de l’architecture sont des points très importants. Et à la différence de la stratégie précédente, chaque élément du backend est déployé dans un pod à part. Cette stratégie inclut un plugin de stockage supplémentaire de type Cassandra ou Elasticsearch.

  • Streaming: Cette stratégie n’est pas très différente en terme d’architecture de la précédente, mais elle est plus intéressante en terme d’efficacité et de performance sur un environnement de production. Elle intègre un composant en plus, qui gère le streaming entre le collecteur et le plugin de stockage (Cassandra ou Elasticsearch), quand ce dernier est beaucoup trop sollicité.

Mise en place de Jaeger avec la stratégie AllInOne 

Architecture:

Le choix de la stratégie de déploiement de Jaeger est définie sur le fichier custom resource, et détermine aussi quelle architecture sera utilisée pour le backend Jaeger. Voici l’architecture de Jaeger avec la stratégie AllInOne.

Photo credit : https://www.jaegertracing.io/docs/1.16/architecture/

Déploiement de Jaeger:

On commence notre déploiement par installer la CustomResourceDefinition (CRD), le service account, le role, le rolebinding puis l’operator:

Copy to Clipboard

Par la suite, puisque la stratégie AllInOne est la stratégie de déploiement par défaut de Jaeger, aucune configuration n’est nécessaire. Nous allons simplement créer une instance Jaeger, avec la custom resource suivante:

Copy to Clipboard

Mise en place de Jaeger avec la stratégie de streaming

Architecture:

Nous venons de déployer Jaeger dans un environnement de développement, qui peut être utilisé pour faire des démos ou des tests de tracing distribué. Par contre, dans un environnement de production comptant des milliers de microservices, il faudrait avoir une architecture plus efficace et plus robuste.

Pour cela, nous allons déployer Jaeger avec la stratégie de streaming, pour répondre au besoin de tracing distribué dans un environnement de production. Cette stratégie inclut :

  • Un plugin de stockage Elasticsearch
  • Un plugin de streaming Kafka qui permettra de diminuer la pression de la charge envoyée depuis le collector vers le Elasticsearch

Le schéma suivant décrit l’architecture d’un déploiement Jaeger avec la stratégie de Streaming, en présentant le lien entre tous les éléments, ainsi que les plugins de stockage et de Streaming intégrés :

Photo credit : https://www.jaegertracing.io/docs/1.16/architecture/

Prérequis:

Pour l’installation de Jaeger sur un environnement de production avec la stratégie de streaming, nous allons d’abord déployer Elasticsearch et Kafka avant de déployer Jager. Il est recommandé d’utiliser des operators pour ces déploiements sur le cluster Kubernetes.

  • Elasticsearch

Pour Elasticsearch, nous allons installer l’operator, la CRD et les RBAC avec la commande suivante :

Copy to Clipboard

Par la suite on va éditer le manifest elastic.yaml en remplaçant <xx> par les bonnes valeurs, pour ensuite lancer l’installation d’un cluster Elasticsearch avec 3 Master nodes et 3 Data nodes sur le namespace observability:

Copy to Clipboard

 

  • Kafka

Ensuite nous allons déployer Kafka en utilisant l’operator Strimzi, sur un namespace dédié qu’on nommera kafka:

Copy to Clipboard

Par la suite on lance le fichier d’installation qui va déployer un ClusterRole, un ClusterRoleBinding et une CustomResourceDefinition (CRD) sur le namespace kafka:

Copy to Clipboard

Une fois la CRD créée, il ne reste plus qu’à éditer le manifest kafka.yaml, pour enfin déployer la custom resource kafka sur le namespace kafka:

Copy to Clipboard

Note: Pour une installation réussie et pour ne pas rencontrer de problème par la suite lors du déploiement de Jaeger, il est recommandé d’attendre le déploiement complet d’Elasticsearch et de kafka. Une fois qu’ils sont UP & Running on peut passer à la suite.

 

Déploiement de Jaeger:

Une fois kafka et elasticsearch déployés, il ne nous reste plus qu’a déployer Jaeger avec la stratégie de streaming. On commence tout d’abord par installer la CustomResourceDefinition (CRD), le service account, le role, le rolebinding puis l’operator dans le namespace observability:

Copy to Clipboard

Avant de déployer Jaeger, il faudra lui indiquer les identifiants de l’utilisateur elastic afin de  communiquer avec Elasticsearch. Nous allons donc extraire ce password, puis l’utiliser pour créer un secret pour Jaeger avec un [username,password] qui sera renseigné au déploiement pour communiquer avec Elasticsearch.

Copy to Clipboard
Copy to Clipboard

Il ne reste plus qu’à éditer le manifest suivant, pour déployer la custom resource kafka sur le namespace observability comme suit :

Copy to Clipboard

 

On va expliquer quelques détails dans ce manifest qui sont assez intéressants et surtout très essentiels à ce déploiement de Jaeger:

  • #1 : Ici on renseigne la configuration de kafka utilisée par le collector qui produit des messages, et l’ingester qui va récupérer ces messages.
  • #2 : Ici on configure la durée de rétention des indexes sur Elasticsearch, et on renseigne aussi à quel moment on souhaite planifier le nettoyage.
  • #3 : Ici on configure le type et les paramètres du stockage utilisé (Elasticsearch dans notre cas sinon Cassandra), en précisant l’url et le port sur lesquels il est exposé. 
  • #4 : Ici on définit la stratégie de déploiement de Jaeger.

Partie II : Mise en place du tracing distribué sur ISTIO en production

Dans cette partie, nous allons mettre en place le tracing sur ISTIO dans un environnement de production, en intégrant Jaeger. Une fois mis en place, nous déploierons l’application bookinfo sur le cluster kubernetes pour générer des traces et les visualiser sur l’interface Jaeger.

Note: Dans cette partie, nous n’allons pas aborder en détails l’installation d’ISTIO sur kubernetes avec ces prérequis. Vous trouverez sur cet article bien rédigé, quels sont les prérequis pour l’installation d’ISTIO et comment le déployer facilement sur un cluster kubernetes:

Activation du tracing sur ISTIO en production:

Lors de l’installation d’ISTIO avec istioctl, si aucun profil n’est spécifié, le profil par défaut est alors utilisé. Et avant de commencer, il y a trois points essentiels à connaître sur la configuration par défaut d’ISTIO:

  • D’abord que le tracing est par défaut désactivé sur ISTIO. Donc pour l’activer, il faudrait changer la valeur de l’option value.tracing.enabled si on est dans un environnement de test ou de demo. Sinon pour notre cas, dans un environnement de production, nous allons configurer l’option d’installation suivante, en renseignant les paramètres du Jaeger externe values.global.tracer.zipkin.address=<jaeger-collector-service>.<jaeger-collector-namespace>:9411
  • Aussi, ISTIO supporte plusieurs plateformes de tracing tels que Jaeger, Zipkin et autres. Par défaut, et si le tracing est activé, ISTIO utilise Jaeger. Et donc pour configurer une autre plateforme de tracing que Jaeger, il faudrait changer la valeur de l’option d’installation value.tracing.provider, la plateforme de tracing désirée.
  • Dernière chose, bien que toutes les traces soient générées, seules quelques-unes sont échantillonnées. Par défaut, Jaeger échantillonne 1% des traces, soit 1 trace sur 100 requêtes. Toutefois, nous pouvons changer le taux d’échantillonnage des traces avec la variable value.pilot.traceSampling 

Maintenant nous allons lancer l’installation d’ISTIO avec le profil par défaut ( recommandé pour un environnement de production), en utilisant la commande istioctl et en incluant les options déjà citées, en fonction du besoin :

Copy to Clipboard

Avec cette commande on lance ISTIO avec le profil par défaut, on définit le taux d’échantillonnage désiré, puis on active le tracing distribué en configurant le Jaeger déjà déployé et en renseignant l’adresse du collector Jaeger et le namespace dans lequel il tourne.

Générer des traces avec l’application Bookinfo

A ce stade, nous avons installé ISTIO avec le mode par défaut, et on a activé le tracing en utilisant un Jaeger déployé avec une stratégie de streaming pour faire face à de grandes charges dans un environnement de production. Il ne nous reste plus qu’à déployer une application sur ISTIO, et  visualiser ses traces .

Déployer l’application bookinfo

Nous avons fait le choix de déployer l’application Bookinfo, qui est disponible sur le repo git officiel d’ISTIO,  pour générer des traces. 

Pour déployer l’application Bookinfo sur ISTIO, nous devons activer le sidecar injection automatiquement sur le namespace default, où sera déployée cette application. On utilise alors la commande suivante pour ajouter le bon label au namespace default :

Copy to Clipboard

On lance maintenant le déploiement de l’application Bookinfo avec la commande suivante :

Copy to Clipboard

Comme vous le constatez, plusieurs microservices ont été créés, le microservice reviews lui a été créé en 3 versions différentes  :

 

 

 

 

 

 

 

 

Générer des traces 

A ce stade, l’application Bookinfo est déployée. Mais pour visualiser les traces de ses microservices sur l’interface Jaeger, il faudrait d’abord les générer en envoyant des requêtes à un service quelconque de l’application bookinfo. Nous allons requêter le service productpage en accédant à l’url : http://$GATEWAY_URL/productpage, qui à son tour va faire appel à d’autres microservices ( nous allons voir ces appels plus en détails sur les traces générées dans l’interface Jaeger )

Cela  signifie en d’autres termes, que depuis le réseau public (internet) nous allons accéder au service productpage à travers l’ingress gateway d’ISTIO.

Pour cela, nous devons alors définir la variable d’environnement GATEWAY_URL comme suit :

Copy to Clipboard

On peut  bien aussi appeler le service productpage avec une boucle itérative, pour générer un nombre plus important de traces, car il ne faut pas oublier que les traces affichées vont dépendre du taux d’échantillonnage configuré lors de l’installation d’ISTIO avec le paramètre value.pilot.traceSampling. Donc pour lancer une boucle itérative on utilise la commande suivante:

Copy to Clipboard

Jaeger UI pour visualiser les traces:

Visualiser les traces :

Il ne nous reste plus qu’à accéder à l’interface de Jaeger pour visualiser les traces générées par les dernières tentatives d’accès au service productpage.

On lance l’interface Jaeger avec la commande istioctl suivante :

Copy to Clipboard

Par la suite on sélectionne le service que nous avions requêté avant, puis on lance une recherche de traces. On a alors une liste de toutes les traces collectées, dans notre exemple ci-dessous, on a 15 traces. Chaque trace affiche un résumé sur le nombre de spans qui composent cette trace, les différents microservices appelés dans la requête, la durée totale de l’exécution de la requête et bien d’autres informations.

On peut aussi avoir plus de détails en cliquant sur une des traces affichées. Et là, on a une liste de tous les microservices, et composants ISTIO, qui ont été impliqués lorsqu’on a requêté le microservice productpage. Chaque span de cette trace correspond à un microservice. Ces spans représentent le moment où le microservice a été appelé et la durée de chaque opération exécutée par ce dernier.

Jaeger nous permet aussi, à travers son interface, de visualiser le directed acyclic graph (DAG)  , sous forme d’un arbre généalogique, représentatif de toutes les dépendances entre les différents microservices et composants ISTIO dans notre service mesh. Le graphe Jaeger n’est pas aussi riche et développé que ce que peut offrir Kiali, mais ils nous offre quand même une visibilité high level sur notre architecture microservices, tout en incluant les interactions entre ces différents microservices.

Visualiser les dépendances:

Jaeger nous permet aussi, à travers son interface, de visualiser le directed acyclic graph (DAG)  , sous forme d’un arbre généalogique, représentatif de toutes les dépendances entre les différents microservices et composants ISTIO dans notre service mesh. Le graphe Jaeger n’est pas aussi riche et développé que ce que peut offrir Kiali, mais ils nous offre quand même une visibilité high level sur notre architecture microservices, tout en incluant les interactions entre ces différents microservices.

Nous verrons dans un prochain article comment configurer et déployer Kiali sur ISTIO, et comment l’utiliser pour visualiser notre mesh à travers son interface. Kiali permettra une meilleure visibilité sur les communication entre les microservices et plus de détails sur les dépendances entre ces derniers, sur des graphes plus riches que ceux offerts sur l’interface Jaeger..