Bài 7: Setup Kafka Cluster — Docker Compose và KRaft

4 min read

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:


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:

💬 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!

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *