blog-image

Monitoring/Gestion des performances applicatives (APM) - [ Partie 2]

  • dHENRY
  • 30/12/2022
  • (Durée de lecture : 4 mn)

Dans la “Partie 1”, j’ai présenté la solution Elastic Search APM. APM (Application Performance Monitoring) permet de mesurer les performances d’une application. Une solution plus légère et alternative à Elastic Search APM peut être “Jaeger”. Très simple à installer sous forme de conteneurs Docker, aucun paramétrage. Vous démarrez, vous utilisez.

L’un n’empêche pas l’autre, on peut disposer d’une solution Elastic dans un environnement de production et utiliser Jaeger dans les environnements de développements (pas de flux, comptes à gérer…).

Jaeger installation

J’ai suivie la documentation officielle, en prenant la solution “all-in-one”.

Shell de démarrage Jaeger

Sur une machine disposant de “Docker”, créer le fichier “startjaeger.sh”

#!/bin/bash
docker run --rm -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:latest

et exécutez :

chmod 750 ./starjaeger.sh
./startjaeger.sh

Pour stopper Jaeger, créer le fichier “stopjaeger.sh”, et exécuter :

chmod 750 ./stopjaeger.sh
docker stop jaeger

PS : J’ai rencontré un problème d’affichage sur la comparaison des traces en version 1.39, corrigé en version 1.40

Contrôle de fonctionnement

Pour se connecter à l’UI Jaeger, ouvrir votre navigateur et indiquer l’url : http://[Adresse IP de serveur Jaeger]:16686"

OK Jaeger fonctionne

Instrumentation du code (nodejs)

source : https://selvaganesh93.medium.com/tracing-node-js-application-with-opentelemetry-jaeger-ui-9523c0ac8453

Installation des paquets nécessaires

npm install \
@opentelemetry/sdk-trace-node \
@opentelemetry/instrumentation \
@opentelemetry/instrumentation-http \
@opentelemetry/instrumentation-express \
@opentelemetry/instrumentation-pg \
opentelemetry/propagator-ot-trace \
@opentelemetry/exporter-jaeger \
@opentelemetry/resources \
@opentelemetry/tracing \
@opentelemetry/semantic-conventions --save

Ce code est à disposer au tout début du programme principal, comme indiqué dans la documentation OTEL :

const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http");
const {
  ExpressInstrumentation,
} = require("@opentelemetry/instrumentation-express");
const { PgInstrumentation } = require("@opentelemetry/instrumentation-pg");
const { OTTracePropagator } = require("@opentelemetry/propagator-ot-trace");
const { JaegerExporter } = require("@opentelemetry/exporter-jaeger");
const { Resource } = require("@opentelemetry/resources");
const { BatchSpanProcessor } = require("@opentelemetry/tracing");
const {
  SemanticResourceAttributes,
} = require("@opentelemetry/semantic-conventions");

const exporter = new JaegerExporter({
  tag: [],
  endpoint: "http://[IADDRDOCKERHOSTJAEGER]:14268/api/traces",
});

const configjaeger = {
  serviceName: "Your-service-Name",
  environment: "developpment",
};
provider = new NodeTracerProvider({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: configjaeger.serviceName, // Service name that showuld be listed in jaeger ui
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
      configjaeger.environment,
  }),
});
// Use the BatchSpanProcessor to export spans in batches in order to more efficiently use resources.
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
provider.register({ propagator: new OTTracePropagator() });

registerInstrumentations({
  // list here your desired OTEL instrumentation - see registry and add npm before using
  instrumentations: [
    new HttpInstrumentation(),
    new ExpressInstrumentation(),
    new PgInstrumentation(),
  ],
});

Analyse des traces

Dans l’article précédent, je présentais les résultats de l’analyse, avec Kibana (ElasticSearch), de l’appel à l’API “/api/mostpopulartracks” pour l’application utilisée dans cette présentation.

Maintenant avec la présentation Jaeger :

L’instrumentation étant identique “Opentelemetry (OTEL)”, la présentation est similaire.

Choisissez la présentation : “FlameGraph” (en haut à droite)

Ce graph représente les temps cumulés par méthodes. Notons que le temps total pour cette transaction est de 125.6ms.

intéressons-nous à la méthode “pg.connect” qui représente quasiment 58% du temps consommé par cette API.

Les temps de connexions aux bases de données sont souvent élevés dans une transaction SQL (connexion TCP, authentification,…). C’est pourquoi, il est conseillé d’utiliser, en production, un “Pooler” de connexions.

Au lieu de se connecter directement au serveur de base de données final, l’application passe par un “Pooler” (PgPool par exemple), qui maintient une connexion avec le serveur de base de données final.

Avant de déployer l’artillerie lourde, en ajoutant un middleware dans mon infra, j’ai préféré voir si le driver ne proposait pas une solution similaire. La documentation du driver “Postgres” (nmp pg), utilisé dans mon application, indique cette possibilité : “pool”.

Au final, le “Database Pooler” de l’infra, sera l’application elle même.

Je vais d’abord modifier mon code dans ce sens, redémarrer l’application et appeler cette même API.

Le temps approche maintenant les 100ms, on a déjà gagné quelque chose. Après analyse, je viens de démarrer l’application, il n’y a pas eu de connexion à la base, je retrouve donc un pg.connect de 33.02ms, qui va initialiser le pool des connexions, la seconde requête fait appel à pg-pool.connect, qui ré-utilise la connexion persistante du “Pooler” (on a gagné le temps d’un pg.connect).

Je rappelle cette même API, dans ce contexte il existe déjà une connexion persistante vers le serveur Postgres, au travers du “Pooler” :

Et là nous passons à 50.39ms. En modifiant le comportement du middleware, nous avons diminué le temps d’exécution de cette API par 2.5 (50.39ms contre 125.6ms) !!!

Voilà clairement l’avantage de travailler avec une solution APM.

ATTENTION : Ce type de réponse n’est envisageable que dans un contexte à faibles utilisateurs. Dans le cas contraire, vous serez obligé d’implémenter un vrai “Pooler” pour supporter la charge, sinon vous écroulerez votre serveur de base de données par un nombre de connexions simultanées trop élevé.

+++

(*) Image Rendering Powered by