zalo-icon
facebook-icon
phone-icon
Cài đặt Apache Kafka 4 bằng Docker và KRaft

Đơn vị biên soạn: TechData.AI
Chủ đề: Data Engineering và Event Streaming
Đối tượng: Người mới học Kafka, Data Engineer và Backend Developer
Phiên bản tham chiếu: Apache Kafka 4.x

MỤC LỤC

  1. Mục tiêu bài học
  2. Môi trường thực hành
  3. KRaft thay đổi cách cài Kafka như thế nào
  4. Các chế độ triển khai KRaft
  5. Xây dựng Kafka bằng Docker Compose
  6. Khởi động và kiểm tra Cluster
  7. Tạo Topic đầu tiên
  8. Gửi và đọc Event bằng Kafka CLI
  9. Kiểm tra Partition và Offset
  10. Bổ sung Kafka UI
  11. Hiểu listeners và advertised.listeners
  12. Lưu dữ liệu bền vững bằng Volume
  13. Các lỗi thường gặp
  14. Khi nào cần Cluster nhiều Broker
  15. Bài thực hành cuối bài
  16. Tổng kết
  17. Câu hỏi ôn tập
  18. Tài liệu tham khảo

1. Mục tiêu bài học

Trong bài trước, chúng ta đã tìm hiểu Apache Kafka là gì, vì sao Kafka phù hợp với kiến trúc Event Streaming và vai trò của Producer, Consumer, Broker, Topic, Partition cùng Offset. Bài này chuyển từ lý thuyết sang thực hành bằng cách xây dựng một môi trường Kafka hoàn chỉnh trên máy cá nhân.

Sau khi hoàn thành, học viên có thể tự cài Kafka bằng Docker, khởi động Cluster sử dụng KRaft, tạo Topic, gửi Event, đọc Event và quan sát dữ liệu bằng giao diện web. Quan trọng hơn, học viên sẽ hiểu ý nghĩa của những cấu hình thường gây lỗi như listeners, advertised.listeners, process.rolescontroller.quorum.voters.

Môi trường trong bài được thiết kế cho học tập và phát triển phần mềm. Nó chưa phải kiến trúc Production. Ở cuối bài, chúng ta sẽ phân biệt rõ những thành phần nào có thể giữ lại và những thành phần nào cần thay đổi khi triển khai trong doanh nghiệp.

2. Môi trường thực hành

Infographic 2 môi trường thực hành - @techdata.ai

Học viên cần chuẩn bị:

  • Máy tính có tối thiểu 8 GB RAM.
  • Docker Desktop hoặc Docker Engine.
  • Docker Compose phiên bản mới.
  • Terminal trên macOS, Linux hoặc Windows PowerShell.
  • Trình soạn thảo như Visual Studio Code.
  • Khoảng 5 GB dung lượng trống.

Kiểm tra Docker:

docker --version
docker compose version

Nếu hai lệnh trả về phiên bản hợp lệ, môi trường đã sẵn sàng. Kafka 4.x yêu cầu Java 17 trở lên khi chạy trực tiếp từ bộ cài, nhưng trong bài này Java đã nằm trong Docker Image nên máy cá nhân không cần cài Java riêng.

Tạo cấu trúc thư mục:

kafka-lab/
  docker-compose.yml
  data/
  scripts/
  producer/
  consumer/

Thư mục data sẽ được dùng để lưu dữ liệu Kafka. Các thư mục producerconsumer được sử dụng trong những bài tiếp theo.

3. KRaft thay đổi cách cài Kafka như thế nào

Infographic 3 kraft thay đổi cách cài kafka như thế nào - @techdata.ai

Các hướng dẫn Kafka cũ thường yêu cầu khởi động ZooKeeper trước, sau đó mới khởi động Kafka Broker. Cách triển khai này không còn phù hợp với Kafka 4.

