Dans l’article précédent, j’ai parlé des métriques des brokers Kafka et expliqué pourquoi les collecter est plus compliqué qu’il ne devrait l’être.

En résumé : Kafka ne possède pas un seul système de métriques.

Il dispose du plus récent Kafka Metrics SPI, configuré via metric.reporters, et de l’ancien registre Yammer/Coda Hale, configuré via kafka.metrics.reporters. Certaines métriques importantes des brokers se trouvent dans l’un des systèmes, d’autres dans l’autre, et la plupart des dispositifs de supervision du monde réel passent toujours par JMX, des expressions régulières, des mappings Prometheus et des tableaux de bord qui correspondent ou non aux noms exportés.

C’est la raison pour laquelle monedula-metrics-reporter a été créé : un plugin Kafka qui exporte les métriques directement via OTLP vers un OpenTelemetry Collector et prend en charge les deux registres de métriques de Kafka avec une seule configuration.

Mais il manquait encore une pièce.

Les métriques des brokers nous en disent long sur le cluster, mais elles n’expliquent pas entièrement ce qui se passe à l’intérieur des applications clientes.

Un broker peut nous indiquer que les requêtes Produce sont lentes. Il peut nous indiquer que les requêtes Fetch sont lentes. Il peut nous renseigner sur les files d’attente de requêtes, les processeurs réseau, les partitions sous-répliquées, les requêtes en échec, et bien d’autres choses encore.

Mais parfois, la vraie question n’est pas seulement :

Que se passe-t-il sur le broker ?

C’est aussi :

Que se passe-t-il à l’intérieur du producer ou du consumer ?

Le producer regroupe-t-il ses messages efficacement ? Est-il throttlé ? Le consumer effectue-t-il régulièrement ses poll ? Passe-t-il du temps dans des rebalances ? Une application se comporte-t-elle différemment d’une autre ?

Traditionnellement, répondre à ces questions impliquait d’instrumenter chaque application cliente séparément. En Java, cela signifiait souvent JMX encore une fois. Dans d’autres langages, cela dépendait de la bibliothèque cliente, du framework et de l’infrastructure d’observabilité dont l’équipe disposait.

Et dans les grandes organisations, cela devient un problème très familier : l’équipe Kafka possède le cluster, mais les applications clientes appartiennent à de nombreuses équipes différentes. Ainsi, lorsqu’un problème survient, l’opérateur du cluster ne dispose souvent pas des métriques côté client dont il a besoin, et les obtenir nécessite de la coordination, des modifications de code, des redéploiements, ou au moins des redémarrages.

C’est précisément le problème que KIP-714 cherche à résoudre.

L’idée derrière KIP-714

KIP-714 ajoute un moyen, piloté par le broker, de collecter les métriques des clients Kafka.

Au lieu que chaque équipe applicative configure son propre exporteur de métriques, le cluster Kafka peut définir des abonnements aux métriques clientes. Un abonnement précise quelles métriques clientes doivent être collectées et à quelle fréquence les clients doivent les envoyer.

Les clients transmettent ensuite leurs métriques aux brokers en utilisant le protocole Kafka. La charge utile elle-même est en OTLP, de sorte que le plugin côté broker peut la transmettre à un OpenTelemetry Collector, à une base de données de séries temporelles ou à tout autre backend d’observabilité.

Ce qui compte, c’est le sens du contrôle.

C’est l’opérateur du cluster qui décide de ce qui est collecté.

Les clients compatibles KIP-714 peuvent recevoir l’abonnement et envoyer les métriques demandées. L’application n’a pas besoin d’exposer JMX. Elle n’a pas besoin d’un exporteur sidecar. Elle n’a pas besoin d’un endpoint Prometheus personnalisé.

Il reste néanmoins deux conditions :

  1. Le broker doit avoir un plugin de télémétrie cliente configuré.
  2. Au moins un abonnement aux métriques clientes doit exister.

Sans abonnement, les clients peuvent bien prendre en charge le protocole, mais ils n’enverront aucune métrique.

