Waarom Kafka-monitoring van cruciaal belang is

Apache Kafka is ontworpen om veerkrachtig te zijn, maar veerkracht is niet automatisch: het vereist actieve waarneembaarheid. Een overbelaste makelaar, een consumentengroep die de productie niet kan bijhouden, of een partitie met een verminderde replicatie als ze niet tijdig worden opgemerkt, kunnen ze leiden tot gegevensverlies of downtime.

De drie fundamentele dimensies die in Kafka moeten worden gemonitord zijn:

  • Clustergezondheid: actieve makelaars, actieve controller, te weinig gerepliceerde partities, offline partities
  • Prestaties van de producent: verzoeksnelheid, recordfoutenpercentage, latentie van verzoeken, batchgrootte
  • Consumentenprestaties: consumentenvertraging per groep en per partitie, commit-snelheid, frequentie van herbalanceren

De 5 meest kritische Kafka-statistieken

  • kafka.consumer.lag: verschil tussen LEO en vastgelegde offset — de nummer 1 maatstaf voor systeemgezondheid
  • kafka.server.UnderReplicatedPartitions: partities met onvolledige ISR - verslechterd brokersignaal
  • kafka.server.OfflinePartitionsCount: leiderloze partities - kritieke fout, gegevens ontoegankelijk
  • kafka.network.RequestMetrics.TotalTimeMs: totale latentie van verzoeken
  • kafka.server.BrokerTopicMetrics.MessagesInPerSec: opnamedoorvoer

Hoe Kafka statistieken blootlegt: JMX

Kafka maakt al zijn interne statistieken openbaar via JMX (Java-beheerextensies), het standaardframework voor monitoring van JVM-applicaties. Elke metriek wordt geïdentificeerd door een MBean-naam in het formaat dominio:tipo=Tipo,nome=Metrica.

Om JMX-statistieken toegankelijk te maken voor Prometheus, het meest gebruikte monitoringsysteem in een cloud-native omgeving, jij gebruikt de JMX-exporteur: een Java-agent die JMX-statistieken beschikbaar stelt als een HTTP-eindpunt in Prometheus-formaat (op tekst gebaseerd, pull-model).

Configureer JMX Exporter als Java-agent

JMX Exporter wordt gestart als een JVM-agent: voeg gewoon de parameter toe -javaagent aan de Kafka-makelaar JVM. U downloadt de JAR uit de officiële Prometheus-repository en maakt een YAML-configuratiebestand dat specificeert welke MBeans je moet verzamelen en hoe je ze kunt hernoemen naar Prometheus-statistieken.

# Scaricare JMX Exporter (ultima versione stabile)
wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/1.0.1/jmx_prometheus_javaagent-1.0.1.jar \
  -O /opt/kafka/libs/jmx_prometheus_javaagent.jar

# File di configurazione: /opt/kafka/config/kafka-jmx-exporter.yml
# Questo file dice a JMX Exporter quali MBean raccogliere
# kafka-jmx-exporter.yml
# Configurazione completa per Kafka broker + consumer group lag
---
startDelaySeconds: 0
ssl: false
lowercaseOutputName: true
lowercaseOutputLabelNames: true

rules:
  # Metriche Broker: throughput messaggi
  - pattern: 'kafka.server<type=BrokerTopicMetrics, name=MessagesInPerSec><>OneMinuteRate'
    name: kafka_server_brokertopicmetrics_messagesinpersec
    labels:
      topic: "$1"

  # Under-replicated partitions: CRITICO
  - pattern: 'kafka.server<type=ReplicaManager, name=UnderReplicatedPartitions><>Value'
    name: kafka_server_replicamanager_underreplicatedpartitions

  # Offline partitions: CRITICO
  - pattern: 'kafka.controller<type=KafkaController, name=OfflinePartitionsCount><>Value'
    name: kafka_controller_kafkacontroller_offlinepartitionscount

  # Active controller count (deve essere sempre 1)
  - pattern: 'kafka.controller<type=KafkaController, name=ActiveControllerCount><>Value'
    name: kafka_controller_kafkacontroller_activecontrollercount

  # Request latency per tipo di request
  - pattern: 'kafka.network<type=RequestMetrics, name=TotalTimeMs, request=(\w+)><>(Mean|99thPercentile)'
    name: kafka_network_requestmetrics_totaltimems
    labels:
      request: "$1"
      quantile: "$2"

  # Producer request rate
  - pattern: 'kafka.server<type=BrokerTopicMetrics, name=BytesInPerSec><>OneMinuteRate'
    name: kafka_server_brokertopicmetrics_bytesinpersec

  # Log size per topic-partizione
  - pattern: 'kafka.log<type=Log, name=Size, topic=(.+), partition=(\d+)><>Value'
    name: kafka_log_log_size
    labels:
      topic: "$1"
      partition: "$2"

  # JVM heap usage
  - pattern: 'java.lang<type=Memory><>HeapMemoryUsage'
    name: jvm_heap_memory_usage
    type: GAUGE