KRaft là cơ chế quản lý metadata được tích hợp trong Kafka. Metadata của Cluster gồm danh sách Broker, Topic, Partition, Replica, cấu hình và trạng thái bầu chọn Leader. Thay vì lưu metadata trong ZooKeeper, KRaft sử dụng một log đồng thuận do nhóm Controller quản lý.

Một Kafka Node có thể mang vai trò broker, controller hoặc đồng thời cả hai. Môi trường học tập thường dùng Combined Mode vì chỉ cần một container. Production nên cân nhắc tách Controller khỏi Broker để cô lập tài nguyên và giảm ảnh hưởng khi Broker xử lý tải lớn.

KRaft sử dụng Cluster ID để nhận diện Cluster. Log directory phải được format và gắn với Cluster ID trước khi Broker hoạt động. Docker Image chính thức có thể tự động hóa bước này thông qua biến môi trường, nhưng Data Engineer vẫn cần hiểu bản chất để xử lý sự cố.

4. Các chế độ triển khai KRaft

Infographic 4 các chế độ triển khai kraft - @techdata.ai

4.1 Combined Mode

Một Node vừa làm Broker vừa làm Controller:

process.roles=broker,controller

Ưu điểm là cấu hình đơn giản, ít tài nguyên và phù hợp với máy cá nhân. Nhược điểm là dữ liệu, request của Client và công việc quản lý metadata cùng chia sẻ một tiến trình.

4.2 Separated Mode

Controller và Broker chạy trên các Node riêng. Controller Quorum thường có ba hoặc năm Node để chịu lỗi. Broker chỉ tập trung lưu trữ và phục vụ Event.

Đây là lựa chọn phù hợp hơn cho Production, đặc biệt khi Cluster có nhiều Topic, Partition hoặc tải lớn. Tuy nhiên, môi trường thực hành không cần độ phức tạp này ngay từ đầu.

5. Xây dựng Kafka bằng Docker Compose

Infographic 5 xây dựng kafka bằng docker compose - @techdata.ai

Tạo file docker-compose.yml với nội dung sau:

services:
  kafka:
    image: apache/kafka:4.3.1
    container_name: kafka
    hostname: kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_LISTENERS: CONTROLLER://:9093,PLAINTEXT://:29092,EXTERNAL://:9092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,EXTERNAL://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_NUM_PARTITIONS: 3
    volumes:
      - kafka_data:/var/lib/kafka/data

volumes:
  kafka_data:

Cấu hình này công bố hai địa chỉ kết nối. Các ứng dụng chạy trong cùng Docker Network sử dụng kafka:29092. Ứng dụng chạy trên máy cá nhân sử dụng localhost:9092.

KAFKA_NODE_ID là định danh duy nhất của Node. KAFKA_PROCESS_ROLES xác định Node vừa làm Broker vừa làm Controller. KAFKA_CONTROLLER_QUORUM_VOTERS cho biết Controller có Node ID 1 và lắng nghe tại cổng 9093.

Các cấu hình Replication Factor được đặt bằng 1 vì môi trường chỉ có một Broker. Tuyệt đối không sao chép nguyên giá trị này sang Production nếu dữ liệu cần khả năng chịu lỗi.

6. Khởi động và kiểm tra Cluster

Infographic 6 khởi động và kiểm tra cluster - @techdata.ai

Khởi động dịch vụ:

docker compose up -d

Kiểm tra container:

docker compose ps

Theo dõi log:

docker compose logs -f kafka

Kafka cần một khoảng thời gian ngắn để format storage, khởi động Controller và đăng ký Broker. Khi log cho thấy Broker đã chuyển sang trạng thái hoạt động, Cluster sẵn sàng nhận kết nối.

Kiểm tra API version:

docker exec kafka /opt/kafka/bin/kafka-broker-api-versions.sh \
  --bootstrap-server localhost:9092

bootstrap-server không có nghĩa Client chỉ giao tiếp với một Broker. Đây là địa chỉ ban đầu để Client lấy metadata. Sau đó, Client sẽ kết nối trực tiếp với Broker phù hợp cho từng Partition.

