Lời Mở Đầu: Môi Trường Local Chuẩn Production
Trên thực tế, nhiều bài tutorial hướng dẫn cài Kafka nhưng bỏ qua một vấn đề: môi trường dev phải đủ giống production để validate logic. Vì vậy, bài này sẽ setup một môi trường 3-broker Kafka KRaft cluster với đầy đủ monitoring UI — giống như bạn sẽ dùng trong doanh nghiệp thực tế.
Xem thêm trong series:
- Hiệu Năng Kafka — Zero-Copy, Page Cache và I/O (Bài trước)

1. Tổng Quan Kiến Trúc Môi Trường
Kiến trúc Docker Compose 3-broker Kafka cluster
docker-compose.yml sẽ bao gồm:
┌─────────────────────────────────────────────────────────┐
│ Docker Network │
│ │
│ kafka-1 (Broker+Controller) ← Port 9092 │
│ kafka-2 (Broker+Controller) ← Port 9093 │
│ kafka-3 (Broker+Controller) ← Port 9094 │
│ │
│ kafka-ui ← Port 8080 (Web UI để quản lý) │
└─────────────────────────────────────────────────────────┘
KRaft mode: 3 node đều là Broker VÀ Controller
Tổng RAM cần: ~4GB
2. Docker Compose — Cấu Hình Đầy Đủ 3-Broker Cluster
# docker-compose.yml
version: "3.9"
networks:
kafka-net:
driver: bridge
services:
# ─────────────────────────────────────────
# Broker 1: Port 9092
# ─────────────────────────────────────────
kafka-1:
image: bitnami/kafka:3.7.0
container_name: kafka-1
hostname: kafka-1
networks:
- kafka-net
ports:
- "9092:9092"
environment:
# KRaft Mode
KAFKA_ENABLE_KRAFT: "yes"
KAFKA_CFG_PROCESS_ROLES: "broker,controller"
# Cluster Identity (PHẢI giống nhau trên 3 node)
KAFKA_CFG_NODE_ID: "1"
KAFKA_KRAFT_CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk" # UUID cố định
# Controller Quorum: 3 node bầu chọn
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: "1@kafka-1:9091,2@kafka-2:9091,3@kafka-3:9091"
# Listeners
KAFKA_CFG_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9091"
KAFKA_CFG_ADVERTISED_LISTENERS: "PLAINTEXT://kafka-1:9092"
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT"
KAFKA_CFG_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
KAFKA_CFG_INTER_BROKER_LISTENER_NAME: "PLAINTEXT"
# Replication defaults
KAFKA_CFG_DEFAULT_REPLICATION_FACTOR: "3"
KAFKA_CFG_MIN_INSYNC_REPLICAS: "2"
KAFKA_CFG_NUM_PARTITIONS: "3"
# Performance
KAFKA_CFG_LOG_RETENTION_HOURS: "168" # 7 days
KAFKA_CFG_LOG_SEGMENT_BYTES: "1073741824" # 1GB
volumes:
- kafka-1-data:/bitnami/kafka
# ─────────────────────────────────────────
# Broker 2: Port 9093
# ─────────────────────────────────────────
kafka-2:
image: bitnami/kafka:3.7.0
container_name: kafka-2
hostname: kafka-2
networks:
- kafka-net
ports:
- "9093:9092"
environment:
KAFKA_ENABLE_KRAFT: "yes"
KAFKA_CFG_PROCESS_ROLES: "broker,controller"
KAFKA_CFG_NODE_ID: "2"
KAFKA_KRAFT_CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: "1@kafka-1:9091,2@kafka-2:9091,3@kafka-3:9091"
KAFKA_CFG_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9091"
KAFKA_CFG_ADVERTISED_LISTENERS: "PLAINTEXT://kafka-2:9092"
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT"
KAFKA_CFG_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
KAFKA_CFG_INTER_BROKER_LISTENER_NAME: "PLAINTEXT"
KAFKA_CFG_DEFAULT_REPLICATION_FACTOR: "3"
KAFKA_CFG_MIN_INSYNC_REPLICAS: "2"
KAFKA_CFG_NUM_PARTITIONS: "3"
volumes:
- kafka-2-data:/bitnami/kafka
# ─────────────────────────────────────────
# Broker 3: Port 9094
# ─────────────────────────────────────────
kafka-3:
image: bitnami/kafka:3.7.0
container_name: kafka-3
hostname: kafka-3
networks:
- kafka-net
ports:
- "9094:9092"
environment:
KAFKA_ENABLE_KRAFT: "yes"
KAFKA_CFG_PROCESS_ROLES: "broker,controller"
KAFKA_CFG_NODE_ID: "3"
KAFKA_KRAFT_CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: "1@kafka-1:9091,2@kafka-2:9091,3@kafka-3:9091"
KAFKA_CFG_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9091"
KAFKA_CFG_ADVERTISED_LISTENERS: "PLAINTEXT://kafka-3:9092"
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT"
KAFKA_CFG_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
KAFKA_CFG_INTER_BROKER_LISTENER_NAME: "PLAINTEXT"
KAFKA_CFG_DEFAULT_REPLICATION_FACTOR: "3"
KAFKA_CFG_MIN_INSYNC_REPLICAS: "2"
KAFKA_CFG_NUM_PARTITIONS: "3"
volumes:
- kafka-3-data:/bitnami/kafka
# ─────────────────────────────────────────
# Kafka UI — Web console quản lý
# ─────────────────────────────────────────
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
networks:
- kafka-net
ports:
- "8080:8080"
environment:
KAFKA_CLUSTERS_0_NAME: "local-cluster"
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: "kafka-1:9092,kafka-2:9092,kafka-3:9092"
depends_on:
- kafka-1
- kafka-2
- kafka-3
volumes:
kafka-1-data:
kafka-2-data:
kafka-3-data:
3. Khởi Động và Kiểm Tra
# Khởi động toàn bộ cluster
docker-compose up -d
# Kiểm tra trạng thái
docker-compose ps
# Xem log của broker 1
docker logs kafka-1 -f --tail=50
# Truy cập Kafka UI
open http://localhost:8080
4. Các Lệnh CLI Bắt Buộc Phải Biết
4.1 Quản Lý Topic
# Exec vào container
docker exec -it kafka-1 bash
# Tạo topic với replication và partition rõ ràng
kafka-topics.sh \
--create \
--topic order-events \
--partitions 6 \
--replication-factor 3 \
--config retention.ms=604800000 \
--config cleanup.policy=delete \
--bootstrap-server kafka-1:9092
# Xem thông tin chi tiết topic (bao gồm ISR)
kafka-topics.sh \
--describe \
--topic order-events \
--bootstrap-server kafka-1:9092
# Output:
# Topic: order-events Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3
# Topic: order-events Partition: 1 Leader: 2 Replicas: 2,3,1 Isr: 2,3,1
# ...
# List tất cả topic
kafka-topics.sh --list --bootstrap-server kafka-1:9092
# Tăng số partition (không thể giảm!)
kafka-topics.sh \
--alter \
--topic order-events \
--partitions 9 \
--bootstrap-server kafka-1:9092
4.2 Producer và Consumer CLI
# Gửi message thủ công (với key)
echo "order-123:{'orderId':'123','amount':150000}" | \
kafka-console-producer.sh \
--topic order-events \
--property "parse.key=true" \
--property "key.separator=:" \
--bootstrap-server kafka-1:9092
# Đọc từ đầu (--from-beginning)
kafka-console-consumer.sh \
--topic order-events \
--from-beginning \
--property print.key=true \
--property print.partition=true \
--property print.offset=true \
--bootstrap-server kafka-1:9092
4.3 Quản Lý Consumer Group
# List tất cả consumer group
kafka-consumer-groups.sh \
--list \
--bootstrap-server kafka-1:9092
# Xem chi tiết lag của group (CỰC QUAN TRỌNG cho monitoring)
kafka-consumer-groups.sh \
--describe \
--group order-billing-group \
--bootstrap-server kafka-1:9092
# Output:
# GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG
# order-billing-group order-events 0 1250 1250 0
# order-billing-group order-events 1 980 1100 120 ← LAG!
# order-billing-group order-events 2 1100 1100 0
# Reset offset về đầu (để reprocess toàn bộ data)
kafka-consumer-groups.sh \
--reset-offsets \
--group order-billing-group \
--topic order-events \
--to-earliest \
--execute \
--bootstrap-server kafka-1:9092
# Reset về timestamp cụ thể (reprocess từ 2h trước)
kafka-consumer-groups.sh \
--reset-offsets \
--group order-billing-group \
--topic order-events \
--to-datetime 2024-01-15T10:00:00.000 \
--execute \
--bootstrap-server kafka-1:9092
5. Performance Testing — Benchmark Cluster Của Bạn
5.1 Producer Performance Test
# Test 1: Throughput tối đa (acks=1, no compression)
kafka-producer-perf-test.sh \
--topic perf-test \
--num-records 1000000 \
--record-size 1024 \
--throughput -1 \
--producer-props \
bootstrap.servers=kafka-1:9092 \
acks=1 \
linger.ms=5 \
batch.size=65536 \
2>&1 | tail -5
# Expected output:
# 1000000 records sent, 145232.56 records/sec (141.83 MB/sec),
# 18.34 ms avg latency, 421.00 ms max latency.
# Test 2: Production config (acks=all, lz4)
kafka-producer-perf-test.sh \
--topic perf-test \
--num-records 500000 \
--record-size 1024 \
--throughput -1 \
--producer-props \
bootstrap.servers=kafka-1:9092 \
acks=all \
linger.ms=10 \
batch.size=131072 \
compression.type=lz4
5.2 Consumer Performance Test
# Test consumer throughput
kafka-consumer-perf-test.sh \
--topic perf-test \
--messages 1000000 \
--bootstrap-server kafka-1:9092 \
--group perf-consumer-group \
2>&1 | tail -3
# Output:
# start.time end.time data.consumed.in.MB MB.sec nMsg.sec
# 2024-01-15 10:00:00:000 2024-01-15 10:00:07:234 976.56 134.98 138206.01
5.3 End-to-End Latency Test
# Đo latency từ Producer đến Consumer
kafka-e2e-latency.sh \
kafka-1:9092 \
latency-test \
10000 \
3 \
1024
# Param: bootstrap-server, topic, numMessages, acks, messageSize(bytes)
# Output: Phân phối latency từ p50, p75, p95, p99, p99.9
6. Sử Dụng Kafka UI

Truy cập http://localhost:8080:
- Topics: Xem partition, offset, replication status, ISR
- Consumers: Xem lag của từng consumer group theo partition
- Brokers: Xem trạng thái từng broker, disk usage, partition assignment
- Schema Registry: Quản lý Avro/Protobuf schema (nếu cài thêm)
Kết Luận & Takeaway
Setup checklist:
✅ 3-broker cluster KRaft mode
✅ replication.factor=3, min.insync.replicas=2
✅ Kafka UI để visualize
✅ Biết các CLI command: topics, consumer-groups, perf-test
✅ Hiểu output của kafka-topics --describe (ISR)
✅ Biết reset offset để reprocess data
Xem thêm trong series:
- Hiệu Năng Kafka — Zero-Copy, Page Cache và I/O (Bài trước)
💬 Challenge: Sau khi dựng cluster, thử kill một broker (
docker stop kafka-2) và quan sát trong Kafka UI. Partition nào bị ảnh hưởng? ISR thay đổi thế nào? Leader election diễn ra trong bao lâu? Share kết quả của bạn!