Schakel JMX Exporter in de Broker in

# Modificare kafka-server-start.sh oppure impostare la variabile d'ambiente
# Aggiungere al file bin/kafka-server-start.sh o configurare systemd

# Opzione 1: variabile d'ambiente KAFKA_OPTS
export KAFKA_OPTS="-javaagent:/opt/kafka/libs/jmx_prometheus_javaagent.jar=9404:/opt/kafka/config/kafka-jmx-exporter.yml"

# Opzione 2: in systemd service file
# [Service]
# Environment="KAFKA_OPTS=-javaagent:/opt/kafka/libs/jmx_prometheus_javaagent.jar=9404:/opt/kafka/config/kafka-jmx-exporter.yml"

# Verificare che l'endpoint funzioni dopo il riavvio
curl http://localhost:9404/metrics | grep kafka_server_replicamanager

Consumentenvertraging: de belangrijkste maatstaf

Il vertraging van de consument meet hoeveel records de consument nog moet verwerken: het is het verschil daartussen de Logeinde-offset (de laatste record die in het onderwerp is geschreven) en de Toegewijde compensatie (het laatste record dat door de consumentengroep is verwerkt). Een toenemende vertraging geeft aan dat consumenten de snelheid van de productie niet kunnen bijhouden.

De vertraging alleen onthult de JMX-statistieken van de makelaars niet: deze moeten worden verzameld door de coördinator van de consumentengroep te ondervragen. Het standaardhulpmiddel hiervoor is Kafka Lag-exporteur (Lightbend/open source-project) of de plug-in kafka_consumentengroep van Prometheus zelf.

# Docker Compose per Kafka Lag Exporter (alternativa moderna)
# https://github.com/seglo/kafka-lag-exporter

services:
  kafka-lag-exporter:
    image: seglo/kafka-lag-exporter:0.8.0
    ports:
      - "8000:8000"
    volumes:
      - ./lag-exporter.conf:/opt/docker/conf/application.conf
    environment:
      JAVA_OPTS: "-Xmx256m"
# lag-exporter.conf (formato HOCON)
kafka-lag-exporter {
  port = 8000

  clusters = [
    {
      name = "production-cluster"
      bootstrap-brokers = "kafka1:9092,kafka2:9092,kafka3:9092"

      # Polling interval
      poll-interval = 30 seconds

      # Consumer groups da monitorare (regex, vuoto = tutti)
      consumer-group-whitelist = [".*"]
    }
  ]

  # Metriche Prometheus esposte:
  # kafka_consumer_group_latest_offset
  # kafka_consumer_group_partition_lag
  # kafka_consumer_group_sum_lag  <-- lag totale per group
  # kafka_consumer_group_max_lag  <-- lag massimo tra le partizioni
}

Bereken vertraging met PromQL

# Query PromQL per Grafana / Prometheus

# Lag totale per consumer group
sum(kafka_consumer_group_partition_lag) by (group)

# Top 10 consumer groups per lag
topk(10, sum(kafka_consumer_group_partition_lag) by (group))

# Tasso di crescita del lag (se positivo, i consumer non stanno al passo)
rate(kafka_consumer_group_sum_lag[5m])

# Consumer lag per topic specifico
sum(kafka_consumer_group_partition_lag{topic="ordini-effettuati"}) by (group)

# Alert: lag sopra soglia critica per piu di 5 minuti
# kafka_consumer_group_sum_lag{group="servizio-inventario"} > 10000

Configureer Prometheus voor Kafka

Prometheus gebruikt een sjabloon trekken: het is Prometheus die statistieken van de eindpunten verzamelt blootgesteld door exporteurs, volgens een configureerbaar interval (scrape_interval). De configuratie specificeert de doel schrapen: De HTTP-eindpunten die moeten worden opgevraagd.

# prometheus.yml - configurazione per scraping Kafka
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "kafka-alerts.yml"

