Learn
Kafka/03-installation-kraft

安装与部署(KRaft 模式)

本章把 Kafka 跑起来:先单机快速体验,再搭一个贴近生产形态的三节点 KRaft 集群,最后给出 Docker Compose 一键编排。所有步骤基于 Kafka 3.7+,不需要 ZooKeeper。

1. 单机快速启动

1.1 下载与解压

Kafka 只需要 JDK 11+(推荐 17):

# 确认 Java
java -version
 
# 下载并解压 (以 3.7.1 为例, Scala 2.13 构建)
curl -O https://archive.apache.org/dist/kafka/3.7.1/kafka_2.13-3.7.1.tgz
tar -xzf kafka_2.13-3.7.1.tgz
cd kafka_2.13-3.7.1

1.2 初始化并启动

KRaft 模式启动前必须格式化存储目录——生成集群 ID 并写入元数据:

# 1. 生成集群唯一 ID
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
echo $KAFKA_CLUSTER_ID
 
# 2. 用集群 ID 格式化日志目录
bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID \
  -c config/kraft/server.properties
 
# 3. 启动 (前台运行, 加 -daemon 可后台)
bin/kafka-server-start.sh config/kraft/server.properties

验证:

# 新开终端, 创建 topic 并收发一条消息
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
  --create --topic hello --partitions 1 --replication-factor 1
 
echo "hello kafka" | bin/kafka-console-producer.sh \
  --bootstrap-server localhost:9092 --topic hello
 
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic hello --from-beginning --max-messages 1
# 输出: hello kafka
⚠️格式化只做一次

kafka-storage.sh format 只在初始化时执行一次。对已有数据目录重复格式化会导致集群 ID 不一致、Broker 拒绝启动。如果启动报 InconsistentClusterIdException,通常是数据目录残留了旧集群的元数据。

2. server.properties 关键配置

单机默认配置能跑,但必须理解这些参数才能上生产:

############ 角色与节点 ############
# 该节点承担的角色: broker、controller 或两者兼任
process.roles=broker,controller
# 集群内唯一的节点 ID
node.id=1
# controller 仲裁投票者列表: id@host:port
controller.quorum.voters=1@localhost:9093
 
############ 监听器 ############
# 9092 服务客户端, 9093 用于 controller 内部通信
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
# 客户端实际连接的地址 —— 容器/云环境最容易踩坑的配置
advertised.listeners=PLAINTEXT://localhost:9092
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT
 
############ 存储 ############
# 日志数据目录, 生产环境用独立磁盘, 可逗号分隔多块盘
log.dirs=/var/kafka/data
# 默认保留 7 天
log.retention.hours=168
# 单个 segment 1GB
log.segment.bytes=1073741824
 
############ 主题默认值 ############
num.partitions=3
default.replication.factor=3
min.insync.replicas=2
# 生产环境务必关闭自动建 topic
auto.create.topics.enable=false
💡advertised.listeners 是新手第一坑

listeners 是进程绑定的地址,advertised.listeners 是告诉客户端来连我用的地址。客户端第一次连上任意 broker 后,会拿到集群元数据里的 advertised 地址再发起真正的读写连接。容器里如果 advertised 写的是容器内部主机名,宿主机客户端就会"能连上 bootstrap 却读写超时"。

3. 三节点 KRaft 集群

生产最小形态:3 个节点,每个都兼任 broker + controller。三份配置的差异只有 node.id、监听端口和日志目录:

# node1.properties (node2/node3 改 id、端口、目录即可)
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@host1:9093,2@host2:9093,3@host3:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://host1:9092
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT
log.dirs=/var/kafka/data
default.replication.factor=3
min.insync.replicas=2

三台机器用同一个集群 ID 格式化并启动:

# 在一台机器上生成一次, 三台共用
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
 
# 每台机器分别执行
bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID -c node1.properties
bin/kafka-server-start.sh -daemon node1.properties

验证集群状态:

# 查看 controller 仲裁状态: 谁是 leader、各节点日志追赶进度
bin/kafka-metadata-quorum.sh --bootstrap-server host1:9092 describe --status
 
# 查看所有 broker
bin/kafka-broker-api-versions.sh --bootstrap-server host1:9092 | grep id

4. Docker Compose 编排

本地开发推荐直接用官方 apache/kafka 镜像。单节点版:

# docker-compose.yml
services:
  kafka:
    image: apache/kafka:3.7.1
    ports:
      - "9092:9092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_LISTENERS: PLAINTEXT://:19092,CONTROLLER://:9093,EXTERNAL://:9092
      # 容器间用 kafka:19092, 宿主机用 localhost:9092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:19092,EXTERNAL://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false"
    healthcheck:
      test: ["CMD", "/opt/kafka/bin/kafka-broker-api-versions.sh",
             "--bootstrap-server", "localhost:19092"]
      interval: 10s
      timeout: 10s
      retries: 5

三节点版只需复制三份服务,KAFKA_NODE_ID 分别为 1/2/3,KAFKA_CONTROLLER_QUORUM_VOTERS 写全三个节点:

      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka1:9093,2@kafka2:9093,3@kafka3:9093

启动与验证:

docker compose up -d
docker compose ps            # 等 healthcheck 变 healthy
docker exec -it <容器名> /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server localhost:19092 --list

5. 生产部署检查清单

项目建议
JDK17,堆 6-8GB(KAFKA_HEAP_OPTS),剩余内存留给 PageCache
磁盘数据目录独立磁盘,XFS,SSD 或多块 HDD
文件句柄ulimit -n 100000 以上
swapvm.swappiness=1
副本default.replication.factor=3,min.insync.replicas=2
自动建 topic关闭
controller大集群独立部署 3 或 5 个 controller 节点
⚠️常见启动故障
  1. 端口被占:9092/9093 冲突,lsof -i :9092 排查。
  2. Cluster ID 不匹配:换配置重搭时没清空 log.dirs,删除数据目录或重新 format。
  3. 客户端连不上:99% 是 advertised.listeners 配错,用 kafka-broker-api-versions.sh 从客户端所在网络测试。
  4. controller 无法选主:controller.quorum.voters 三台配置不一致,或防火墙拦了 9093。

小结

  • KRaft 启动三步:生成集群 ID → format 存储目录 → 启动
  • process.roles、controller.quorum.voters、advertised.listeners 是 KRaft 三大核心配置
  • 三节点集群共用一个集群 ID,配置只差 node.id 与地址
  • 本地开发用官方镜像 + Docker Compose,healthcheck 用 broker-api-versions 探测
  • 生产记住:副本 3、min.insync.replicas 2、关自动建 topic、留内存给 PageCache
🎯练习
  1. 用 Docker Compose 启动三节点集群,创建一个 3 分区 3 副本的 topic,用 describe 确认 leader 分布在三个不同 broker 上。
  2. docker stop 掉其中一个 broker,再次 describe,观察 Leader 和 Isr 列的变化;重新启动后再看 Isr 是否恢复。
  3. 故意把 advertised.listeners 改成错误主机名,体验一下客户端"能 bootstrap 却发不了消息"的经典故障。