Aller au contenu

À la recherche de vos traces avec Tempo

·10 mins
observabilité tempo alloy promtail grafana conteneur kubernetes cncf helm prometheus logs traefik
Romain Boulanger
Auteur
Romain Boulanger
Architecte Infra/Cloud avec une touche de DevSecOps
Sommaire

Les traces, une histoire d’observabilité
#

Vous commencez à connaître le terme observabilité par cœur maintenant à force d’en parler à travers mes articles.

Dans l’article précédent, je vous ai parlé de logs avec Loki et Alloy. C’est donc la suite logique de continuer sur cette lancée en abordant les traces !

Oui mais qu’est-ce qu’une trace ?

Eh bien c’est ce qui permet de visualiser et de comprendre le flux d’exécution d’une requête, en particulier dans les architectures microservices où une seule action utilisateur peut déclencher des dizaines d’appels entre différents services.

Chaque trace est composée de plusieurs segments appelés spans. Ces segments représentent les différentes opérations entre les composants permettant d’établir toutes les relations entre ces derniers et comprendre les différentes dépendances.

Trace et spans

Différences entre trace et spans

Si on parle de plus en plus de traces aujourd’hui c’est notamment parce que l’adoption de standards comme OpenTracing puis OpenTelemetry a considérablement démocratisé l’instrumentation des applications pour les collecter, rendant cette pratique plus accessible.

Laisser des traces dans votre cluster Kubernetes, ça peut vous aider !
#

Kubernetes est devenu l’environnement que l’on mentionne le plus quand on parle d’applications Cloud Natives notamment quand elles sont décomposées en microservices. Tout simplement parce que les forces de cet orchestrateur reposent sur le fait de mettre à l’échelle rapidement chaque composant, permettant aussi d’améliorer leur résilience et leur disponibilité.

Voici quelques points qui résument, à mon sens, le rôle essentiel d’avoir des traces dans un monde conteneurisé :

  • Identifier les goulots d’étranglement : Les traces permettent d’identifier précisément quels services ou quels appels consomment le plus de temps dans une transaction complète ;

  • Comprendre et identifier les dépendances : Les traces révèlent les relations entre les différents services et composants, aidant à comprendre l’architecture globale d’une application ;

  • Optimisation des performances : Quand on parle de performance au sujet des traces, on identifie surtout la latence. Cela permet d’avoir une idée du composant qui met le plus de temps sur une requête au global et agir sur l’optimisation de ce dernier.

Sans oublier que dans l’écosystème Kubernetes, d’autres composants rentrent en jeu comme les Ingress Controllers, Gateway API, Service Mesh, Network Policy, etc. Ces traces sont donc un prérequis pour comprendre le cheminement d’un appel à travers une application mais aussi à travers les différents mécanismes de l’orchestrateur !

Tempo
#

Tempo de Grafana est un outil open source présent au sein de la Cloud Native Computing Foundation (CNCF), qui s’intègre à la perfection avec le monde Prometheus et Grafana. Il représente en lui seul une solution de stockage pour les traces sous forme de backend.

La CNCF avec Tempo

L’observabilité au sein de la CNCF en mars 2025

Tempo peut s’interfacer avec du stockage objet comme S3, Google Cloud Storage, Azure Blob Storage voire Minio pour stocker les traces brutes.

De plus, il offre un large éventail de formats : Jaeger, Zipkin et OpenTelemetry, sous différents protocoles facilitant ainsi son adoption dans des environnements existants.

Comme dit plus haut, il se marie très bien avec Grafana permettant de faire correspondre facilement des métriques et des logs aux traces grâce à l’utilisation d’identifiants de trace (traceId).

La durée de conservation des traces est aussi configurable, grâce à la définition de politiques de rétention flexibles, adaptées aux besoins spécifiques de chaque organisation.

L’architecture de Tempo lui permet de s’adapter à des volumes massifs de données de trace sans perte de performance.

