Proč je Kafka Monitoring kritický

Apache Kafka je navržen tak, aby byl odolný, ale odolnost není automatická: vyžaduje aktivní pozorovatelnost. Přetížený broker, skupina spotřebitelů, která nestíhá držet krok s výrobou, nebo oddíl s degradovanou replikací pokud nejsou včas odhaleny, mohou se změnit ve ztrátu dat nebo výpadek.

Tři základní dimenze, které je třeba v Kafkovi sledovat, jsou:

  • Zdraví klastru: aktivní zprostředkovatelé, aktivní řadič, nedostatečně replikované oddíly, offline oddíly
  • Výkon výrobce: četnost požadavků, četnost chyb záznamu, latence požadavku, velikost dávky
  • Výkon spotřebitele: zpoždění spotřebitele na skupinu a na oddíl, četnost odevzdání, frekvence vyvažování

5 nejkritičtějších Kafkových metrik

  • kafka.consumer.lag: rozdíl mezi LEO a potvrzeným offsetem – metrika č. 1 pro stav systému
  • kafka.server.UnderReplicatedPartitions: oddíly s neúplným ISR — degradovaný signál zprostředkovatele
  • kafka.server.OfflinePartitionsCount: oddíly bez vedoucích pozic — kritická chyba, data nedostupná
  • kafka.network.RequestMetrics.TotalTimeMs: celková latence požadavků
  • kafka.server.BrokerTopicMetrics.MessagesInPerSec: propustnost příjmu

Jak Kafka odhaluje metriky: JMX

Kafka odhaluje všechny své interní metriky prostřednictvím JMX (Java Management Extensions), standardní rámec pro monitorování aplikací JVM. Každá metrika je označena a MBean jméno ve formátu dominio:tipo=Tipo,nome=Metrica.

Chcete-li zpřístupnit metriky JMX pro Prometheus, nejpoužívanější monitorovací systém v cloudovém nativním prostředí, používáte Exportér JMX: Java agent, který odhaluje metriky JMX jako koncový bod HTTP ve formátu Prometheus (textový, pull model).

Nakonfigurujte JMX Exporter jako Java Agent

JMX Exporter se spouští jako agent JVM: stačí přidat parametr -javaagent na Kafka broker JVM. Stáhnete si JAR z oficiálního úložiště Prometheus a vytvoříte konfigurační soubor YAML, který specifikuje které MBeans sbírat a jak je přejmenovat na metriky Prometheus.

# 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

Povolte exportér JMX v Brokeru

# 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

Spotřebitelské zpoždění: Nejdůležitější metrika

Il spotřebitelské zpoždění měří, kolik záznamů musí spotřebitel ještě zpracovat: je to rozdíl mezi a Log End Offset (poslední záznam zapsaný v tématu) a Committed Offset (poslední záznam zpracovaný skupinou spotřebitelů). Rostoucí zpoždění naznačuje, že spotřebitelé nedrží krok s rychlostí výroby.

Samotné zpoždění neodhalí metriky JMX makléřů: musí být shromážděny dotazem na koordinátora skupiny spotřebitelů. Standardní nástroj k tomu je Exportér Kafka Lag (projekt Lightbend/open source) nebo plugin kafka_consumer_group samotného Prométhea.

# 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
}

Vypočítejte zpoždění pomocí 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

Nakonfigurujte Prometheus pro Kafku

Prometheus používá šablonu SEM: je to Prometheus, kdo shromažďuje metriky z koncových bodů vystavené vývozci podle konfigurovatelného intervalu (scrape_interval). Konfigurace specifikuje škrábat cíl: Koncové body HTTP pro dotaz.

# 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]

Pravidla výstrah Kafka: Základní pravidla

Pravidla výstrah definují podmínky, které spouštějí oznámení. V Prometheovi se vyjadřují s PromQL a jsou spojeny do samostatných souborů YAML. Zde jsou základní pravidla pro Kafku ve výrobě:

# 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 pro Kafku

Grafana je standardní vizualizační nástroj pro Prometheus. Existují řídicí panely připravené k importu přímo z Grafana Dashboard Hub (grafana.com/dashboards). Nejpoužívanější pro Kafku jsou:

  • ID panelu 7589: Přehled Kafka (zdraví zprostředkovatele, propustnost, oddíly)
  • ID panelu 9021: Confluent Platform Kafka Metrics
  • ID panelu 13282: Kafka Consumer Lag (podrobné zpoždění skupiny spotřebitelů)

