目 录CONTENT

文章目录

Kafka-KRaft单节点Docker部署避坑指南

过客
2026-05-22 / 0 评论 / 1 点赞 / 3 阅读 / 0 字

10多年前的游戏后端消息队列从 Kafka 0.x 用到 3.x,之前都是使用ZooKeeper集群模式部署的。这次测 Kafka 3.8.0 KRaft 模式踩了几个坑,单独开一篇记录。

一、为什么用 KRaft

之前 Kafka 依赖 ZooKeeper 做元数据管理,部署麻烦还要额外部署 ZK 集群。KRaft 模式下 Kafka 自己搞定选主,不再需要 ZooKeeper,Docker 一把梭就行。

单节点够用是因为这次只是​测试环境验证​,生产游戏服日均千万级消息还是要上多节点集群。

二、Docker Compose 部署

version: '3.8'
services:
  kafka:
    image: apache/kafka:3.8.0
    container_name: kafka
    network_mode: host
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://<服务器IP>:9092
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@0.0.0.0:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_LOG_DIRS: /opt/kafka/logs
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false"
    volumes:
      - kafka-data:/opt/kafka/logs

volumes:
  kafka-data:

关键参数说明​:

参数 作用
KAFKA_PROCESS_ROLES broker,controller 单节点同时担任 broker 和 controller
KAFKA_ADVERTISED_LISTENERS <服务器IP>:9092 这里踩坑了,详见第四节
KAFKA_AUTO_CREATE_TOPICS_ENABLE false 禁止自动创建 topic,避免乱序问题
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR 1 单节点设为 1

启动:

docker-compose up -d

# 等待 60 秒让 KRaft 选主完成(首次启动需要 generate UUID)
docker exec kafka bash -c "sleep 60 && echo 'KRaft ready'"

三、初始化 Topic

先说清楚这次的业务需求:

  • business:业务日志,12 分区,7 天保留
  • system:系统日志,6 分区,7 天保留
  • 日均消息量:千万级
cat > init-topics.sh << 'EOF'
#!/bin/bash

TOPIC_BUSINESS="business"
TOPIC_SYSTEM="system"
PARTITIONS_BUSINESS=12
PARTITIONS_SYSTEM=6
RETENTION_MS=604800000  # 7天

KAFKA_BIN="/opt/kafka/bin"
CONTAINER_NAME="kafka"

# 手动创建 __consumer_offsets(KRaft 单节点不自动创建)
docker exec ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-topics.sh \
  --create \
  --topic __consumer_offsets \
  --partitions 50 \
  --config cleanup.policy=compact \
  --if-not-exists

# 创建业务 topic
docker exec ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-topics.sh \
  --create \
  --topic ${TOPIC_BUSINESS} \
  --partitions ${PARTITIONS_BUSINESS} \
  --replication-factor 1 \
  --config retention.ms=${RETENTION_MS}

# 创建系统 topic
docker exec ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-topics.sh \
  --create \
  --topic ${TOPIC_SYSTEM} \
  --partitions ${PARTITIONS_SYSTEM} \
  --replication-factor 1 \
  --config retention.ms=${RETENTION_MS}

echo "Topic 初始化完成"
docker exec ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-topics.sh --list
EOF

chmod +x init-topics.sh
./init-topics.sh

注意​:__consumer_offsets 必须手动创建,KRaft 单节点模式不会自动生成。这是导致消费者组无法工作的最常见原因。

四、踩坑记录

坑 1:advertised.listeners 配置错误

症状​:本地测试生产者发消息成功,但消费者组始终无法注册,kafka consumer 日志里没有任何 poll 操作。

原因​​:测试服 SSH 进去后用 localhost:9092 发消息,而 advertised.listeners 配置的是容器内部地址,外部客户端拿到的是错误的引导地址,后续请求全部走不通。

排查过程​:

# 查看消费者组状态——无注册信息
docker exec kafka /opt/kafka/bin/kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --list

# 对比测试服发消息时用的配置
# acks=0, retries=0,通过 localhost:9092 发送 —— 消息静默丢失了

解决​:把 advertised.listeners 改成测试服真实 IP,重新部署,重启 GameServer 用正确的 bootstrap.servers 验证链路。

坑 2:__consumer_offsets 不存在

症状​:消费者组无法启动,日志显示没有 kafkaInitsubscribepoll 任何一步。

原因​​:KRaft 单节点默认不自动创建 __consumer_offsets 内部 topic,导致消费者组元数据无处存储。

解决​:手动创建

docker exec kafka /opt/kafka/bin/kafka-topics.sh \
  --create \
  --topic __consumer_offsets \
  --partitions 50 \
  --config cleanup.policy=compact

创建后消费者组注册成功,开始正常消费 business(24条)和 system(4条)消息。

坑 3:TOPIC_AUTHORIZATION_FAILED

症状​:clog 服务报 errorCode=17,consumer 没有权限访问 日志服system日志服business 这两个 topic。

原因​​:consumer 的 ACL 权限配置不完整,缺少对业务 topic 的访问权限。

解决​:检查并补全 consumer 的 topic 授权,确保有 read 权限。

五、验证集群

cat > verify.sh << 'EOF'
#!/bin/bash
CONTAINER_NAME="kafka"
KAFKA_BIN="/opt/kafka/bin"

echo "=== 检查 broker 是否就绪 ==="
docker exec ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-broker-api-versions.sh \
  --bootstrap-server localhost:9092

echo "=== 列出所有 topic ==="
docker exec ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-topics.sh \
  --list --bootstrap-server localhost:9092

echo "=== 测试生产者 ==="
docker exec ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-console-producer.sh \
  --bootstrap-server localhost:9092 \
  --topic business \
  --property "parse.key=true" \
  --property "key.separator=:"

echo "=== 测试消费者 ==="
docker exec -d ${CONTAINER_NAME} ${KAFKA_BIN}/kafka-console-consumer.sh \
  --bootstrap-server localhost:9092 \
  --topic business \
  --from-beginning \
  --property "print.key=true"
EOF

chmod +x verify.sh
./verify.sh

六、参数自动优化依据

根据日均千万级消息量自动计算的参数:

参数 计算逻辑
num.network.threads 8 CPU 核数 × 2
num.io.threads 16 CPU 核数 × 4
socket.send.buffer.bytes 2097152 2MB
socket.receive.buffer.bytes 2097152 2MB
log.retention.hours 168 7天
log.retention.bytes -1 不设上限

七、总结

  1. KRaft 模式下 __consumer_offsets 必须手动创建​,这是和 ZooKeeper 模式最大的区别
  2. ​**advertised.listeners 必须是外部可访问的地址**​,不能用 localhost 或容器内部地址
  3. 消费者权限要单独配置​,不能只配 broker 访问权限
1
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区