Enfin, Tempo se met à l’échelle en fonction de vos besoins grâce à une architecture distribuée, facilitant l’amélioration des performances lors de l’ingestion des traces.

Architecture
#

Architecture de Tempo

Architecture de Tempo provenant de la documentation officielle

L’architecture de Tempo se décompose en plusieurs composants :

  • Distributor va récupérer les traces sous différents formats ;
  • Ingester sera chargé de créer des filtres et index en organisant les traces sous forme de blocs avant de les stocker ;
  • Query Frontend permet de fournir une API pour récupérer les traces, c’est ce composant que l’on interrogera via Grafana ;
  • Querier se situe derrière le Query Frontend permettant de venir piocher dans les traces en utilisant le stockage ou l’Ingester ;
  • Compactor se charge de réduire la place du stockage ;
  • Metrics generator est un composant optionnel permettant de générer des métriques à partir des traces pour alimenter, pourquoi pas, Prometheus.

Modes d’installation
#

Tempo arrive avec deux modes d’installation :

  • Monolithic (Single Binary) : Rapide à mettre en place dans le cas d’un test, ou pour les petits volumes de données ;

  • Distributed : À considérer pour les déploiements de grande envergure, Tempo est décomposé en microservices (ingester, compactor, distributor, etc.), permettant une mise à l’échelle beaucoup plus granulaire que sur le premier mode.

À noter qu’un opérateur existe : Tempo Operator pour les amateurs de ce type de mécanisme. Il reprend en partie le mode microservices décrit plus haut.

Après la théorie, la pratique !
#

Pour suivre les étapes, vous pouvez récupérer les configurations à travers ce dépôt de code :

axinorm/traces-with-tempo

Set up and configure Tempo with Alloy and Grafana to look for traces in your Kubernetes cluster.

null
0
0

N’hésitez pas à modifier les différentes valeurs si cela fait du sens par rapport à votre configuration.

Ces différents fichiers values permettront de configurer Tempo et les différents outils associés.

C’est parti !

Alloy
#

Alloy permet d’aller beaucoup plus loin qu’un simple collecteur de logs. Il permet aussi de récupérer les traces via une configuration dédiée.

Pour cela, il est primordial d’injecter la bonne configuration, par rapport à ce cas d’utilisation, les points de terminaison http et grpc au format OpenTelemetry sont nécessaires.

Cette configuration est totalement modulable et dépend du format que votre application ou votre outil est capable de fournir dans le but de récupérer les traces.

alloy:
  configMap:
    # -- Create a new ConfigMap for the config file.
    create: true
    # -- Content to assign to the new ConfigMap.  This is passed into `tpl` allowing for templating from values.
    content: |-
      logging {
        level = "info"
      }

      otelcol.receiver.otlp "default" {
        http {}
        grpc {}

        output {
          traces = [otelcol.processor.batch.default.input]
        }
      }

      otelcol.processor.batch "default" {
        output {
          traces = [otelcol.exporter.otlp.tempo.input]
        }
      }

      otelcol.exporter.otlp "tempo" {
        client {
          endpoint = "tempo-distributor.observability.svc.cluster.local:4317"
          tls {
            insecure = true
          }
        }
      }

Sans oublier de les faire suivre et donc de les exporter vers le service Tempo qui sera déployé juste après.

Par défaut, le collecteur d’Alloy expose un service qu’il est nécessaire de surcharger pour rendre disponible les ports associés au protocole d’OpenTelemetry (OTLP) aussi bien en HTTP qu’en GRPC.

  # -- Extra ports to expose on the Alloy container.
  extraPorts:
  - name: otlp-http
    port: 4318
    targetPort: 4318
    protocol: TCP
  - name: otlp-grpc
    port: 4317
    targetPort: 4317
    protocol: TCP

La configuration semble correcte, l’étape suivante consiste à le déployer.

Avant de commencer, n’oubliez pas de configurer la source pour récupérer les charts Helm de Grafana :

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

Et ensuite, de procéder à l’installation :

