En el artículo anterior escribí sobre las métricas del broker de Kafka y por qué recopilarlas es más difícil de lo que debería.
La versión corta era: Kafka no tiene un único sistema de métricas.
Tiene el más reciente Kafka Metrics SPI, configurado con metric.reporters, y el más antiguo registro Yammer/Coda Hale, configurado con kafka.metrics.reporters. Algunas métricas importantes del broker viven en un sistema y otras en el otro, y la mayoría de las configuraciones de monitorización del mundo real todavía pasan por JMX, expresiones regulares, mapeos de Prometheus y dashboards que pueden coincidir o no con los nombres exportados.
Esa fue la razón para crear monedula-metrics-reporter: un plugin de Kafka que exporta métricas directamente sobre OTLP a un OpenTelemetry Collector y maneja ambos registros de métricas de Kafka con una sola configuración.
Pero todavía faltaba una parte.
Las métricas del broker nos dicen mucho sobre el clúster, pero no explican del todo qué está pasando dentro de las aplicaciones cliente.
Un broker puede decirnos que las solicitudes Produce son lentas. Puede decirnos que las solicitudes Fetch son lentas. Puede informarnos sobre las colas de solicitudes, los procesadores de red, las particiones subreplicadas, las solicitudes fallidas y muchas otras cosas.
Pero a veces la pregunta real no es solo:
¿Qué está pasando en el broker?
También es:
¿Qué está pasando dentro del producer o del consumer?
¿Está el producer agrupando en lotes de forma eficiente? ¿Está siendo limitado (throttled)? ¿Está el consumer haciendo poll con regularidad? ¿Está pasando tiempo en rebalances? ¿Se comporta una aplicación de forma diferente a otra?
Tradicionalmente, responder a estas preguntas significaba instrumentar cada aplicación cliente por separado. En Java, eso a menudo significaba JMX otra vez. En otros lenguajes, dependía de la biblioteca cliente, del framework y de cualquier configuración de observabilidad que tuviera el equipo.
Y en organizaciones grandes esto se convierte en un problema muy familiar: el equipo de Kafka es dueño del clúster, pero las aplicaciones cliente son propiedad de muchos equipos diferentes. Así que cuando algo va mal, el operador del clúster a menudo no dispone de las métricas del lado del cliente que necesita, y obtenerlas requiere coordinación, cambios de código, redespliegues o al menos reinicios.
Este es exactamente el problema que KIP-714 intenta resolver.
La idea detrás de KIP-714
KIP-714 añade una forma dirigida por el broker de recopilar métricas de los clientes de Kafka.
En lugar de que cada equipo de aplicación configure su propio exportador de métricas, el clúster de Kafka puede definir suscripciones a métricas de clientes. Una suscripción indica qué métricas de cliente deben recopilarse y con qué frecuencia deben enviarlas los clientes.
Los clientes entonces envían sus métricas a los brokers utilizando el protocolo de Kafka. El payload en sí es OTLP, por lo que el plugin del lado del broker puede reenviarlo a un OpenTelemetry Collector, a una base de datos de series temporales o a cualquier otro backend de observabilidad.
La parte importante es la dirección del control.
El operador del clúster decide qué recopilar.
Los clientes que soportan KIP-714 pueden recibir la suscripción y enviar las métricas solicitadas. La aplicación no necesita exponer JMX. No necesita un exportador sidecar. No necesita un endpoint personalizado de Prometheus.
Todavía hay dos condiciones:
- El broker debe tener configurado un plugin de telemetría de clientes.
- Debe existir al menos una suscripción a métricas de clientes.
Sin una suscripción, los clientes pueden soportar el protocolo, pero no enviarán métricas.
Este es un buen diseño. Las métricas de cliente no se recopilan accidentalmente. El operador debe habilitar explícitamente la recopilación del lado del clúster.
Un detalle importante: no todos los clientes envían las mismas métricas
KIP-714 crea un mecanismo común para recopilar métricas de clientes, pero no hace que, por arte de magia, todos los clientes de Kafka expongan el mismo conjunto de métricas.
Las métricas que obtienes dependen de la biblioteca cliente.
El cliente de Java es actualmente el que envía el conjunto de métricas más rico. Esto no es sorprendente: el cliente de Java es la implementación de referencia, tiene un registro de métricas interno muy maduro y muchas métricas de Kafka se diseñaron primero allí.
Otros clientes pueden exponer un conjunto más reducido. Los clientes basados en librdkafka, como los que usan los wrappers de Python, Go, .NET y Node, también pueden participar en KIP-714, pero las métricas disponibles no son necesariamente idénticas a las que envía el cliente de Java.
Así que el modelo mental debería ser:
KIP-714 estandariza cómo se solicitan y transportan las métricas de cliente. El conjunto de métricas real todavía depende de la implementación del cliente.
Esto importa a la hora de construir dashboards.
Algunos paneles pueden basarse en métricas comunes y deberían funcionar en distintas bibliotecas cliente. Otros paneles solo tienen sentido cuando el cliente envía realmente el conjunto de métricas más rico. En la práctica, el cliente de Java ofrece hoy la historia de observabilidad más completa.
¿Por qué añadir esto a monedula-metrics-reporter?
Al principio, monedula-metrics-reporter se centraba en las métricas del broker.
Manejaba el registro Kafka SPI. Manejaba el registro Yammer. Exportaba ambos sobre OTLP. Añadía la identidad del broker como atributos de recurso. Se distribuía con dashboards de Grafana que coincidían con las métricas emitidas.
Pero una vez que existe KIP-714, hay un siguiente paso muy natural:
Si el plugin ya se ejecuta dentro del broker y ya exporta OTLP al collector, también debería manejar las métricas enviadas por los clientes.
De lo contrario, la monitorización de Kafka seguiría estando fragmentada.
Tendrías una ruta para las métricas del SPI del broker, otra ruta para las métricas de Yammer y todavía otro plugin o componente para la telemetría de clientes de KIP-714.
Eso funcionaría, pero contradiría toda la idea de simplificar el stack de métricas de Kafka.
Así que la nueva versión de monedula-metrics-reporter ahora maneja tres fuentes de métricas:
- Kafka Metrics SPI
- Métricas del broker de Yammer / Coda Hale
- Telemetría enviada por los clientes de KIP-714
El objetivo es simple: un plugin, un pipeline de collector, un conjunto de dashboards.
Las métricas de cliente se reenvían a través de la misma ruta de OpenTelemetry que las métricas del broker. Pueden llevar las mismas etiquetas del broker, como kafka_cluster_id y kafka_node_id, y también pueden enriquecerse con el client_id, de modo que las métricas de distintas aplicaciones no se fundan en un único flujo anónimo.
Ese último punto es muy importante.
Si dos producers envían el mismo nombre de métrica, necesitamos saber cuál es cuál. Por defecto, el reporter añade el client id como etiqueta. Las etiquetas más sensibles o de alta cardinalidad, como el client instance id, el principal o la dirección del cliente, son opcionales (opt-in).
Cómo funciona
La parte del lado del broker está implementada a través de la interfaz ClientTelemetry de Kafka.
Cuando un cliente compatible con KIP-714 envía telemetría a un broker, el plugin recibe el payload OTLP. El hilo request-handler realiza solo un trabajo mínimo: copia el payload a una cola en memoria acotada. Luego, un hilo daemon lee de esa cola, convierte el payload en datos de métricas del SDK de OpenTelemetry, lo enriquece con etiquetas y lo exporta usando un exportador de métricas dedicado.
Este es el mismo principio de diseño que el resto del reporter:
Kafka nunca debe ser bloqueado por el pipeline de observabilidad.
Si el collector no está disponible, está lento o mal configurado, Kafka debe continuar sirviendo tráfico. El lote de telemetría puede descartarse. La latencia de Kafka no debe depender del backend de monitorización.
El receptor de telemetría de clientes también tiene sus propias métricas de automonitorización:
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
Así que si la telemetría de clientes deja de fluir, hay algo que revisar antes de ponerse a adivinar.
Instalación
El artefacto está publicado en Maven Central, por lo que no hace falta compilarlo manualmente.
Para la versión 0.10.0, descarga el JAR desde 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
Luego cópialo al classpath de Kafka:
cp monedula-metrics-reporter-0.10.0.jar $KAFKA_HOME/libs/
Después de eso, configura el broker:
metric.reporters=dev.monedula.metricsreporter.OtlpMetricReporter
otlp.metric.reporter.endpoint=http://otel-collector:4317
otlp.metric.reporter.transport=grpc
Esto habilita el reporter para las métricas del Kafka SPI, las métricas del broker de Yammer y la telemetría de clientes.
La telemetría de clientes está habilitada por defecto en los brokers:
otlp.metric.reporter.client.telemetry.enabled=true
Puedes deshabilitarla explícitamente:
otlp.metric.reporter.client.telemetry.enabled=false
Cuando está deshabilitada, el broker no anunciará la capacidad de telemetría a los clientes.
Configuración del collector
Una configuración mínima de OpenTelemetry Collector puede recibir OTLP del reporter y exponer las métricas para 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]
La opción resource_to_telemetry_conversion es importante cuando se usa Prometheus. Promueve los atributos de recurso de OpenTelemetry a etiquetas de Prometheus.
Sin ella, etiquetas como kafka_cluster_id, kafka_node_id y client_id pueden no aparecer donde esperas.
Prometheus puede entonces hacer scrape del collector:
scrape_configs:
- job_name: otel-collector
static_configs:
- targets: ["otel-collector:8889"]
La parte importante: suscripciones a métricas de clientes
Instalar el plugin no es suficiente.
Esta es la parte que es fácil pasar por alto con KIP-714.
Los clientes envían métricas solo cuando hay una suscripción a métricas de clientes configurada en el clúster.
Por ejemplo:
kafka-client-metrics.sh \
--bootstrap-server broker:9092 \
--alter \
--name all-clients \
--metrics "*" \
--interval 5000
Esto crea o actualiza una suscripción llamada all-clients, solicita todas las métricas de cliente disponibles y establece el intervalo de envío en 5 segundos.
Para uso en producción, recopilar todo puede ser demasiado. También puedes suscribirte solo a prefijos de métricas seleccionados.
Por ejemplo, puedes empezar con las métricas estándar del cliente de Kafka:
kafka-client-metrics.sh \
--bootstrap-server broker:9092 \
--alter \
--name standard-client-metrics \
--metrics "org.apache.kafka." \
--interval 10000
La estrategia exacta de suscripción depende de qué quieras observar y de qué volumen de métricas te resulte cómodo.
Lo importante es que la suscripción vive en el lado del broker. Esto hace posible que el operador de Kafka decida qué debe recopilarse sin pedir a cada equipo de aplicación que cambie su configuración de monitorización.
¿Y la configuración del cliente?
Para los clientes que soportan KIP-714, el envío de métricas suele estar habilitado por defecto.
Aun así, puedes hacerlo explícito:
enable.metrics.push=true
Para los clientes de Java, esto forma parte de la configuración del cliente de Kafka.
Para los clientes basados en librdkafka, como confluent-kafka-python, aplica la misma idea. Por ejemplo:
Producer({
"bootstrap.servers": "broker:9092",
"client.id": "orders-producer",
"enable.metrics.push": True,
})
Y para un consumer:
Consumer({
"bootstrap.servers": "broker:9092",
"group.id": "orders-consumer-group",
"client.id": "orders-consumer",
"enable.metrics.push": True,
})
Vale la pena establecer el client.id de forma deliberada.
El reporter lo añade por defecto a las métricas de cliente reenviadas, de modo que los dashboards pueden separar las métricas por aplicación. Sin un client id significativo, seguirás obteniendo métricas, pero serán mucho más difíciles de usar durante la resolución de problemas.
Opciones de enriquecimiento
Por defecto, las métricas de cliente reenviadas se enriquecen con la identidad del broker y el client id:
otlp.metric.reporter.client.telemetry.enrich.broker=true
otlp.metric.reporter.client.telemetry.enrich.client.id=true
Esto significa que las métricas de cliente pueden agruparse por clúster, broker y aplicación.
También hay enriquecimientos opcionales:
otlp.metric.reporter.client.telemetry.enrich.client.instance.id=true
otlp.metric.reporter.client.telemetry.enrich.client.identity=true
El client instance id es útil cuando quieres distinguir instancias de cliente individuales en ejecución, pero es de alta cardinalidad.
La identidad del cliente añade el principal y la dirección del cliente. Esto puede ser muy útil en entornos controlados, pero también puede contener información sensible, por lo que está deshabilitado por defecto.
En resumen: empieza con las etiquetas del broker y el client id. Habilita el resto solo cuando realmente lo necesites.
Quickstart
El repositorio contiene un stack de quickstart con Kafka, el OpenTelemetry Collector, Prometheus y Grafana.
Inicia el quickstart:
cd quickstart
docker compose up -d
El servicio de demostración crea una suscripción a métricas de clientes de KIP-714 e inicia tráfico de producer y consumer. Así que, una vez levantado el stack, tanto los dashboards del broker como los dashboards de cliente deberían empezar a recibir datos.
Grafana está disponible en:
http://localhost:3000
Prometheus está disponible en:
http://localhost:9090
El quickstart crea la suscripción con:
kafka-client-metrics.sh \
--bootstrap-server kafka1:9092 \
--alter \
--name quickstart-clients \
--metrics "*" \
--interval 5000
Eso es intencionadamente simple: recopilar todo, enviar cada 5 segundos y hacer que los dashboards cobren vida rápidamente.
Para entornos reales, yo normalmente empezaría con más cautela. Primero recopilar las métricas estándar, validar el volumen y luego añadir más métricas si hace falta.
Clientes Java frente a otros clientes
Una cosa interesante de KIP-714 es que crea un mecanismo común, pero no todas las bibliotecas cliente exponen la misma profundidad de métricas.
El cliente de Java envía el conjunto de métricas más rico. Puede enviar muchas métricas de su registro interno, incluidos detalles más ricos de producer y consumer: agrupación en lotes, uso de buffer, tiempos de solicitud, desgloses a nivel de topic, lag y lead del consumer y otras señales específicas del cliente de Java.
Otras bibliotecas cliente pueden enviar un conjunto más reducido.
Esto no significa que KIP-714 sea útil solo para clientes Java. Sigue siendo muy útil disponer de una ruta de recopilación común, dirigida por el broker, para todos los clientes que lo soporten. Pero cuando mires los dashboards, deberías recordar que los paneles ausentes no siempre significan que algo esté roto.
A veces la métrica simplemente no existe en esa biblioteca cliente.
Por eso los nuevos dashboards de Grafana están divididos en dos partes:
- Paneles estándar
- Paneles extendidos
Los paneles estándar deberían funcionar para cualquier cliente compatible con KIP-714 que exponga las métricas comunes relevantes.
Los paneles extendidos son útiles principalmente para el cliente de Java, porque el cliente de Java proporciona actualmente el conjunto de métricas más rico.
Esta división es intencionada. No quiero un dashboard que parezca roto solo porque un cliente no-Java no expone métricas específicas de Java. Es mejor hacer visible la frontera.
Nuevos dashboards
La nueva versión añade dos dashboards centrados en el cliente:
- Kafka Producers (cliente)
- Kafka Consumers (cliente)
El dashboard de producers se centra en la vista del lado del cliente de la producción de registros: latencia de solicitudes, throttling, encolado, agrupación en lotes, errores y throughput.
El dashboard de consumers se centra en la vista del lado del cliente del consumo de registros: latencia de fetch, tasa de commit, comportamiento del poll, señales relacionadas con rebalances, cambios de asignación y lag/lead del lado del consumer cuando está disponible.
Ambos dashboards incluyen filtros por clúster y por client id. Cuando el conjunto de métricas lo permite, también incluyen desgloses a nivel de topic o de group.


