Skip to content

Zookeeper 模式三节点集群 ​

Kafka 3.x 仍支持 Zookeeper 模式,存量集群大量是这个形态。新集群请直接用 KRaft,但维护老集群必须懂这套。

场景 ​

  • 接手一个 Kafka 2.x/3.x 的老集群,需要扩缩容、排障。
  • 领导要求"和现有环境保持一致"。
  • 想理解 controller、broker.id、/brokers/ids 这些概念的来源。

Kafka 4.0 已彻底移除 Zookeeper 支持,本章内容只适用于 3.x 及更早。

一、拓扑 ​

机器角色端口
10.0.0.21ZK + broker 12181 / 9092
10.0.0.22ZK + broker 22181 / 9092
10.0.0.23ZK + broker 32181 / 9092

ZK 必须奇数台(3/5),broker 数量随意。三台机器混部在小规模场景很常见。

二、安装 ​

bash
cd /opt
wget https://downloads.apache.org/kafka/3.9.0/kafka_2.13-3.9.0.tgz
tar zxf kafka_2.13-3.9.0.tgz
ln -sfn /opt/kafka_2.13-3.9.0 /opt/kafka

java -version
text
openjdk version "11.0.24" 2024-07-16 LTS
OpenJDK Runtime Environment (Red_Hat-11.0.24.0.7-1) (build 11.0.24+7-LTS)

解释:Kafka 3.9 的 broker 要求 Java 11+(客户端还兼容 8)。JDK 8 启动会直接报 Unsupported class file major version。安装 JDK 见 JDK 安装。

三、Zookeeper 配置 ​

三台的 zoo.cfg 完全一样,只有 myid 文件不同。

ini
# /opt/kafka/config/zookeeper.properties
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper
clientPort=2181
maxClientCnxns=60

# 自动清理旧快照和事务日志,否则 dataDir 会慢慢涨满
autopurge.snapRetainCount=3
autopurge.purgeInterval=24

# 集群成员
server.1=10.0.0.21:2888:3888
server.2=10.0.0.22:2888:3888
server.3=10.0.0.23:2888:3888
bash
mkdir -p /data/zookeeper
echo 1 > /data/zookeeper/myid          # 10.0.0.22 写 2,10.0.0.23 写 3
参数含义调错的后果
tickTimeZK 的基本时间单位(ms)一般不用改
initLimitfollower 与 leader 初始同步允许的 tick 数数据量大时同步超时,选举失败
syncLimit心跳超时 tick 数太小会频繁误判 follower 挂
2888follower 与 leader 数据同步必须放通
3888选举通信必须放通
autopurge.purgeInterval几小时清理一次不设 → 磁盘被快照吃满

systemd:

ini
# /etc/systemd/system/zookeeper.service
[Unit]
Description=Apache Zookeeper
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=kafka
Group=kafka
Environment="JAVA_HOME=/usr/local/jdk-11"
ExecStart=/opt/kafka/bin/zookeeper-server-start.sh /opt/kafka/config/zookeeper.properties
ExecStop=/opt/kafka/bin/zookeeper-server-stop.sh
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
bash
useradd -r -s /sbin/nologin kafka
chown -R kafka:kafka /data/zookeeper /opt/kafka
systemctl daemon-reload
systemctl enable --now zookeeper

验证(三台依次起完再验):

bash
echo ruok | nc 10.0.0.21 2181
echo stat | nc 10.0.0.21 2181
echo mntr | nc 10.0.0.21 2181 | grep -E 'zk_server_state|zk_followers|zk_synced_followers'
text
imok

Zookeeper version: 3.8.4-9316c2a1d5b4d1c0b3c8b3f6d3d3d6c1e2f3a4b5, built on 2024-02-09
Clients:
 /10.0.0.21:51234[1](queued=0,recved=1,sent=0)

Latency min/avg/max: 0/0.0/0
Received: 1
Sent: 0
Connections: 1
Outstanding: 0
Zxid: 0x0
Mode: standalone
Node count: 5

zk_server_state	leader
zk_followers	2
zk_synced_followers	2

⚠️ 关键:zk_server_state 必须是 leader 或 follower。如果显示 standalone 或 looking:

  • standalone:server.N 没配对,或 myid 缺失/K 写错 → 它以为自己是单机。
  • looking:正在选举,长时间不结束说明三台互相连不上(检查 2888/3888)。

三台都在 looking 是最常见的部署失败

原因几乎都是:myid 内容不对(写成了 server.1 或带了换行以外字符)、server.N 的 IP 填错、2888/3888 被防火墙挡。先 ss -lntp | grep -E '2888|3888' 确认在监听,再用 nc -vz 逐个测连通。

四、broker 配置 ​

三台的 server.properties 只有 broker.id、listeners、advertised.listeners 三处不同。

ini
# /opt/kafka/config/server.properties
broker.id=1
listeners=PLAINTEXT://10.0.0.21:9092
advertised.listeners=PLAINTEXT://10.0.0.21:9092
num.network.threads=8
num.io.threads=16
socket.send.buffer.bytes=1048576
socket.receive.buffer.bytes=1048576
socket.request.max.bytes=104857600

log.dirs=/data/kafka/logs
num.partitions=3
num.recovery.threads.per.data.dir=2

# 副本与 ISR
default.replication.factor=2
min.insync.replicas=2
unclean.leader.election.enable=false

# 内部 topic 必须 3 副本,否则三台死一台就报错
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2

# 日志保留
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000