scrape_configs:
  # JMX Exporter su ogni broker Kafka
  - job_name: "kafka-brokers"
    static_configs:
      - targets:
          - "kafka1:9404"
          - "kafka2:9404"
          - "kafka3:9404"
    relabel_configs:
      - source_labels: [__address__]
        target_label: broker
        regex: "([^:]+):.*"
        replacement: "$1"

  # Kafka Lag Exporter per consumer group metrics
  - job_name: "kafka-lag-exporter"
    static_configs:
      - targets:
          - "kafka-lag-exporter:8000"
    scrape_interval: 30s  # lag cambia meno frequentemente

  # JVM metrics dei broker (se abilitato jvm_exporter separato)
  - job_name: "kafka-jvm"
    static_configs:
      - targets:
          - "kafka1:9404"
          - "kafka2:9404"
          - "kafka3:9404"
    metrics_path: /metrics
    params:
      module: [jvm]

Waarschuwingsregels Kafka: de essentiële regels

De waarschuwingsregels definiëren de voorwaarden die een melding activeren. In Prometheus drukken ze zich uit met PromQL en worden gebundeld in afzonderlijke YAML-bestanden. Hier zijn de basisregels voor Kafka in productie:

# kafka-alerts.yml - Regole di alert per Prometheus Alertmanager
groups:
  - name: kafka.critical
    rules:
      # Broker down: nessun dato dall'endpoint JMX
      - alert: KafkaBrokerDown
        expr: up{job="kafka-brokers"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Kafka broker {{ $labels.broker }} non raggiungibile"
          description: "Il broker {{ $labels.broker }} non risponde da piu di 1 minuto."

      # Partizioni offline: CRITICO, dati non accessibili
      - alert: KafkaOfflinePartitions
        expr: kafka_controller_kafkacontroller_offlinepartitionscount > 0
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "{{ $value }} partizioni offline in Kafka"
          description: "Partizioni senza leader: dati non leggibili/scrivibili."

      # Nessun active controller
      - alert: KafkaNoActiveController
        expr: sum(kafka_controller_kafkacontroller_activecontrollercount) != 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Kafka senza controller attivo"

  - name: kafka.warning
    rules:
      # Under-replicated partitions: replica degradata
      - alert: KafkaUnderReplicatedPartitions
        expr: kafka_server_replicamanager_underreplicatedpartitions > 0
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $value }} partizioni under-replicated su {{ $labels.broker }}"
          description: "Replica degradata: un broker potrebbe essere lento o down."

      # Consumer lag critico
      - alert: KafkaConsumerLagHigh
        expr: sum(kafka_consumer_group_partition_lag) by (group) > 50000
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Consumer lag alto per gruppo {{ $labels.group }}"
          description: "Lag: {{ $value }}. I consumer non stanno al passo con la produzione."

      # Consumer lag critico prolungato
      - alert: KafkaConsumerLagCritical
        expr: sum(kafka_consumer_group_partition_lag) by (group) > 200000
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Consumer lag CRITICO per gruppo {{ $labels.group }}"

      # JVM heap usage alta
      - alert: KafkaBrokerHeapHigh
        expr: jvm_heap_memory_usage{area="used"} / jvm_heap_memory_usage{area="max"} > 0.85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Heap JVM alta su broker Kafka {{ $labels.broker }}"

Grafana-dashboard voor Kafka

Grafana is de standaard visualisatietool voor Prometheus. Er zijn kant-en-klare dashboards rechtstreeks vanuit de Grafana Dashboard Hub (grafana.com/dashboards). De meest gebruikte voor Kafka zijn:

  • Dashboard-ID 7589: Kafka-overzicht (brokerstatus, doorvoer, partities)
  • Dashboard-ID 9021: Confluent Platform Kafka-statistieken
  • Dashboard-ID 13282: Kafka Consumer Lag (gedetailleerde vertraging van de consumentengroep)

Om een ​​dashboard in Grafana te importeren: Dashboards → Importeren → voer ID in → selecteer Prometheus-gegevensbron.

Belangrijke panelen voor uw dashboard

# Pannello 1: Broker Health Overview
# Stat panel: up/down per ogni broker
up{job="kafka-brokers"}

# Pannello 2: Under-Replicated Partitions
# Stat panel con soglia colorata (verde=0, rosso>0)
sum(kafka_server_replicamanager_underreplicatedpartitions)

# Pannello 3: Consumer Lag per Group (time series)
sum(kafka_consumer_group_partition_lag) by (group)

# Pannello 4: Messages In/Out per secondo
rate(kafka_server_brokertopicmetrics_messagesinpersec[1m])

# Pannello 5: Request Latency p99 (heatmap o time series)
kafka_network_requestmetrics_totaltimems{quantile="99thPercentile"}

# Pannello 6: Disk Usage per Broker
# Utile per pianificare l'espansione storage
node_filesystem_size_bytes{mountpoint="/kafka-data"} -
node_filesystem_avail_bytes{mountpoint="/kafka-data"}

Docker Compose: complete stackmonitoring

Voor ontwikkelings- of faseringsomgevingen lanceert deze Docker Compose de volledige monitoringstack: JMX Exporter (geïntegreerd in de makelaar), Kafka Lag Exporter, Prometheus en Grafana.

# docker-compose.monitoring.yml
version: "3.9"

services:
  kafka:
    image: apache/kafka:4.0.0
    container_name: kafka
    ports:
      - "9092:9092"
      - "9404:9404"   # JMX Exporter
    volumes:
      - ./config/kafka-jmx-exporter.yml:/opt/kafka-jmx-exporter.yml
      - ./libs/jmx_prometheus_javaagent.jar:/opt/jmx_prometheus_javaagent.jar
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: "broker,controller"
      KAFKA_LISTENERS: "PLAINTEXT://kafka:9092,CONTROLLER://kafka:9093"
      KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9092"
      KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
      KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka:9093"
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT"
      CLUSTER_ID: "monitoring-demo-cluster"
      KAFKA_OPTS: "-javaagent:/opt/jmx_prometheus_javaagent.jar=9404:/opt/kafka-jmx-exporter.yml"

  kafka-lag-exporter:
    image: seglo/kafka-lag-exporter:0.8.0
    container_name: kafka-lag-exporter
    ports:
      - "8000:8000"
    volumes:
      - ./config/lag-exporter.conf:/opt/docker/conf/application.conf
    depends_on:
      - kafka

  prometheus:
    image: prom/prometheus:v2.50.1
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./config/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./config/kafka-alerts.yml:/etc/prometheus/kafka-alerts.yml
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.retention.time=30d"

  grafana:
    image: grafana/grafana:10.3.1
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: "kafka-monitoring"
      GF_USERS_ALLOW_SIGN_UP: "false"
    volumes:
      - grafana-data:/var/lib/grafana
      - ./config/grafana-datasources.yml:/etc/grafana/provisioning/datasources/prometheus.yml
      - ./config/grafana-dashboards.yml:/etc/grafana/provisioning/dashboards/kafka.yml

volumes:
  grafana-data:

Monitoring van consumentenvertraging met Java (programmatisch)

In sommige gevallen is het nuttig om de consumentenvertraging rechtstreeks in de applicatiecode te meten, bijvoorbeeld om statistiek bloot te leggen via micrometer/actuator in een Spring Boot-toepassing. Deze aanpak is complementair aan externe monitoring en maakt nauwkeurigere correlaties mogelijk.

// ConsumerLagMonitor.java - Misura il lag programmaticamente con Kafka AdminClient
import org.apache.kafka.clients.admin.*;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.*;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;

public class ConsumerLagMonitor {

    private final AdminClient adminClient;
    private final String groupId;

    public ConsumerLagMonitor(String bootstrapServers, String groupId) {
        Properties props = new Properties();
        props.put("bootstrap.servers", bootstrapServers);
        this.adminClient = AdminClient.create(props);
        this.groupId = groupId;
    }

    public Map<TopicPartition, Long> getLagPerPartition() throws ExecutionException, InterruptedException {
        // 1. Recupera committed offsets del consumer group
        ListConsumerGroupOffsetsResult offsetsResult =
            adminClient.listConsumerGroupOffsets(groupId);

        Map<TopicPartition, OffsetAndMetadata> committedOffsets =
            offsetsResult.partitionsToOffsetAndMetadata().get();

        // 2. Recupera l'end offset (Log End Offset) per le stesse partizioni
        Map<TopicPartition, OffsetSpec> latestOffsetRequest = committedOffsets.keySet().stream()
            .collect(Collectors.toMap(tp -> tp, tp -> OffsetSpec.latest()));

        ListOffsetsResult latestOffsets = adminClient.listOffsets(latestOffsetRequest);
        Map<TopicPartition, ListOffsetsResult.ListOffsetsResultInfo> endOffsets =
            latestOffsets.all().get();

        // 3. Calcola il lag = endOffset - committedOffset
        Map<TopicPartition, Long> lag = new HashMap<>();
        for (Map.Entry<TopicPartition, OffsetAndMetadata> entry : committedOffsets.entrySet()) {
            TopicPartition tp = entry.getKey();
            long committedOffset = entry.getValue().offset();
            long endOffset = endOffsets.get(tp).offset();
            lag.put(tp, Math.max(0, endOffset - committedOffset));
        }

        return lag;
    }

    public long getTotalLag() throws ExecutionException, InterruptedException {
        return getLagPerPartition().values().stream()
            .mapToLong(Long::longValue)
            .sum();
    }

    public void close() {
        adminClient.close();
    }
}

Beste praktijken voor het monitoren van Kafka

Anti-patroon: waarschuwing bij elke piek in vertraging

De vertraging van de consument fluctueert op natuurlijke wijze: een tijdelijke piek in de productie of een pauze van de consumentenconsumptie ze veroorzaken een tijdelijke vertraging die snel herstelt. Configureer waarschuwingen op absolute drempels zonder een tijdvenster (for: 10m) genereert voortdurend valse positieven. Houd altijd een minimumduur (5-10 minuten) aan voordat u de waarschuwing activeert.

  • Houd de trend in de gaten, niet de absolute waarde: Een afnemende vertraging van 100K is minder zorgwekkend van een groeiende vertraging van 5K. VS rate() in PromQL om de afgeleide te zien.
  • Aparte dashboards per persoon: een operationeel dashboard met kritische waarschuwingen voor SRE's, een dashboard voor capaciteitsplanning voor infrastructuurteams, een zakelijk KPI-dashboard voor productmanagers.
  • Voeg JVM-statistieken toe: 30% van de prestatieproblemen van Kafka komt voort uit GC-druk over de makelaar. Houd altijd toezicht jvm_gc_pause_seconds en heapgebruik.
  • Bewaar Prometheus minimaal 30 dagen: voor trendanalyse en maandelijkse capaciteitsplanning. Overweeg Thanos of Mimir voor langdurige retentie.
  • Test waarschuwingen met amtool: Controleer of de waarschuwingsregels syntactisch correct zijn correct zijn en dat ze worden geactiveerd met de verwachte testgegevens voordat ze in productie gaan.

Kafka in Cloud Managed: Confluente Cloud Metrics-API

Als u Confluent Cloud of een beheerde Kafka-service (AWS MSK, Aiven Kafka) gebruikt, zijn JMX-statistieken niet direct toegankelijk. In dit geval gebruiken wij de Metriek-API van de cloudaanbieder. Confluent Cloud stelt een Prometheus-compatibele Metrics REST API beschikbaar via remote_write:

# prometheus.yml per Confluent Cloud Metrics API
scrape_configs:
  - job_name: "confluent-cloud"
    honor_timestamps: true
    metrics_path: "/v2/metrics/cloud/export"
    scheme: https
    basic_auth:
      username: "<CONFLUENT_CLOUD_API_KEY>"
      password: "<CONFLUENT_CLOUD_API_SECRET>"
    static_configs:
      - targets:
          - "api.telemetry.confluent.cloud"
    params:
      resource.kafka.id:
        - "<CLUSTER_ID>"

# Per AWS MSK: usa il CloudWatch exporter
# https://github.com/prometheus/cloudwatch_exporter
scrape_configs:
  - job_name: "aws-msk"
    static_configs:
      - targets:
          - "cloudwatch-exporter:9106"

Samenvatting: Kafka-monitoringchecklist

  • JMX Exporter geconfigureerd op alle makelaars, poort 9404
  • Kafka Lag Exporter of gelijkwaardig voor statistieken over consumentengroepen
  • Prometheus met schraapinterval 15s voor makelaar, 30s voor lag-exporteur
  • Kritieke waarschuwingen: broker defect, offline partities, geen actieve controller
  • Waarschuwingswaarschuwing: te weinig gerepliceerde partities, hoge consumentenvertraging, heap> 85%
  • Grafana-dashboard geïmporteerd (ID 7589 voor overzicht, ID 13282 voor vertraging)
  • Bewaartermijn Prometheus minimaal 30 dagen
  • Alarmen getest met een minimale duur (voor: 5 m) om valse positieven te voorkomen

Volgende stappen in de serie

U hebt monitoring geconfigureerd. De volgende stap is weten wat u moet doen als er zich problemen voordoen:

  • Artikel 10 – Dead Letter-wachtrij en foutafhandeling: patronen voor het beheren van berichten die niet kunnen worden verwerkt, waaronder DLQ, exponentiële nieuwe pogingen en detectie van gifpillen.
  • Artikel 11 – Kafka in productie: complete operationele gids voor clustergrootte, de optimale configuratie van retentie- en replicatiefactoren, en MirrorMaker 2 voor noodherstel.

Link met andere series

  • Apache Kafka-grondbeginselen (artikel 1): Het concept van consumentenvertraging vereist begrip het verschil tussen LEO, HW en vastgelegde compensatie wordt uitgelegd in het eerste artikel van de serie.
  • Gebeurtenisgestuurde architectuur: Het monitoren van de consumentenvertraging is ook van cruciaal belang in EDA-systemen met AWS SQS en SNS, waarbij de equivalente maatstaf de wachtrijdiepte is.