helm install alloy grafana/alloy --version 0.12.5 --namespace observability --create-namespace --values ./values-alloy.yaml

Alloy est maintenant prêt pour ingurgiter vos événements !

Tempo
#

Pour l’installation de Tempo avec Helm, vous avez le choix entre deux configurations :

  • Un chart tempo en mode monolithique, un seul binaire à déployer ;

  • Un autre chart décomposé en microservices : tempo-distributed idéal pour gérer la charge de manière granulaire sur chacun des composants.

Pour tester le fonctionnement de Tempo dans des conditions proches de la réalité, j’ai choisi le chart tempo-distributed. Libre à vous de choisir l’installation qui vous convient le mieux.

Dans ce mode de configuration, un stockage objet est fortement recommandé. Pour ma part, j’utilise Minio qui est directement inclus sous forme de sous chart Helm en lui associant un bucket pour stocker les traces :

# Minio
minio:
  enabled: true
  mode: standalone
  rootUser: grafana-tempo
  rootPassword: supersecret
  buckets:
    # Default Tempo storage bucket.
    - name: tempo-traces
      policy: none
      purge: false

Ensuite, les protocoles OTLP doivent être activés pour que Tempo soit en mesure de recevoir les traces provenant d’Alloy :

traces:
  otlp:
    http:
      # -- Enable Tempo to ingest Open Telemetry HTTP traces
      enabled: true
    grpc:
      # -- Enable Tempo to ingest Open Telemetry GRPC traces
      enabled: true

Enfin, on termine avec la configuration du stockage en mode S3 avec les paramètres du Minio déployé :

# To configure a different storage backend instead of local storage:
storage:
  trace:
    # -- The supported storage backends are gcs, s3 and azure, as specified in https://grafana.com/docs/tempo/latest/configuration/#storage
    backend: s3
    wal:
      path: /tmp/tempo/wal
    s3:
      bucket: tempo-traces
      endpoint: tempo-minio:9000
      access_key: grafana-tempo
      secret_key: supersecret
      insecure: true
      tls_insecure_skip_verify: true

Pour déployer le tout, la commande helm install que vous connaissez (presque) par cœur :

helm install tempo grafana/tempo-distributed --version 1.32.7 --namespace observability --create-namespace --values ./values-tempo.yaml

Grafana
#

Grafana, l’outil de visualisation par excellence bénéficie d’une configuration allégée, demandant d’initialiser une datasource afin de manipuler les données de Tempo :

datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
    # Tempo DataSource
    - name: Tempo
      uid: tempo
      type: tempo
      url: http://tempo-query-frontend:3100/
      access: proxy
      orgId: 1

Là aussi, même punition pour déployer :

helm install grafana grafana/grafana --version 8.10.4 --namespace observability --create-namespace --values ./values-grafana.yaml

Traefik pour tester
#

Il ne reste plus qu’à venir brancher une application sur laquelle on souhaite visualiser les traces. De mon côté j’utilise Traefik qui peut générer des traces au format OTLP et qui sera capable de les déverser dans un collecteur comme Alloy.

Pourquoi Traefik ?

Traefik est un Ingress Controller, en d’autres termes, la porte d’entrée pour exposer mes services vers l’extérieur. Je trouve intéressant de pouvoir comprendre les routes et middlewares appelés pour chacune des requêtes. Cela m’aide à comprendre si ma configuration est correcte ou non.

Pour cela, la configuration de Traefik est légère. L’objectif étant de pouvoir lire des traces basiques fournies par ce dernier.

Pour déverser les traces au format OTLP, voici un extrait du paramétrage à adopter :

## Tracing
# -- https://doc.traefik.io/traefik/observability/tracing/overview/
tracing:  # @schema additionalProperties: false
  # -- Enables tracing for internal resources. Default: false.
  addInternals: true
  otlp:
    # -- See https://doc.traefik.io/traefik/v3.0/observability/tracing/opentelemetry/
    enabled: true
    http:
      # -- Set to true in order to send metrics to the OpenTelemetry Collector using HTTP.
      enabled: true
      # -- Format: <scheme>://<host>:<port><path>. Default: http://localhost:4318/v1/metrics
      endpoint: "http://alloy.observability:4318/v1/traces"