C’est une bonne conception. Les métriques clientes ne sont pas collectées par accident. L’opérateur doit activer explicitement la collecte du côté cluster.

Un détail important : tous les clients n’envoient pas les mêmes métriques

KIP-714 crée un mécanisme commun pour collecter les métriques clientes, mais il ne fait pas comme par magie que chaque client Kafka expose le même ensemble de métriques.

Les métriques que vous obtenez dépendent de la bibliothèque cliente.

Le client Java envoie actuellement l’ensemble de métriques le plus riche. Ce n’est pas surprenant : le client Java est l’implémentation de référence, il dispose d’un registre de métriques interne très mature, et de nombreuses métriques Kafka y ont été conçues en premier.

D’autres clients peuvent exposer un ensemble plus réduit. Les clients basés sur librdkafka, comme ceux utilisés par les wrappers Python, Go, .NET et Node, peuvent tout de même participer à KIP-714, mais les métriques disponibles ne sont pas nécessairement identiques à celles envoyées par le client Java.

Le modèle mental doit donc être le suivant :

KIP-714 standardise la façon dont les métriques clientes sont demandées et transportées. L’ensemble réel des métriques dépend toujours de l’implémentation du client.

Cela a son importance lors de la construction des tableaux de bord.

Certains panneaux peuvent s’appuyer sur des métriques communes et devraient fonctionner avec différentes bibliothèques clientes. D’autres panneaux n’ont de sens que lorsque le client envoie réellement l’ensemble de métriques le plus riche. En pratique, le client Java offre aujourd’hui l’histoire d’observabilité la plus complète.

Pourquoi ajouter cela à monedula-metrics-reporter ?

Au départ, monedula-metrics-reporter concernait les métriques des brokers.

Il prenait en charge le registre Kafka SPI. Il prenait en charge le registre Yammer. Il exportait les deux via OTLP. Il ajoutait l’identité du broker sous forme d’attributs de ressource. Il était livré avec des tableaux de bord Grafana correspondant aux métriques émises.

Mais dès lors que KIP-714 existe, une étape suivante s’impose très naturellement :

Si le plugin s’exécute déjà à l’intérieur du broker et exporte déjà en OTLP vers le collector, il devrait aussi gérer les métriques envoyées par les clients.

Sinon, la supervision de Kafka resterait fragmentée.

Vous auriez un chemin pour les métriques du SPI broker, un autre pour les métriques Yammer, et encore un autre plugin ou composant pour la télémétrie cliente KIP-714.

Cela fonctionnerait, mais irait à l’encontre de toute l’idée de simplifier la pile de métriques de Kafka.

Ainsi, la nouvelle version de monedula-metrics-reporter prend désormais en charge trois sources de métriques :

  1. Kafka Metrics SPI
  2. Métriques broker Yammer / Coda Hale
  3. Télémétrie cliente envoyée via KIP-714

L’objectif est simple : un seul plugin, un seul pipeline de collector, un seul jeu de tableaux de bord.

Les métriques clientes sont transmises par le même chemin OpenTelemetry que les métriques des brokers. Elles peuvent porter les mêmes labels de broker, comme kafka_cluster_id et kafka_node_id, et elles peuvent aussi être enrichies avec le client_id, afin que les métriques provenant de différentes applications ne se fondent pas en un seul flux anonyme.

Ce dernier point est très important.

Si deux producers envoient le même nom de métrique, nous devons savoir quel producer est lequel. Par défaut, le reporter ajoute le client id comme label. Les labels plus sensibles ou à forte cardinalité, comme le client instance id, le principal ou l’adresse du client, sont désactivés par défaut et doivent être activés explicitement.

Comment ça fonctionne

La partie côté broker est implémentée via l’interface ClientTelemetry de Kafka.