7. Tạo Topic đầu tiên

Infographic 7 tạo topic đầu tiên - @techdata.ai

Tạo Topic order-created với ba Partition:

docker exec kafka /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --create \
  --topic order-created \
  --partitions 3 \
  --replication-factor 1

Liệt kê Topic:

docker exec kafka /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --list

Xem chi tiết Topic:

docker exec kafka /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --describe \
  --topic order-created

Kết quả cho biết Topic có ba Partition. Vì Cluster chỉ có một Broker, Leader và Replica đều nằm trên Node 1. Trong Cluster nhiều Broker, các Partition và Replica được phân phối trên nhiều Node.

Không nên phụ thuộc hoàn toàn vào cơ chế tự động tạo Topic. Production nên tạo Topic bằng quy trình kiểm soát, Infrastructure as Code hoặc công cụ quản trị để bảo đảm đúng Partition, Replication Factor và retention.

8. Gửi và đọc Event bằng Kafka CLI

Infographic 8 gửi và đọc event bằng kafka cli - @techdata.ai

Mở Producer Console:

docker exec -it kafka /opt/kafka/bin/kafka-console-producer.sh \
  --bootstrap-server localhost:9092 \
  --topic order-created

Nhập từng Event JSON:

{"order_id":"ORD-10001","customer_id":"CUS-001","total_amount":1250000,"status":"created"}
{"order_id":"ORD-10002","customer_id":"CUS-002","total_amount":890000,"status":"created"}
{"order_id":"ORD-10003","customer_id":"CUS-001","total_amount":2150000,"status":"created"}

Mỗi dòng trở thành một record. Dừng Producer bằng Ctrl+C.

Mở Consumer Console:

docker exec -it kafka /opt/kafka/bin/kafka-console-consumer.sh \
  --bootstrap-server localhost:9092 \
  --topic order-created \
  --from-beginning

Tùy chọn --from-beginning yêu cầu Consumer đọc từ Offset sớm nhất còn được giữ lại. Nếu bỏ tùy chọn này, Console Consumer thường chỉ hiển thị Event xuất hiện sau khi Consumer bắt đầu.

Kafka không xóa Event chỉ vì Console Consumer đã đọc. Chạy lại lệnh với --from-beginning, chúng ta vẫn có thể xem những Event cũ.

9. Kiểm tra Partition và Offset

Infographic 9 kiểm tra partition và offset - @techdata.ai

Để quan sát metadata của từng record, chạy:

docker exec -it kafka /opt/kafka/bin/kafka-console-consumer.sh \
  --bootstrap-server localhost:9092 \
  --topic order-created \
  --from-beginning \
  --property print.partition=true \
  --property print.offset=true \
  --property print.timestamp=true

Vì các Event trước chưa có Key, chúng có thể được phân phối theo cơ chế mặc định của Producer Console. Để gửi Key và Value, sử dụng:

docker exec -it kafka /opt/kafka/bin/kafka-console-producer.sh \
  --bootstrap-server localhost:9092 \
  --topic order-created \
  --property parse.key=true \
  --property key.separator=:

Nhập dữ liệu:

ORD-10001:{"order_id":"ORD-10001","status":"paid"}
ORD-10001:{"order_id":"ORD-10001","status":"packed"}
ORD-10002:{"order_id":"ORD-10002","status":"paid"}

Hai Event của ORD-10001 có cùng Key nên được đưa vào cùng một Partition. Đây là nền tảng để giữ thứ tự sự kiện của một đơn hàng.

10. Bổ sung Kafka UI

Infographic 10 bổ sung kafka ui - @techdata.ai

CLI phù hợp để kiểm tra nhanh nhưng không thuận tiện khi cần quan sát nhiều Topic và Consumer Group. Có thể bổ sung Kafka UI vào Docker Compose:

  kafka-ui:
    image: provectuslabs/kafka-ui:latest
    container_name: kafka-ui
    depends_on:
      - kafka
    ports:
      - "8080:8080"
    environment:
      KAFKA_CLUSTERS_0_NAME: local-kafka
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092