# 禁止自动建 topic(生产必须关)
auto.create.topics.enable=false
delete.topic.enable=true

zookeeper.connect=10.0.0.21:2181,10.0.0.22:2181,10.0.0.23:2181/kafka
zookeeper.connection.timeout.ms=18000

逐项解释:

配置为什么这么设
advertised.listeners客户端实际连的地址。设错就是"本机 --bootstrap-server localhost 能用,别的机器连不上"
log.dirs单独一块盘,别和系统盘共用(日志写满会带崩 OS)
offsets.topic.replication.factor=3默认是 3 但只在集群有 3 台 broker 时才能真正建成;集群只有 1 台时这个 topic 建不出来,消费组用不了
unclean.leader.election.enable=false宁可不可用也不选一个落后副本当 leader(会丢数据)
auto.create.topics.enable=false开着的话,生产者打错 topic 名就静默创建一堆 1 分区 topic
zookeeper.connect 末尾的 /kafka所有元数据放 /kafka 下,同一套 ZK 可以给多个 Kafka 集群用

default.replication.factor 不等于自动建 topic 的副本数

真正生效的是 default.replication.factor(3.0+)或 offsets.topic.replication.factor 管内部 topic、num.partitions + default.replication.factor 管自动建的业务 topic。生产环境 auto.create.topics.enable=false,所有 topic 都该显式创建并指定副本数。

五、启动与验证 ​

bash
chown -R kafka:kafka /data/kafka
systemctl enable --now kafka
journalctl -u kafka -n 30 --no-pager

正常日志:

text
[2026-10-27 19:10:02,114] INFO [KafkaServer id=1] started (kafka.server.KafkaServer)
[2026-10-27 19:10:02,188] INFO [BrokerLifecycleManager id=1] Registered broker 1 with metadata.version 3.9-IV0. (kafka.server.BrokerLifecycleManager)

集群检查:

bash
/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server 10.0.0.21:9092 | head -5
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.21:9092 --list
text
10.0.0.21:9092 (id: 1 rack: null) -> (
	Produce(0): 0 to 11 [usable: 11],
	Fetch(1): 0 to 13 [usable: 13],
	...
)

__consumer_offsets

__consumer_offsets 已经存在,说明内部 topic 建成功了。

确认三台都注册到 ZK:

bash
/opt/kafka/bin/zookeeper-shell.sh 10.0.0.21:2181 ls /kafka/brokers/ids
text
Connecting to 10.0.0.21:2181
Welcome to ZooKeeper!
JLine support is enabled

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[1, 2, 3]

[1, 2, 3] 就是三台 broker 的 broker.id。少一个,去那台看日志。

六、冒烟测试 ​

bash
# 建 topic:3 分区 2 副本
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.21:9092 \
  --create --topic smoke --partitions 3 --replication-factor 2

# 看分布
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.21:9092 --describe --topic smoke
text
Created topic smoke.

Topic: smoke	TopicId: 8sT2mQ1nR4vW6xY8zA0bCw	PartitionCount: 3	ReplicationFactor: 2	Configs:
	Topic: smoke	Partition: 0	Leader: 1	Replicas: 1,2	Isr: 1,2
	Topic: smoke	Partition: 1	Leader: 2	Replicas: 2,3	Isr: 2,3
	Topic: smoke	Partition: 2	Leader: 3	Replicas: 3,1	Isr: 3,1

Isr 与 Replicas 一样长 = 全部同步中。如果 Isr: 1(只有 leader),说明副本没跟上,看 kafka-server.log 里的副本拉取错误。

bash
# 生产
printf 'hello\nworld\n' | /opt/kafka/bin/kafka-console-producer.sh \
  --bootstrap-server 10.0.0.21:9092 --topic smoke

# 消费
/opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server 10.0.0.21:9092 \
  --topic smoke --from-beginning --max-messages 2
text
hello
world
Processed a total of 2 messages

七、常用运维 ​

bash
# 停
systemctl stop kafka

# 滚动重启(一次一台,等它 ISR 恢复再下一台)
for h in 10.0.0.21 10.0.0.22 10.0.0.23; do
  ssh $h 'systemctl restart kafka'
  sleep 30
  /opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.21:9092 --describe --under-replicated-partitions
done

# 看 controller 是谁
/opt/kafka/bin/zookeeper-shell.sh 10.0.0.21:2181 get /kafka/controller
text
{"version":1,"brokerid":2,"timestamp":"1761549602114"}

brokerid 就是当前 controller。controller 负责分区 leader 选举、副本分配,它挂了会触发重新选举(通常几秒)。

八、踩过的坑 ​

现象根因处置
ZK 三台都 Mode: standaloneserver.N/myid 没用上检查 myid 内容与路径
ZK 长期 looking2888/3888 不通放通端口,nc -vz 验证
本机能连、别的机器连不上advertised.listeners 填了 localhost改成真实 IP
消费组报 __consumer_offsets 相关错误集群不足 3 台时该 topic 建不出来临时把 offsets.topic.replication.factor 调到 broker 数,或补齐 broker
broker.id 重复拷贝配置没改每台唯一,且不要随意改(改了集群不认)
重启后数据目录报 Cluster ID mismatchlog.dirs 被换过/挂载丢失恢复原目录,别用空目录启动
磁盘不明原因增长没开 log.retention.bytes,或 autopurge 关着见 Topic 与分区
集群只有 1 台 broker 却设 RF=3创建 topic 直接失败 replication factor: 3 larger than available brokers: 1先凑够 broker 或降 RF