Hay una cosa que me gusta mucho de esta configuración: la misma carpeta de Grafana ahora puede mostrar ambos lados de la historia.
Los dashboards del broker responden:
¿Está sano el clúster?
Los dashboards de cliente responden:
¿Cómo experimentan las aplicaciones el clúster?
Y como ambos provienen del mismo pipeline OTLP, con etiquetas consistentes, se vuelve mucho más fácil correlacionarlos.
Resumen
KIP-714 es un paso muy importante para la observabilidad de Kafka.
Cambia las métricas de cliente de ser algo que cada equipo de aplicación tiene que exponer de forma independiente a ser algo que el clúster de Kafka puede solicitar de forma centralizada.
monedula-metrics-reporter ahora soporta este modelo.
Sigue exportando las métricas del broker desde ambos registros de métricas de Kafka. Pero ahora también recibe la telemetría OTLP enviada por los clientes de KIP-714 y la reenvía al mismo OpenTelemetry Collector.
Así que el plugin ahora cubre tres fuentes:
- Métricas del Kafka SPI
- Métricas del broker de Yammer
- Telemetría de clientes de KIP-714
El resultado es una arquitectura de monitorización de Kafka más simple: un plugin en los brokers, un pipeline OTLP y dashboards que cubren el comportamiento tanto del lado del broker como del lado del cliente.
El código es de código abierto y está disponible en GitHub