Khởi động lại:

docker compose up -d

Mở http://localhost:8080. Giao diện cho phép xem Broker, Topic, Partition, Message và Consumer Group.

Kafka UI là công cụ bên ngoài Apache Kafka. Production cần khóa truy cập, bật xác thực và không công khai giao diện quản trị ra Internet.

11. Hiểu listeners và advertised.listeners

Infographic 11 hiểu listeners và advertisedlisteners - @techdata.ai

Đây là phần gây nhiều lỗi nhất khi cài Kafka bằng Docker.

listeners xác định địa chỉ và cổng mà Kafka thực sự mở để nhận kết nối. advertised.listeners xác định địa chỉ Kafka công bố cho Client sau bước bootstrap.

Client có thể kết nối thành công đến localhost:9092 ở bước đầu nhưng sau đó vẫn lỗi nếu Broker trả về địa chỉ kafka:29092 mà máy cá nhân không phân giải được. Ngược lại, container khác không thể dùng localhost:9092localhost bên trong container chỉ chính container đó.

Vì vậy, môi trường Docker thường cần hai listener:

  • Listener nội bộ cho container trong Docker Network.
  • Listener bên ngoài cho ứng dụng chạy trên máy cá nhân.

Khi triển khai trên máy chủ hoặc Kubernetes, địa chỉ advertised phải là DNS hoặc IP mà Client thực sự truy cập được. Không nên sửa ngẫu nhiên cổng cho đến khi chạy được. Cần xác định rõ Client đang ở mạng nào và Broker phải công bố địa chỉ nào.

12. Lưu dữ liệu bền vững bằng Volume

Infographic 12 lưu dữ liệu bền vững bằng volume - @techdata.ai

Kafka lưu Event trên ổ đĩa. Nếu container bị xóa mà không có Volume, dữ liệu có thể mất theo container.

Named Volume trong Docker Compose giúp dữ liệu tồn tại qua những lần khởi động lại:

volumes:
  - kafka_data:/var/lib/kafka/data

Dừng container nhưng giữ dữ liệu:

docker compose down

Khởi động lại:

docker compose up -d

Topic và Event vẫn còn trong Volume.

Lệnh sau xóa cả container và Volume:

docker compose down -v

Chỉ dùng khi chắc chắn muốn xóa toàn bộ dữ liệu thực hành. Trong Production, Volume không thay thế Replication, backup hoặc Disaster Recovery.

13. Các lỗi thường gặp

Infographic 13 các lỗi thường gặp - @techdata.ai

Kafka khởi động rồi dừng

Kiểm tra log bằng docker compose logs kafka. Nguyên nhân thường là biến môi trường sai, cổng bị chiếm, quyền ghi Volume hoặc metadata cũ không tương thích với Cluster ID.

Client kết nối bootstrap thành công nhưng không gửi được dữ liệu

Kiểm tra advertised.listeners. Broker có thể đang trả về hostname mà Client không truy cập được.

Topic yêu cầu Replication Factor lớn hơn số Broker

Một Cluster một Broker không thể tạo Topic có Replication Factor bằng 3. Môi trường học tập dùng giá trị 1. Production nên có ít nhất ba Broker nếu Topic quan trọng cần ba Replica.

Kafka UI không thấy Cluster

Kafka UI chạy trong Docker Network nên phải dùng kafka:29092, không dùng localhost:9092.

Port 9092 đã được sử dụng

Kiểm tra ứng dụng khác đang giữ cổng hoặc đổi cổng bên ngoài, chẳng hạn 19092:9092, đồng thời cập nhật EXTERNAL advertised listener.

Consumer không thấy Event cũ

Thêm --from-beginning hoặc sử dụng một Consumer Group mới. Consumer Group đã commit Offset sẽ tiếp tục từ vị trí đã lưu.

14. Khi nào cần Cluster nhiều Broker

Infographic 14 khi nào cần cluster nhiều broker - @techdata.ai