Lorsqu’un client compatible KIP-714 envoie de la télémétrie à un broker, le plugin reçoit la charge utile OTLP. Le thread du request-handler n’effectue qu’un travail minimal : il copie la charge utile dans une file d’attente bornée en mémoire. Un thread daemon lit ensuite dans cette file, convertit la charge utile en données de métriques du SDK OpenTelemetry, l’enrichit avec des labels et l’exporte à l’aide d’un exporteur de métriques dédié.

C’est le même principe de conception que pour le reste du reporter :

Kafka ne doit jamais être bloqué par le pipeline d’observabilité.

Si le collector est indisponible, lent ou mal configuré, Kafka doit continuer à servir le trafic. Le lot de télémétrie peut être abandonné. La latence de Kafka ne doit pas dépendre du backend de supervision.

Le récepteur de télémétrie cliente dispose lui aussi de ses propres métriques d’auto-supervision :

monedula_reporter_clienttelemetry_received_total
monedula_reporter_clienttelemetry_forwarded_total
monedula_reporter_clienttelemetry_dropped_total
monedula_reporter_clienttelemetry_unsupported_metrics_dropped_total
monedula_reporter_clienttelemetry_queue_depth

Ainsi, si la télémétrie cliente cesse de circuler, il y a quelque chose à examiner avant de se lancer dans des conjectures.

Installation

L’artefact est publié sur Maven Central, il n’est donc pas nécessaire de le compiler manuellement.

Pour la version 0.10.0, téléchargez le JAR depuis Maven Central :

curl -L \
  -o monedula-metrics-reporter-0.10.0.jar \
  https://repo1.maven.org/maven2/dev/monedula/monedula-metrics-reporter/0.10.0/monedula-metrics-reporter-0.10.0.jar

Puis copiez-le dans le classpath de Kafka :

cp monedula-metrics-reporter-0.10.0.jar $KAFKA_HOME/libs/

Ensuite, configurez le broker :

metric.reporters=dev.monedula.metricsreporter.OtlpMetricReporter

otlp.metric.reporter.endpoint=http://otel-collector:4317
otlp.metric.reporter.transport=grpc

Cela active le reporter pour les métriques Kafka SPI, les métriques broker Yammer et la télémétrie cliente.

La télémétrie cliente est activée par défaut sur les brokers :

otlp.metric.reporter.client.telemetry.enabled=true

Vous pouvez la désactiver explicitement :

otlp.metric.reporter.client.telemetry.enabled=false

Lorsqu’elle est désactivée, le broker n’annoncera pas la capacité de télémétrie aux clients.

Configuration du collector

Une configuration minimale d’OpenTelemetry Collector peut recevoir l’OTLP du reporter et exposer les métriques pour Prometheus :

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"
      http:
        endpoint: "0.0.0.0:4318"

exporters:
  prometheus:
    endpoint: "0.0.0.0:8889"
    resource_to_telemetry_conversion:
      enabled: true

service:
  pipelines:
    metrics:
      receivers: [otlp]
      exporters: [prometheus]

Le paramètre resource_to_telemetry_conversion est important lorsqu’on utilise Prometheus. Il promeut les attributs de ressource OpenTelemetry en labels Prometheus.

Sans lui, des labels tels que kafka_cluster_id, kafka_node_id et client_id pourraient ne pas apparaître là où vous les attendez.

Prometheus peut ensuite scraper le collector :

scrape_configs:
  - job_name: otel-collector
    static_configs:
      - targets: ["otel-collector:8889"]

Le point crucial : les abonnements aux métriques clientes

Installer le plugin ne suffit pas.

C’est la partie qu’il est facile de manquer avec KIP-714.

Les clients n’envoient des métriques que lorsqu’un abonnement aux métriques clientes est configuré sur le cluster.

Par exemple :

kafka-client-metrics.sh \
  --bootstrap-server broker:9092 \
  --alter \
  --name all-clients \
  --metrics "*" \
  --interval 5000

Cela crée ou met à jour un abonnement nommé all-clients, demande toutes les métriques clientes disponibles et fixe l’intervalle d’envoi à 5 secondes.