Chcete-li importovat řídicí panel do Grafany: Řídicí panely → Import → zadejte ID → vyberte zdroj dat Prometheus.

Klávesové panely, které budete mít na svém řídicím panelu

# 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: Kompletní sledování zásobníku

Pro vývojová nebo pracovní prostředí tento Docker Compose spouští celý zásobník monitorování: JMX Exporter (integrovaný do brokera), Kafka Lag Exporter, Prometheus a 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:

Monitorování spotřebitelských zpoždění pomocí Java (programové)

V některých případech je užitečné měřit zpoždění spotřebitele přímo v kódu aplikace, například k vystavení metriky pomocí mikrometru/akčního členu v aplikaci Spring Boot. Tento přístup doplňuje externí monitorování a umožňuje přesnější korelace.

// 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();
    }
}

Nejlepší postupy pro monitorování Kafky

Anti-Pattern: Alert on Every Spike of Lag

Zpoždění spotřebitele přirozeně kolísá: momentální vrchol ve výrobě nebo pauza spotřebitele GC způsobují dočasné zpoždění, které se rychle obnoví. Nakonfigurujte upozornění na absolutní prahové hodnoty bez časové okno (for: 10m) generuje neustále falešně pozitivní výsledky. Před spuštěním výstrahy vždy používejte minimální dobu trvání (5-10 minut).

  • Sledujte trend, ne absolutní hodnotu: Klesající zpoždění o 100 000 je méně znepokojivé rostoucího zpoždění 5K. USA rate() v PromQL vidět derivát.
  • Oddělte panely podle osob: provozní panel s kritickými výstrahami pro SRE, řídicí panel plánování kapacity pro týmy infrastruktury, řídicí panel obchodních klíčových ukazatelů výkonu pro produktové manažery.
  • Zahrnout metriky JVM: 30 % problémů s výkonem Kafky pochází z tlaku GC o makléře. Vždy sledujte jvm_gc_pause_seconds a využití haldy.
  • Uchovávejte Prometheus alespoň 30 dní: pro analýzu trendů a měsíční plánování kapacity. Zvažte Thanose nebo Mimira pro dlouhodobé uchování.
  • Testovat upozornění pomocí amtool: Ověřte, že pravidla výstrah jsou syntakticky správná správné a že se spouštějí s očekávanými testovacími daty před uvedením do výroby.

Kafka v Cloud Managed: Confluent Cloud Metrics API

Pokud používáte Confluent Cloud nebo spravovanou službu Kafka (AWS MSK, Aiven Kafka), metriky JMX nejsou přímo přístupný. V tomto případě použijeme Metrics API poskytovatele cloudu. Confluent Cloud zpřístupňuje Metrics REST API kompatibilní s Prometheus prostřednictvím 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"

Shrnutí: Kafka Monitoring Checklist

  • JMX Exporter nakonfigurován na všech brokerech, port 9404
  • Kafka Lag Exporter nebo ekvivalent pro metriky skupiny spotřebitelů
  • Prometheus s intervalem stírání 15s pro brokera, 30s pro lag exportéra
  • Kritická upozornění: výpadek zprostředkovatele, offline oddíly, žádný aktivní řadič
  • Upozornění: nedostatečně replikované oddíly, velké zpoždění spotřebitele, halda > 85 %
  • Importován řídicí panel Grafana (ID 7589 pro přehled, ID 13282 pro zpoždění)
  • Uchovávání Prometheus minimálně 30 dní
  • Upozornění testována s minimální dobou trvání (po dobu: 5 m), aby se zabránilo falešně pozitivním výsledkům

Další kroky v sérii

Nastavili jste sledování. Dalším krokem je vědět, co dělat, když nastanou problémy:

  • Článek 10 – Fronta nedoručených zpráv a zpracování chyb: vzory pro správu zpráv které se nepodaří zpracovat, včetně DLQ, exponenciálního opakování a detekce jedových pilulek.
  • Článek 11 – Kafka ve výrobě: kompletní provozní příručka pro dimenzování clusteru, optimální konfiguraci faktorů uchovávání a replikace a MirrorMaker 2 pro obnovu po havárii.

Propojení s ostatními sériemi

  • Apache Kafka Fundamentals (článek 1): Koncept spotřebitelského zpoždění vyžaduje pochopení rozdíl mezi LEO, HW a potvrzeným offsetem vysvětlený v prvním článku série.
  • Event-Driven Architecture: Sledování zpoždění spotřebitelů je také zásadní v systémech EDA s AWS SQS a SNS, kde ekvivalentní metrikou je hloubka fronty.