Cluster một Broker phù hợp để học, phát triển và chạy kiểm thử cục bộ. Nó không có khả năng chịu lỗi vì tất cả Partition chỉ tồn tại trên một Node.

Doanh nghiệp nên cân nhắc nhiều Broker khi:

  • Dữ liệu không được phép mất khi một máy chủ hỏng.
  • Throughput vượt khả năng của một Broker.
  • Cần bảo trì mà không dừng toàn hệ thống.
  • Cần phân phối Partition và Replica giữa nhiều máy.
  • Có yêu cầu SLA hoặc Disaster Recovery.

Cluster ba Broker là điểm khởi đầu phổ biến, không phải quy tắc tuyệt đối. Kích thước Cluster phải dựa trên throughput, retention, Replication Factor, tốc độ ổ đĩa, băng thông mạng và mục tiêu phục hồi.

15. Bài thực hành cuối bài

Infographic 15 bài thực hành cuối bài - @techdata.ai

Hãy hoàn thành các yêu cầu sau:

  1. Khởi động Kafka và Kafka UI bằng Docker Compose.
  2. Tạo Topic payment-events có ba Partition.
  3. Gửi năm Event thanh toán dạng JSON.
  4. Dùng payment_id làm Key.
  5. Đọc Event và hiển thị Key, Partition, Offset, Timestamp.
  6. Dừng toàn bộ container nhưng giữ Volume.
  7. Khởi động lại và chứng minh dữ liệu vẫn tồn tại.
  8. Mở Kafka UI và xác nhận số Partition của Topic.

Học viên nên ghi lại kết quả và giải thích vì sao các Event cùng Key nằm trong cùng một Partition.

16. Tổng kết

Trong bài này, chúng ta đã xây dựng một môi trường Apache Kafka 4 sử dụng Docker và KRaft. Kafka Cluster có một Node đảm nhiệm đồng thời vai trò Broker và Controller, phù hợp cho học tập nhưng chưa phù hợp với Production.

Các điểm cần ghi nhớ:

  • Kafka 4 sử dụng KRaft và không cần ZooKeeper.
  • listeners là địa chỉ Kafka mở để lắng nghe.
  • advertised.listeners là địa chỉ Kafka công bố cho Client.
  • Client trong Docker và Client trên máy cá nhân cần địa chỉ khác nhau.
  • Topic được chia thành Partition.
  • Record trong mỗi Partition có Offset riêng.
  • Docker Volume giúp giữ dữ liệu khi container khởi động lại.
  • Cluster một Broker không có khả năng chịu lỗi.

Bài tiếp theo sẽ sử dụng Python để xây dựng Kafka Producer, gửi Event JSON, lựa chọn Message Key và tối ưu độ tin cậy khi truyền dữ liệu.

17. Câu hỏi ôn tập

  1. KRaft thay thế thành phần nào trong kiến trúc Kafka cũ?
  2. Broker và Controller khác nhau như thế nào?
  3. Vì sao môi trường Docker cần listener nội bộ và bên ngoài?
  4. bootstrap-server có phải Broker duy nhất Client sử dụng không?
  5. Vì sao Cluster một Broker chỉ dùng Replication Factor bằng 1?
  6. Docker Volume giải quyết vấn đề gì?
  7. --from-beginning ảnh hưởng đến Console Consumer như thế nào?
  8. Vì sao các Event cùng Key thường nằm trong cùng Partition?

18. Tài liệu tham khảo

  1. Apache Kafka Quickstart: https://kafka.apache.org/quickstart/
  2. Apache Kafka Documentation: https://kafka.apache.org/documentation/
  3. Apache Kafka Docker Image Usage Guide: https://github.com/apache/kafka/blob/trunk/docker/examples/README.md
  4. Apache Kafka KRaft Configuration: https://kafka.apache.org/documentation/#kraft
  5. Apache Kafka Topic Operations: https://kafka.apache.org/documentation/#basic_ops_add_topic
Scroll to Top