Pour un usage en production, tout collecter peut s’avérer excessif. Vous pouvez aussi vous abonner uniquement à certains préfixes de métriques.

Par exemple, vous pouvez commencer par les métriques clientes Kafka standard :

kafka-client-metrics.sh \
  --bootstrap-server broker:9092 \
  --alter \
  --name standard-client-metrics \
  --metrics "org.apache.kafka." \
  --interval 10000

La stratégie d’abonnement exacte dépend de ce que vous souhaitez observer et du volume de métriques que vous êtes prêt à accepter.

L’essentiel est que l’abonnement réside du côté broker. Cela permet à l’opérateur Kafka de décider de ce qui doit être collecté sans demander à chaque équipe applicative de modifier son dispositif de supervision.

Qu’en est-il de la configuration des clients ?

Pour les clients qui prennent en charge KIP-714, l’envoi des métriques est généralement activé par défaut.

Vous pouvez néanmoins le rendre explicite :

enable.metrics.push=true

Pour les clients Java, cela fait partie de la configuration du client Kafka.

Pour les clients basés sur librdkafka, comme confluent-kafka-python, le même principe s’applique. Par exemple :

Producer({
    "bootstrap.servers": "broker:9092",
    "client.id": "orders-producer",
    "enable.metrics.push": True,
})

Et pour un consumer :

Consumer({
    "bootstrap.servers": "broker:9092",
    "group.id": "orders-consumer-group",
    "client.id": "orders-consumer",
    "enable.metrics.push": True,
})

Il vaut la peine de définir le client.id de manière réfléchie.

Le reporter l’ajoute par défaut aux métriques clientes transmises, de sorte que les tableaux de bord peuvent séparer les métriques par application. Sans un client id significatif, vous obtiendrez tout de même des métriques, mais elles seront bien plus difficiles à exploiter lors du dépannage.

Options d’enrichissement

Par défaut, les métriques clientes transmises sont enrichies avec l’identité du broker et le client id :

otlp.metric.reporter.client.telemetry.enrich.broker=true
otlp.metric.reporter.client.telemetry.enrich.client.id=true

Cela signifie que les métriques clientes peuvent être regroupées par cluster, par broker et par application.

Il existe aussi des enrichissements optionnels :

otlp.metric.reporter.client.telemetry.enrich.client.instance.id=true
otlp.metric.reporter.client.telemetry.enrich.client.identity=true

Le client instance id est utile lorsque vous voulez distinguer les instances de client en cours d’exécution, mais il est à forte cardinalité.

L’identité du client ajoute le principal et l’adresse du client. Cela peut être très utile dans des environnements contrôlés, mais peut aussi contenir des informations sensibles, c’est pourquoi c’est désactivé par défaut.

En bref : commencez par les labels de broker et le client id. N’activez le reste que lorsque vous en avez réellement besoin.

Quickstart

Le dépôt contient une stack quickstart avec Kafka, l’OpenTelemetry Collector, Prometheus et Grafana.

Démarrez le quickstart :

cd quickstart
docker compose up -d

Le service de démonstration crée un abonnement aux métriques clientes KIP-714 et démarre du trafic producer et consumer. Ainsi, une fois la stack lancée, les tableaux de bord des brokers comme ceux des clients devraient commencer à recevoir des données.

Grafana est accessible à l’adresse :

http://localhost:3000

Prometheus est accessible à l’adresse :

http://localhost:9090

Le quickstart crée l’abonnement avec :

kafka-client-metrics.sh \
  --bootstrap-server kafka1:9092 \
  --alter \
  --name quickstart-clients \
  --metrics "*" \
  --interval 5000

C’est volontairement simple : tout collecter, envoyer toutes les 5 secondes, et faire prendre vie rapidement aux tableaux de bord.

Pour des environnements réels, je commencerais généralement avec plus de prudence. Collecter d’abord les métriques standard, valider le volume, puis ajouter davantage de métriques si nécessaire.

Clients Java vs autres clients

