Kafka Operations and Monitoring
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
Kafka Architecture explained what a broker, controller, and partition replica are; this page is about keeping a cluster built from them healthy once it is running — the command-line tools an operator reaches for daily, what to watch on a dashboard, how to replicate data between clusters, and how a cluster actually gets deployed and run.
The command-line toolbox
Every tool below ships in Kafka’s bin/ directory (bin/kafka-.sh on Linux/macOS, bin\windows\kafka-.bat
on Windows) and, since Kafka 3.0, talks to the cluster directly over the Kafka protocol via
--bootstrap-server — none of them need direct access to the controller quorum’s storage.
| Tool | Purpose |
|---|---|
|
Create, list, describe, alter (partition count, most topic-level configs), and delete topics. |
|
List and describe consumer groups — including the per-partition lag table used below — reset committed offsets, and delete groups. |
|
Describe and dynamically alter configuration for any entity type ( |
|
Produce or consume from a terminal for ad hoc testing — reading stdin line by line, or printing records as they arrive — rather than writing a throwaway client. |
|
Describe and upgrade the cluster’s feature versions ( |
# create a topic with an explicit partition count and replication factor
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
--create --topic orders --partitions 12 --replication-factor 3
# describe one consumer group's progress against every partition it reads
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--describe --group order-processors
# GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID
# order-processors orders 0 10482 10490 8 consumer-1-6f2a... /10.0.0.5 consumer-1
# order-processors orders 1 9931 9931 0 consumer-2-9c1d... /10.0.0.6 consumer-2
# roll the cluster's feature versions forward to a target Kafka release's defaults
bin/kafka-features.sh --bootstrap-server localhost:9092 \
--release-version 4.3 upgrade
The LAG column above — log-end offset minus the group’s committed offset, per partition — is exactly what
"Consumer lag" below defines, expressed as a one-off snapshot instead of a continuously scraped metric; running
this command is the fastest way to check a specific group by hand.
How many partitions?
Kafka Architecture already covers what a partition is — the unit of ordering, parallelism, and per-consumer-group throughput; this is the operational question of how
many a topic should actually have. Three factors usually decide it: the target aggregate throughput divided by
what one partition can sustain (a few or a few dozen MB/s depending on hardware, replication factor, and
compression — benchmark rather than assume); the number of consumer instances that must be able to read the
topic in parallel within a single group, since a topic with P partitions can never keep more than P
consumers in one group busy at once (see Kafka Consumers); and
per-partition overhead on the brokers and controller — open file handles, replication traffic, and metadata
records — which is what stops "just create thousands of partitions to be safe" from being free. kafka-topics.sh
--alter --partitions can only increase a topic’s partition count, never reduce it, and increasing it reshuffles
which partition a given key hashes to for any producer using the default partitioner — so it is worth erring
generously (over-provisioning within reason) up front rather than treating the initial count as easily
reversible later.
Dynamic configuration
Most topic-, broker-, and client-level settings can be changed on a live cluster with kafka-configs.sh --alter,
without restarting a single process — the change is written to the metadata log and picked up by every broker
that needs it. This is what makes tuning retention, throttling a noisy client, or fixing a
too-low min.insync.replicas an operational action rather than a deployment.
# raise retention for a single topic
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics --entity-name orders \
--alter --add-config retention.ms=1209600000
# throttle one client's produce/consume byte rate to protect the rest of the cluster
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type clients --entity-name reporting-service \
--alter --add-config 'producer_byte_rate=1048576,consumer_byte_rate=2097152'
# inspect the effective (default + override) configuration for a topic
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics --entity-name orders --describe
Not every setting is dynamic — some, such as a broker’s listener list or log directories, are read once at
startup — but the common levers operators reach for repeatedly (retention, compaction thresholds,
min.insync.replicas, per-client quotas) all are.
Key broker metrics to watch
Kafka exposes its internal state as JMX MBeans; whatever metrics pipeline scrapes them (Prometheus’s JMX exporter, Datadog, CloudWatch agent, and so on), these are the ones worth an alert rather than just a dashboard panel.
| Metric (JMX MBean) | What it means |
|---|---|
|
Count of partitions whose in-sync replica set is currently smaller than their full replica set. Zero is the
healthy state; any sustained non-zero value means followers are falling behind or a broker is down — see
Kafka Reliability and Exactly-Once for what a
shrunk ISR does to |
|
Each broker reports |
|
End-to-end request latency, broken down by companion metrics
( |
Consumer lag
Lag is a partition’s log-end offset minus a consumer group’s last committed offset on that partition — how
many records the group still has to catch up on. It is the single most useful number for answering "is this
consumer keeping up," independent of whether the consumer process looks healthy: a consumer can be alive,
polling, and heartbeating normally while still falling further behind every minute if it can’t process records
as fast as they arrive. kafka-consumer-groups.sh --describe (shown above) reports it as a point-in-time
snapshot; the same figure is available continuously per consumer instance as the records-lag-max client metric
(kafka.consumer:type=consumer-fetch-manager-metrics,client-id=…), which is what an alerting pipeline should
scrape rather than polling the CLI tool on a cron.
Quotas
Quotas cap how much of a broker’s resources one client is allowed to consume, so a single noisy or misbehaving
client can’t starve every other client sharing the cluster. Kafka enforces three kinds, all set the same way as
any other dynamic configuration via kafka-configs.sh --entity-type clients|users --alter --add-config: a
produce byte-rate quota (producer_byte_rate), a consume byte-rate quota (consumer_byte_rate), and a
request quota (request_percentage) that limits the fraction of a broker’s request-handling threads a
client may occupy, independent of the bytes it moves. Quotas can be scoped to a client-id, a user (once
authentication is in place — see Kafka Security), or the
combination of both, and a default can be set at any of those levels with --entity-default so that new clients
get a sane cap automatically instead of running unthrottled until someone notices.
Geo-replication with MirrorMaker 2
MirrorMaker 2 replicates topics, consumer group offsets, topic configuration, and ACLs from a source
cluster to a target cluster, running as a set of connectors
(MirrorSourceConnector, MirrorCheckpointConnector, MirrorHeartbeatConnector) on top of
Kafka Connect's distributed-mode worker framework rather than as a
bespoke replication process. The original MirrorMaker (kafka-mirror-maker.sh, sometimes called "MirrorMaker
1"), a much simpler tool that only copied records and predates this connector-based architecture, was deprecated
in Kafka 3.0 under
KIP-720 and removed
entirely in Kafka 4.0 — MirrorMaker 2 is the only mirroring tool Kafka itself ships today.
A replication flow is configured as <source-cluster>→<target-cluster>.enabled=true, and which topics it
covers is a regex allowlist (topics). Two topologies cover most needs: active/passive, a single one-way
flow used for disaster recovery, where the target cluster is a standby that is promoted only on failover; and
active/active, a pair of flows running in both directions so that either cluster can serve local reads and
writes with the other kept in sync — typically for geographic locality (clients in each region talking to
their nearest cluster). Active/active needs a way to stop a record from bouncing back and forth forever between
the two flows: MirrorMaker 2’s default DefaultReplicationPolicy renames a topic as it crosses — a topic named
orders on cluster us-west arrives on us-east as us-west.orders — so a flow never re-mirrors a topic that
already carries the other cluster’s prefix.
MirrorCheckpointConnector additionally replicates consumer group offsets (translated between the two
clusters' offset spaces) so a consumer failing over from one cluster to the other can resume close to where it
left off, and MirrorHeartbeatConnector writes a periodic heartbeat topic used to monitor whether a flow is
actually making progress. See
Apache Kafka Documentation — Geo-Replication (Cross-Cluster Data Mirroring) for the full connector configuration reference, offset
translation details, and fan-out/aggregation topologies beyond the two covered here.
Tiered storage operations
Kafka Architecture introduced tiered storage’s local/remote
split conceptually; operating it is two separate switches. The cluster-wide
remote.log.storage.system.enable flag is a broker configuration (set in server.properties, taking effect
on that broker’s next restart, not a live kafka-configs.sh change) that turns the feature on for the cluster;
enabling it per topic is then an ordinary dynamic change:
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics --entity-name orders \
--alter --add-config remote.storage.enable=true
# how much of the local tier a broker keeps before offloading a segment to the remote tier
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics --entity-name orders \
--alter --add-config local.retention.ms=86400000
Once enabled, reads for offsets still in the local tier are served exactly as before; reads for older offsets
are fetched transparently from the remote tier through the pluggable RemoteStorageManager — a
kafka-console-consumer.sh --from-beginning against a long-retention topic is a simple way to confirm both
tiers are being read correctly end to end. See
Apache Kafka Documentation — Tiered Storage for the
full configuration reference, including how disabling tiered storage on a topic that already has remote data is
handled.
Deployment options
Kafka’s own documentation walks running a broker via the official images: apache/kafka (JVM-based, published
from Kafka 3.7 on) and the experimental, GraalVM-native apache/kafka-native (from 3.8 on, explicitly called
out as suitable for local development and testing only, not production) — Getting Started with Messaging already uses the former for the
single-node docker run quickstart; see
Apache Kafka Documentation — Docker for both images and
the multi-broker Docker Compose examples in Kafka’s own repository it links to.
Running and upgrading a cluster of brokers by hand — coordinating rolling restarts, certificate rotation,
storage, and scaling — is a substantial operational burden most teams don’t take on directly. Strimzi is the
most common answer for running Kafka on Kubernetes: an operator that manages brokers, controllers, topics, users,
and MirrorMaker 2 flows as Kubernetes custom resources (Kafka, KafkaTopic, KafkaUser,
KafkaMirrorMaker2), reconciling the running cluster to match them the way any Kubernetes operator does. Fully
managed services — Confluent Cloud, Amazon MSK, and similar offerings from other cloud providers — go a step
further and remove broker operation from the team entirely, at the cost of losing direct control over broker
configuration and upgrade timing. Which of the three (self-managed, Strimzi-on-Kubernetes, fully managed) fits
best is mostly a question of how much operational capacity a team already has and how much control over broker
internals it actually needs — all three run the same Kafka protocol described in
Protocols, so client code does not change between them.