3、集群部署实战:多Broker部署、ZooKeeper/KRaft模式选择、关键配置项解读
好,咱们进入第三章。这一章我打算直接动手,聊聊怎么把一个单机版的Kafka,变成一个真正能扛生产的集群。
说实话,很多新手在部署Kafka时,最容易犯的错就是——照着官方文档配完,以为万事大吉。结果一上线,要么Leader选举慢得像蜗牛,要么磁盘写爆了都不知道。嗯,今天咱们就把这些坑一个个填上。
3.1 多Broker部署:从单点到集群
先说说最基础的多Broker部署。你想想看,单机Kafka说白了就是个玩具。一旦机器挂了,整个系统就瘫了。所以我们要做的是:把多个Broker组成一个集群,让它们互相备份。
部署步骤其实不复杂,我习惯这么干:
- 准备三台机器(至少三台,奇数台是为了配合ZooKeeper的选举机制)。
- 每台机器上解压Kafka,路径保持一致,比如
/opt/kafka。 - 修改
config/server.properties,重点改以下几个参数:
# 每个Broker的唯一ID,不能重复
broker.id=0
# 监听地址,建议用内网IP
listeners=PLAINTEXT://192.168.1.10:9092
# 日志数据目录,建议挂载独立磁盘
log.dirs=/data/kafka/logs
# ZooKeeper连接串,多个用逗号分隔
zookeeper.connect=192.168.1.10:2181,192.168.1.11:2181,192.168.1.12:2181
这里有个细节——broker.id 不能重复。我在项目中遇到过有人复制配置文件忘了改这个,结果第二个Broker死活起不来,日志里报“Duplicate broker ID”。
启动命令也很简单:
# 每个Broker上分别执行
bin/kafka-server-start.sh -daemon config/server.properties
启动后,用 bin/kafka-broker-api-versions.sh --bootstrap-server 192.168.1.10:9092 验证一下,能看到所有Broker的版本信息,就说明集群通了。
3.2 ZooKeeper vs KRaft:怎么选?
好,接下来是个大话题——元数据管理选ZooKeeper还是KRaft?
说白了,Kafka从2.8版本开始引入了KRaft模式,目的是干掉ZooKeeper这个“外部依赖”。但直到3.x版本,KRaft才算真正成熟。我个人建议:
- 新项目、新集群:直接用KRaft。省掉一套ZooKeeper集群,运维成本直线下降。
- 已有ZooKeeper集群:别急着迁移。等Kafka 4.0正式发布,ZooKeeper被彻底移除后再说。
两者的核心差异,我整理了一张表:
| 对比维度 | ZooKeeper模式 | KRaft模式 |
|---|---|---|
| 依赖组件 | 需要额外部署ZooKeeper集群 | Kafka自身管理元数据 |
| 部署复杂度 | 高(两套集群) | 低(一套搞定) |
| 元数据一致性 | ZooKeeper的Zab协议 | Kafka自研的KRaft(基于Raft) |
| Controller选举 | 依赖ZooKeeper临时节点 | KRaft Quorum内部选举 |
| 生产推荐度 | 成熟稳定,广泛使用 | 3.5+版本可生产,推荐新集群使用 |
如果你决定用KRaft,配置上有个关键变化——不再需要 zookeeper.connect,而是用 controller.quorum.voters 来指定元数据节点:
# KRaft模式下的关键配置
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@192.168.1.10:9093,2@192.168.1.11:9093,3@192.168.1.12:9093
注意这里的 process.roles,可以同时指定 broker 和 controller,也可以分开。我习惯把前几台节点同时承担两种角色,后面的节点只做Broker,这样既保证了元数据高可用,又降低了Controller的压力。
node.id 和 broker.id 没保持一致。结果集群启动后,Controller和Broker互相不认,日志里全是“Unknown member”的报错。记住:KRaft模式下,node.id 就是全局唯一标识,别再单独设 broker.id 了。
3.3 关键配置项解读:别让默认值坑了你
Kafka的配置项少说也有上百个,但真正需要你操心的,其实就十几个。我挑几个最容易出问题的,一个一个说。
3.3.1 网络与线程配置
# 监听器,生产环境建议用SSL或SASL
listeners=PLAINTEXT://0.0.0.0:9092
advertised.listeners=PLAINTEXT://你的公网或内网IP:9092
# 网络线程数,一般设为CPU核数
num.network.threads=8
# IO线程数,建议设为磁盘数的2倍
num.io.threads=16
这里有个坑——advertised.listeners。如果你用Docker或云主机,内网IP和客户端能访问的IP可能不一样。不配这个参数,客户端连上来会拿到Broker的内网IP,直接连不上。我踩过这个坑,那次排查了整整两个小时。
3.3.2 日志与数据保留
# 日志保留时间,默认7天
log.retention.hours=168
# 日志保留大小,默认-1(不限)
log.retention.bytes=-1
# 日志段大小,默认1GB
log.segment.bytes=1073741824
你想想看,如果不设 log.retention.bytes,磁盘写满了会发生什么?Kafka会直接报错,不再接受新消息。所以我的习惯是:同时设置时间和大小两个限制,哪个先到就触发清理。
- 保留时间:72小时(3天),满足大部分回溯需求
- 保留大小:每个Topic 500GB,防止单个Topic撑爆磁盘
- 段大小:1GB,平衡文件数量和IO性能
3.3.3 副本与可靠性
# 默认副本数,建议设为2或3
default.replication.factor=3
# 最小同步副本数,保证写入可靠性
min.insync.replicas=2
# 确认模式:0=不等待,1=Leader确认,all=所有ISR确认
acks=all
这三个参数是黄金组合。我见过很多团队把 acks 设为1,觉得性能好。结果Broker挂了,数据丢了都不知道。我的建议是:核心业务用 acks=all + min.insync.replicas=2,非核心业务可以放宽到 acks=1。
3.3.4 文件系统与刷盘
# 刷盘间隔,默认每次写入都刷盘(性能差)
log.flush.interval.messages=10000
log.flush.interval.ms=1000
这里有个误区——很多人觉得Kafka是异步刷盘的,其实默认是每次写入都刷盘。但这样性能太差。我一般会调大这两个值,让操作系统自己决定什么时候刷盘。Linux的Page Cache机制比Kafka自己刷盘要高效得多。
3.4 一张图看懂集群部署架构
说了这么多,咱们用一张SVG图把整个集群部署架构串起来。这样你脑子里能有个整体印象。
3.5 部署后的验证清单
集群部署完,别急着上线。我习惯按这个清单逐一验证:
- Broker状态:
bin/kafka-broker-api-versions.sh --bootstrap-server <你的集群地址>,确认所有Broker都在线。 - Topic创建:创建一个测试Topic,指定
--replication-factor 3 --partitions 3,然后用--describe查看副本分布是否均匀。 - 生产消费测试:用
kafka-console-producer.sh和kafka-console-consumer.sh发几条消息,确认能正常收发。 - 故障模拟:手动停掉一个Broker,看看Leader是否自动切换到其他Broker。再停掉一个,看看集群是否还能正常服务。
- 性能基准:用
kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh跑一下,确认吞吐量符合预期。
controller.socket.timeout.ms 设得太长。这种问题,只有模拟故障才能暴露出来。
好了,这一章的内容就到这里。集群部署是个细致活,每个参数背后都有它的设计意图。别偷懒,把每个配置项都理解透,你的Kafka集群才能跑得稳、跑得久。