Un point intéressant à propos de KIP-714 est qu’il crée un mécanisme commun, mais que toutes les bibliothèques clientes n’exposent pas la même profondeur de métriques.

Le client Java envoie l’ensemble de métriques le plus riche. Il peut envoyer de nombreuses métriques de son registre interne, y compris des détails plus poussés sur les producers et consumers : batching, utilisation des buffers, timing des requêtes, découpage par topic, lag et lead du consumer, et d’autres signaux propres au client Java.

D’autres bibliothèques clientes peuvent envoyer un ensemble plus réduit.

Cela ne signifie pas que KIP-714 n’est utile que pour les clients Java. Il reste très précieux de disposer d’un chemin de collecte commun, piloté par le broker, pour tous les clients qui le prennent en charge. Mais lorsque vous regardez les tableaux de bord, vous devez garder à l’esprit qu’un panneau manquant ne signifie pas toujours que quelque chose est cassé.

Parfois, la métrique n’existe tout simplement pas dans cette bibliothèque cliente.

C’est pourquoi les nouveaux tableaux de bord Grafana sont divisés en deux parties :

  1. Panneaux standard
  2. Panneaux étendus

Les panneaux standard devraient fonctionner pour tout client conforme à KIP-714 qui expose les métriques communes pertinentes.

Les panneaux étendus sont surtout utiles pour le client Java, car c’est lui qui fournit actuellement l’ensemble de métriques le plus riche.

Cette séparation est intentionnelle. Je ne veux pas d’un tableau de bord qui semble cassé simplement parce qu’un client non-Java n’expose pas de métriques spécifiques à Java. Il vaut mieux rendre cette frontière visible.

Nouveaux tableaux de bord

La nouvelle version ajoute deux tableaux de bord axés sur les clients :

  • Kafka Producers (client)
  • Kafka Consumers (client)

Le tableau de bord des producers se concentre sur la vue côté client de la production d’enregistrements : latence des requêtes, throttling, mise en file d’attente, batching, erreurs et débit.

Le tableau de bord des consumers se concentre sur la vue côté client de la consommation d’enregistrements : latence de fetch, taux de commit, comportement des poll, signaux liés aux rebalances, changements d’assignation, et lag/lead côté consumer lorsqu’ils sont disponibles.

Les deux tableaux de bord incluent des filtres par cluster et par client id. Là où l’ensemble de métriques le permet, ils incluent aussi des explorations détaillées au niveau des topics ou des groupes.

Le tableau de bord des producers - côté client.
Le tableau de bord des producers - côté client
Le tableau de bord des consumers - côté client.
Le tableau de bord des consumers - côté client

Il y a une chose que j’apprécie vraiment dans ce dispositif : le même dossier Grafana peut désormais montrer les deux facettes de l’histoire.

Les tableaux de bord des brokers répondent à :

Le cluster est-il en bonne santé ?

Les tableaux de bord des clients répondent à :

Comment les applications vivent-elles le cluster ?

Et comme les deux proviennent du même pipeline OTLP, avec des labels cohérents, il devient beaucoup plus facile de les corréler.

En résumé

KIP-714 est une étape très importante pour l’observabilité de Kafka.

Il transforme les métriques clientes : d’une chose que chaque équipe applicative devait exposer de manière indépendante, elles deviennent quelque chose que le cluster Kafka peut demander de façon centralisée.

monedula-metrics-reporter prend désormais en charge ce modèle.

Il exporte toujours les métriques des brokers depuis les deux registres de métriques de Kafka. Mais il reçoit maintenant aussi la télémétrie OTLP envoyée par les clients KIP-714 et la transmet au même OpenTelemetry Collector.

Le plugin couvre donc désormais trois sources :

  • Métriques Kafka SPI
  • Métriques broker Yammer
  • Télémétrie cliente KIP-714

Le résultat est une architecture de supervision Kafka plus simple : un seul plugin sur les brokers, un seul pipeline OTLP, et des tableaux de bord qui couvrent le comportement côté broker comme côté client.

Le code est open source et disponible sur GitHub