Sans surprise, on vient renseigner le endpoint fourni par Alloy associé au protocole HTTP.

De plus, le addInternals: true permet de tracer l’ensemble des couches internes de Traefik, très utile quand on configure une suite de Middleware.

Enfin, dans l’objectif de générer du trafic et avoir des traces, on peut utiliser ici un service en mode NodePort pour simplifier le cas d’utilisation :

service:
  enabled: true
  ## -- Single service is using `MixedProtocolLBService` feature gate.
  ## -- When set to false, it will create two Service, one for TCP and one for UDP.
  type: NodePort

Là aussi, pour déployer le chart Traefik, Helm est de la partie :

helm install traefik traefik/traefik --version 34.4.1 --namespace ingress --create-namespace --values ./values-traefik.yaml

Les outils sont maintenant correctement déployés et surtout opérationnels ! Comme vous pouvez le constater :

$ kubectl -n observability get po
NAME                                   READY   STATUS    RESTARTS      AGE
alloy-d7649                            2/2     Running   0             72s
grafana-5b5dd98f75-dpdlm               1/1     Running   0             67s
tempo-compactor-5856cfc4b6-vf9sm       1/1     Running   3 (94s ago)   2m1s
tempo-distributor-776dd495cc-k2vdl     1/1     Running   3 (97s ago)   2m1s
tempo-ingester-0                       1/1     Running   3 (86s ago)   2m1s
tempo-ingester-1                       1/1     Running   3 (88s ago)   2m1s
tempo-ingester-2                       1/1     Running   3 (89s ago)   2m1s
tempo-memcached-0                      1/1     Running   0             2m1s
tempo-minio-568d558987-rvvjp           1/1     Running   0             2m1s
tempo-querier-6b7fb8f848-2ztqs         1/1     Running   3 (88s ago)   2m1s
tempo-query-frontend-685fcd8fb-fl8bg   1/1     Running   3 (97s ago)   2m1s
$ kubectl -n ingress get po
NAME                       READY   STATUS    RESTARTS   AGE
traefik-59c8dbcb57-9gmpt   1/1     Running   0          78s

Consulter les traces
#

Allez dans Grafana grâce à un port-forward :

kubectl -n observability port-forward svc/grafana 8080:80

Générez quelques traces en accédant au service Traefik et sans plus attendre, allez dans la section Explore de Grafana en sélectionnant Tempo comme DataSource.

Voici un exemple de ma configuration personnelle, bien évidemment pour obtenir ce résultat, j’ai configuré quelques IngressRoute et Middleware au sein de Traefik :

Visualisation des traces dans Grafana

Visualisation de traces de Traefik avec Tempo et Grafana

Quelques mots pour conclure
#

Tempo se révèle comme l’outil idéal pour collecter les traces de vos applications. À travers son architecture en mode microservices, il permet de bénéficier de la mise à l’échelle offerte par Kubernetes tout en s’intégrant à merveille dans l’écosystème Grafana.

Les différents formats et protocoles permettent à Tempo de stocker un très large éventail de données sans avoir à utiliser d’autres solutions tierces.

Enfin, sa mise en place est assez rapide grâce à une documentation et des charts Helm prêt à l’emploi.

Articles connexes

Reprenez le contrôle de vos logs avec Loki
·14 mins
observabilité loki alloy promtail grafana conteneur kubernetes cncf helm prometheus logs
Surveiller votre cluster Kubernetes et vos applications avec kube-prometheus-stack
·8 mins
kubernetes monitoring prometheus alertmanager grafana k3s cncf
Gérer les accès à vos services avec Authelia
·6 mins
authentication authelia traefik docker grafana monitoring prometheus reverse-proxy supervision