Kafka KRaft 集群安装指南
概述
KRaft 模式简介
Kafka KRaft(Kafka Raft Metadata)模式是自 Kafka 2.8 开始引入、在 Kafka 3.3 之后正式生产可用的架构模式。其核心思想是移除 ZooKeeper 依赖,由 Kafka 自身基于 Raft 共识协议管理集群元数据。
在 KRaft 模式下,每个节点可以承担以下两种角色之一或兼具:
- Controller:参与 Raft 仲裁,负责管理元数据(Topic、分区、副本分配等)
- Broker:负责实际的消息存储与读写
当 process.roles=broker,controller 时,该节点同时承担两种角色,称为联合节点(Combined Node)。
与 ZooKeeper 模式对比
| 对比项 | ZooKeeper 模式 | KRaft 模式 |
|---|---|---|
| 外部依赖 | 需要 ZooKeeper 集群(至少 3 节点) | 无外部依赖,Kafka 自管理元数据 |
| 元数据存储 | ZK 中 znode | Kafka 内部 Topic(@clusterId) |
| 元数据更新 | 需要与 ZK 同步,存在延迟 | Raft 协议直接写入,一致性强 |
| 部署复杂度 | 高(需维护两套系统) | 低(仅部署 Kafka 即可) |
| 集群规模上限 | 受 ZK 元数据量限制 | 支持更大规模的分区数 |
| 故障恢复 | 依赖 ZK leader 选举 | Raft 协议自动 leader 选举 |
| 版本支持 | Kafka 4.0 起将完全移除 | Kafka 3.3+ 生产可用,4.0+ 唯一模式 |
伪分布式集群架构
本指南在单台主机 上部署 3 个 Kafka KRaft 节点,构成伪分布式集群。每个节点使用不同的端口和数据目录,模拟真实的多机集群环境。
bash
┌─────────────────────────────────────────┐
│ 主机: 192.168.1.1 (admin-PC) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ node1 │ │ node2 │ │ node3 │
│ │ id=1 │ │ id=2 │ │ id=3 │
│ │ B:9092 │ │ B:9192 │ │ B:9292 │
│ │ C:9093 │ │ C:9193 │ │ C:9293 │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │ │
│ └────── Raft 仲裁协议 ───────┘ │
│ │
│ 数据目录: │
│ /tmp/kafka-kraft-node1 │
│ /tmp/kafka-kraft-node2 │
│ /tmp/kafka-kraft-node3 │
└─────────────────────────────────────────┘
B = Broker 端口 C = Controller 端口
每个节点均配置为 broker,controller 联合模式,3 个 Controller 节点组成 Raft 仲裁集群。
环境准备
初始状态说明
本指南基于干净的初始环境编写------Kafka 从未启动过,无历史数据。若您的环境已有历史运行数据,需先清理残留的日志目录,否则可能导致 Cluster UUID 不一致、端口占用等启动失败问题。
清理历史数据(仅非首次安装时执行):
bash
# 停止所有 Kafka 进程(如有)
/usr/local/kafka/bin/kafka-server-stop.sh
# 确认无残留进程
jps -l | grep kafka
# 清理默认日志目录(如有历史数据)
rm -rf /tmp/kraft-combined-logs/*
rm -rf /tmp/kafka-kraft-node1/*
rm -rf /tmp/kafka-kraft-node2/*
rm -rf /tmp/kafka-kraft-node3/*
系统信息
| 项目 | 值 |
|---|---|
| 操作系统 | UnionTech OS Server 20 Enterprise |
| 内核版本 | 4.19 |
| 主机名 | admin-PC |
| 主机 IP | 192.168.1.1 |
| Java | OpenJDK 21.0.10 Temurin |
| JAVA_HOME | /usr/local/java/jdk-21.0.10+7 |
| Kafka 版本 | 3.8.1 (Scala 2.13) |
| Kafka 安装路径 | /usr/local/kafka |
| CPU | 16 核 |
| 内存 | 62 GB |
| 磁盘可用空间 | 174 GB |
环境检查
bash
# 检查 Java 版本
java -version
# 验证 JAVA_HOME 环境变量
echo $JAVA_HOME
# 检查磁盘可用空间(确保有足够空间存储 Kafka 日志)
df -h /tmp
# 检查 Kafka 安装目录
ls -la /usr/local/kafka/bin/ | head -20
# 确认 Kafka 版本
/usr/local/kafka/bin/kafka-storage.sh version
# 检查关键端口是否被占用(应为全部空闲)
ss -tlnp | grep -E '9092|9093|9192|9193|9292|9293'
目录规划
节点目录分配
| 节点 | node.id | 数据目录 | 配置文件路径 | Broker 端口 | Controller 端口 |
|---|---|---|---|---|---|
| node1 | 1 | /tmp/kafka-kraft-node1 | /usr/local/kafka/config/kraft/server-node1.properties | 9092 | 9093 |
| node2 | 2 | /tmp/kafka-kraft-node2 | /usr/local/kafka/config/kraft/server-node2.properties | 9192 | 9193 |
| node3 | 3 | /tmp/kafka-kraft-node3 | /usr/local/kafka/config/kraft/server-node3.properties | 9292 | 9293 |
创建数据目录
bash
# 创建 3 个节点的数据目录
mkdir -p /tmp/kafka-kraft-node1
mkdir -p /tmp/kafka-kraft-node2
mkdir -p /tmp/kafka-kraft-node3
# 验证目录创建结果
ls -ld /tmp/kafka-kraft-node1 /tmp/kafka-kraft-node2 /tmp/kafka-kraft-node3
配置文件详解
以下为 3 个节点的完整配置文件。所有配置基于 /usr/local/kafka/config/kraft/server.properties 模板修改,针对伪分布式 3 节点集群进行了适配。
node1 配置文件
文件路径: /usr/local/kafka/config/kraft/server-node1.properties
properties
############################# 角色与节点标识 #############################
# 节点同时承担 broker 和 controller 角色(联合节点)
process.roles=broker,controller
# 节点唯一 ID,集群内不可重复
node.id=1
# Controller 仲裁投票者列表,列出所有 controller 节点的 id@host:port
# 3 个节点的 controller 端口分别为 9093、9193、9293
controller.quorum.voters=1@localhost:9093,2@localhost:9193,3@localhost:9293
############################# 监听器配置 #############################
# 节点监听的端口:Broker 使用 9092,Controller 使用 9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
# Broker 间通信使用的监听器名称
inter.broker.listener.name=PLAINTEXT
# 对外宣告的 Broker 地址,必须配置为实际可达 IP(非 localhost)
# 客户端和其他 Broker 通过此地址连接本节点
advertised.listeners=PLAINTEXT://192.168.1.1:9092
# Controller 监听器名称
controller.listener.names=CONTROLLER
# 监听器到安全协议的映射
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL
############################# 线程与网络 #############################
# 网络请求处理线程数(默认 3)
num.network.threads=3
# 磁盘 IO 处理线程数(默认 8)
num.io.threads=8
# Socket 发送缓冲区大小(默认 100KB)
socket.send.buffer.bytes=102400
# Socket 接收缓冲区大小(默认 100KB)
socket.receive.buffer.bytes=102400
# Socket 请求最大字节数(默认 100MB)
socket.request.max.bytes=104857600
############################# 日志与数据存储 #############################
# 本节点数据目录(与其他节点隔离)
log.dirs=/tmp/kafka-kraft-node1
# 新建 Topic 的默认分区数(集群 3 节点,设为 3 以充分利用)
num.partitions=3
# 启动时每个数据目录的恢复线程数
num.recovery.threads.per.data.dir=1
############################# 内部 Topic 副本数 #############################
# __consumer_offsets Topic 的副本数(集群 3 节点设为 3)
offsets.topic.replication.factor=3
# __transaction_state Topic 的副本数
transaction.state.log.replication.factor=3
# 事务状态日志的最小同步副本数
transaction.state.log.min.isr=3
############################# 日志保留策略 #############################
# 日志保留时间(默认 168 小时 = 7 天)
log.retention.hours=168
# 日志段文件大小(默认 1GB)
log.segment.bytes=1073741824
# 日志保留检查间隔(默认 5 分钟)
log.retention.check.interval.ms=300000
node2 配置文件
文件路径: /usr/local/kafka/config/kraft/server-node2.properties
properties
############################# 角色与节点标识 #############################
# 节点同时承担 broker 和 controller 角色(联合节点)
process.roles=broker,controller
# 节点唯一 ID,集群内不可重复
node.id=2
# Controller 仲裁投票者列表,3 个节点共用同一配置
controller.quorum.voters=1@localhost:9093,2@localhost:9193,3@localhost:9293
############################# 监听器配置 #############################
# 节点监听的端口:Broker 使用 9192,Controller 使用 9193
listeners=PLAINTEXT://:9192,CONTROLLER://:9193
# Broker 间通信使用的监听器名称
inter.broker.listener.name=PLAINTEXT
# 对外宣告的 Broker 地址,配置为实际可达 IP
advertised.listeners=PLAINTEXT://192.168.1.1:9192
# Controller 监听器名称
controller.listener.names=CONTROLLER
# 监听器到安全协议的映射
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL
############################# 线程与网络 #############################
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
############################# 日志与数据存储 #############################
# 本节点数据目录(与其他节点隔离)
log.dirs=/tmp/kafka-kraft-node2
# 新建 Topic 的默认分区数
num.partitions=3
num.recovery.threads.per.data.dir=1
############################# 内部 Topic 副本数 #############################
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=3
############################# 日志保留策略 #############################
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
node3 配置文件
文件路径: /usr/local/kafka/config/kraft/server-node3.properties
properties
############################# 角色与节点标识 #############################
# 节点同时承担 broker 和 controller 角色(联合节点)
process.roles=broker,controller
# 节点唯一 ID,集群内不可重复
node.id=3
# Controller 仲裁投票者列表,3 个节点共用同一配置
controller.quorum.voters=1@localhost:9093,2@localhost:9193,3@localhost:9293
############################# 监听器配置 #############################
# 节点监听的端口:Broker 使用 9292,Controller 使用 9293
listeners=PLAINTEXT://:9292,CONTROLLER://:9293
# Broker 间通信使用的监听器名称
inter.broker.listener.name=PLAINTEXT
# 对外宣告的 Broker 地址,配置为实际可达 IP
advertised.listeners=PLAINTEXT://192.168.1.1:9292
# Controller 监听器名称
controller.listener.names=CONTROLLER
# 监听器到安全协议的映射
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL
############################# 线程与网络 #############################
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
############################# 日志与数据存储 #############################
# 本节点数据目录(与其他节点隔离)
log.dirs=/tmp/kafka-kraft-node3
# 新建 Topic 的默认分区数
num.partitions=3
num.recovery.threads.per.data.dir=1
############################# 内部 Topic 副本数 #############################
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=3
############################# 日志保留策略 #############################
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
配置文件快速创建
以下脚本可快速生成 3 个配置文件:
bash
# Kafka 配置目录
KRAFT_CONFIG_DIR=/usr/local/kafka/config/kraft
# 复制模板并逐一修改(如果手动编辑,可跳过此脚本)
cp $KRAFT_CONFIG_DIR/server.properties $KRAFT_CONFIG_DIR/server-node1.properties
cp $KRAFT_CONFIG_DIR/server.properties $KRAFT_CONFIG_DIR/server-node2.properties
cp $KRAFT_CONFIG_DIR/server.properties $KRAFT_CONFIG_DIR/server-node3.properties
# 然后根据上述各节点配置内容,手动编辑修改对应配置项
# 也可使用 sed 批量替换(见下方命令)
# node1 配置修改
sed -i 's/^node.id=.*/node.id=1/' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's/^listeners=.*/listeners=PLAINTEXT:\/\/:9092,CONTROLLER:\/\/:9093/' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's|^advertised.listeners=.*|advertised.listeners=PLAINTEXT://192.168.1.1:9092|' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's|^log.dirs=.*|log.dirs=/tmp/kafka-kraft-node1|' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's/^controller.quorum.voters=.*/controller.quorum.voters=1@localhost:9093,2@localhost:9193,3@localhost:9293/' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's/^offsets.topic.replication.factor=.*/offsets.topic.replication.factor=3/' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's/^transaction.state.log.replication.factor=.*/transaction.state.log.replication.factor=3/' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's/^transaction.state.log.min.isr=.*/transaction.state.log.min.isr=3/' $KRAFT_CONFIG_DIR/server-node1.properties
sed -i 's/^num.partitions=.*/num.partitions=3/' $KRAFT_CONFIG_DIR/server-node1.properties
# node2 配置修改
sed -i 's/^node.id=.*/node.id=2/' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's/^listeners=.*/listeners=PLAINTEXT:\/\/:9192,CONTROLLER:\/\/:9193/' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's|^advertised.listeners=.*|advertised.listeners=PLAINTEXT://192.168.1.1:9192|' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's|^log.dirs=.*|log.dirs=/tmp/kafka-kraft-node2|' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's/^controller.quorum.voters=.*/controller.quorum.voters=1@localhost:9093,2@localhost:9193,3@localhost:9293/' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's/^offsets.topic.replication.factor=.*/offsets.topic.replication.factor=3/' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's/^transaction.state.log.replication.factor=.*/transaction.state.log.replication.factor=3/' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's/^transaction.state.log.min.isr=.*/transaction.state.log.min.isr=3/' $KRAFT_CONFIG_DIR/server-node2.properties
sed -i 's/^num.partitions=.*/num.partitions=3/' $KRAFT_CONFIG_DIR/server-node2.properties
# node3 配置修改
sed -i 's/^node.id=.*/node.id=3/' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's/^listeners=.*/listeners=PLAINTEXT:\/\/:9292,CONTROLLER:\/\/:9293/' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's|^advertised.listeners=.*|advertised.listeners=PLAINTEXT://192.168.1.1:9292|' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's|^log.dirs=.*|log.dirs=/tmp/kafka-kraft-node3|' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's/^controller.quorum.voters=.*/controller.quorum.voters=1@localhost:9093,2@localhost:9193,3@localhost:9293/' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's/^offsets.topic.replication.factor=.*/offsets.topic.replication.factor=3/' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's/^transaction.state.log.replication.factor=.*/transaction.state.log.replication.factor=3/' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's/^transaction.state.log.min.isr=.*/transaction.state.log.min.isr=3/' $KRAFT_CONFIG_DIR/server-node3.properties
sed -i 's/^num.partitions=.*/num.partitions=3/' $KRAFT_CONFIG_DIR/server-node3.properties
# 验证配置文件关键项
echo "=== node1 关键配置 ==="
grep -E '^(process\.roles|node\.id|controller\.quorum\.voters|listeners|advertised\.listeners|log\.dirs|offsets\.topic\.replication\.factor|num\.partitions)' $KRAFT_CONFIG_DIR/server-node1.properties
echo "=== node2 关键配置 ==="
grep -E '^(process\.roles|node\.id|controller\.quorum\.voters|listeners|advertised\.listeners|log\.dirs|offsets\.topic\.replication\.factor|num\.partitions)' $KRAFT_CONFIG_DIR/server-node2.properties
echo "=== node3 关键配置 ==="
grep -E '^(process\.roles|node\.id|controller\.quorum\.voters|listeners|advertised\.listeners|log\.dirs|offsets\.topic\.replication\.factor|num\.partitions)' $KRAFT_CONFIG_DIR/server-node3.properties
集群初始化
KRaft 模式要求在首次启动前对每个节点的数据目录进行格式化(format),生成并绑定 Cluster UUID。3 个节点必须使用同一个 Cluster UUID,否则无法组成集群。
步骤 1:生成 Cluster UUID
bash
# 生成集群唯一标识 UUID
CLUSTER_UUID=$(/usr/local/kafka/bin/kafka-storage.sh random-uuid)
echo "Cluster UUID: $CLUSTER_UUID"
# 将 UUID 保存到变量供后续使用(或手动记录)
# 示例输出:Cluster UUID: fPvXyZabc123defGhiJklMnoPqrStuVw
步骤 2:使用同一 UUID 格式化 3 个节点目录
将上一步生成的 UUID 替换下面命令中的 <YOUR_CLUSTER_UUID>,对 3 个节点目录分别执行格式化:
bash
# 将生成的 UUID 赋值给变量(替换为实际生成的 UUID)
CLUSTER_UUID=<YOUR_CLUSTER_UUID>
# 格式化 node1 数据目录
/usr/local/kafka/bin/kafka-storage.sh format \
-t $CLUSTER_UUID \
-c /usr/local/kafka/config/kraft/server-node1.properties
# 格式化 node2 数据目录
/usr/local/kafka/bin/kafka-storage.sh format \
-t $CLUSTER_UUID \
-c /usr/local/kafka/config/kraft/server-node2.properties
# 格式化 node3 数据目录
/usr/local/kafka/bin/kafka-storage.sh format \
-t $CLUSTER_UUID \
-c /usr/local/kafka/config/kraft/server-node3.properties
步骤 3:验证格式化结果
bash
# 检查各节点目录下是否生成了 meta.properties 文件
cat /tmp/kafka-kraft-node1/meta.properties
cat /tmp/kafka-kraft-node2/meta.properties
cat /tmp/kafka-kraft-node3/meta.properties
# 确认 3 个节点的 cluster.id 值一致
grep cluster.id /tmp/kafka-kraft-node1/meta.properties
grep cluster.id /tmp/kafka-kraft-node2/meta.properties
grep cluster.id /tmp/kafka-kraft-node3/meta.properties
一键格式化脚本
bash
#!/bin/bash
# 文件路径: /home/admin/format-cluster.sh
# 功能: 生成 Cluster UUID 并格式化 3 个节点目录
KAFKA_HOME=/usr/local/kafka
KRAFT_CONFIG_DIR=$KAFKA_HOME/config/kraft
# 生成 Cluster UUID
CLUSTER_UUID=$($KAFKA_HOME/bin/kafka-storage.sh random-uuid)
echo "生成的 Cluster UUID: $CLUSTER_UUID"
# 格式化 3 个节点
for i in 1 2 3; do
echo "正在格式化 node${i}..."
$KAFKA_HOME/bin/kafka-storage.sh format \
-t $CLUSTER_UUID \
-c $KRAFT_CONFIG_DIR/server-node${i}.properties
if [ $? -eq 0 ]; then
echo "node${i} 格式化成功"
else
echo "node${i} 格式化失败!"
exit 1
fi
done
echo "所有节点格式化完成,Cluster UUID: $CLUSTER_UUID"
启动集群
手动逐节点启动
按顺序依次启动 3 个节点,使用 -daemon 参数后台运行:
bash
# 启动 node1
/usr/local/kafka/bin/kafka-server-start.sh \
-daemon \
/usr/local/kafka/config/kraft/server-node1.properties
# 等待 node1 启动完成(约 5-10 秒)
sleep 5
# 启动 node2
/usr/local/kafka/bin/kafka-server-start.sh \
-daemon \
/usr/local/kafka/config/kraft/server-node2.properties
# 等待 node2 启动完成
sleep 5
# 启动 node3
/usr/local/kafka/bin/kafka-server-start.sh \
-daemon \
/usr/local/kafka/config/kraft/server-node3.properties
一键启动脚本
文件路径: /home/admin/start-cluster.sh
bash
#!/bin/bash
# Kafka KRaft 3 节点集群一键启动脚本
KAFKA_HOME=/usr/local/kafka
KRAFT_CONFIG_DIR=$KAFKA_HOME/config/kraft
LOG_FILE=/home/admin/kafka-cluster-start.log
echo "========================================" | tee $LOG_FILE
echo "Kafka KRaft 集群启动" | tee -a $LOG_FILE
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a $LOG_FILE
echo "========================================" | tee -a $LOG_FILE
# 启动函数
start_node() {
local node_id=$1
local config_file=$KRAFT_CONFIG_DIR/server-node${node_id}.properties
echo "启动 node${node_id}..." | tee -a $LOG_FILE
$KAFKA_HOME/bin/kafka-server-start.sh -daemon $config_file
if [ $? -eq 0 ]; then
echo " node${node_id} 启动命令已发送" | tee -a $LOG_FILE
else
echo " node${node_id} 启动失败!" | tee -a $LOG_FILE
return 1
fi
# 等待节点启动
sleep 5
return 0
}
# 按顺序启动 3 个节点
start_node 1
start_node 2
start_node 3
# 等待集群完成 Controller 选举
echo "等待 Controller 选举完成..." | tee -a $LOG_FILE
sleep 10
# 验证启动结果
echo "" | tee -a $LOG_FILE
echo "=== 进程检查 (jps) ===" | tee -a $LOG_FILE
jps -l | grep -i kafka | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "=== 端口检查 ===" | tee -a $LOG_FILE
ss -tlnp | grep -E '9092|9093|9192|9193|9292|9293' | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "=== 集群仲裁状态 ===" | tee -a $LOG_FILE
$KAFKA_HOME/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
describe --status | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "集群启动完成,日志已保存至 $LOG_FILE"
赋予执行权限并运行:
bash
# 赋予执行权限
chmod +x /home/admin/start-cluster.sh
# 执行启动脚本
/home/admin/start-cluster.sh
启动顺序说明
- 先启动 node1:作为第一个 Controller,node1 会发起 Raft 选举并成为初始 Leader
- 再启动 node2:加入仲裁集群,参与投票
- 最后启动 node3:加入后集群达到 3 节点,满足多数派(2/3)条件,仲裁稳定
注意:虽然启动顺序不影响最终集群状态,但按顺序启动可以减少选举抖动,便于观察启动过程。
启动后验证
bash
# 检查 Kafka 进程(应看到 3 个 Kafka 进程)
jps -l | grep kafka
# 预期输出(类似):
# 12345 kafka.Kafka
# 12346 kafka.Kafka
# 12347 kafka.Kafka
# 检查端口监听状态(应看到 6 个端口)
ss -tlnp | grep -E '9092|9093|9192|9193|9292|9293'
# 预期输出(类似):
# LISTEN 0 50 0.0.0.0:9092 ...
# LISTEN 0 50 0.0.0.0:9093 ...
# LISTEN 0 50 0.0.0.0:9192 ...
# LISTEN 0 50 0.0.0.0:9193 ...
# LISTEN 0 50 0.0.0.0:9292 ...
# LISTEN 0 50 0.0.0.0:9293 ...
# 查看集群仲裁状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
describe --status
集群验证
1. 创建测试 Topic
bash
# 创建 3 分区、3 副本的测试 Topic
/usr/local/kafka/bin/kafka-topics.sh \
--create \
--topic test-topic \
--partitions 3 \
--replication-factor 3 \
--bootstrap-server 192.168.1.1:9092
2. 查看 Topic 列表和详情
bash
# 查看所有 Topic 列表
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.1:9092
# 查看 Topic 详细信息(分区、副本分布、Leader)
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic test-topic \
--bootstrap-server 192.168.1.1:9092
# 查看所有 Topic 详情
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--bootstrap-server 192.168.1.1:9092
3. 生产消息(Console Producer)
bash
# 启动控制台生产者,发送消息到 test-topic
/usr/local/kafka/bin/kafka-console-producer.sh \
--topic test-topic \
--bootstrap-server 192.168.1.1:9092
# 在交互式命令行中输入消息(每行一条),例如:
# Hello Kafka KRaft Cluster!
# This is message 2
# This is message 3
# 按 Ctrl+C 退出
4. 消费消息(Console Consumer)
bash
# 启动控制台消费者,从头开始消费 test-topic 的消息
/usr/local/kafka/bin/kafka-console-consumer.sh \
--topic test-topic \
--from-beginning \
--bootstrap-server 192.168.1.1:9092
# 或消费指定消费者组的消息
/usr/local/kafka/bin/kafka-console-consumer.sh \
--topic test-topic \
--group test-consumer-group \
--bootstrap-server 192.168.1.1:9092
5. 查看仲裁状态
bash
# 查看集群元数据仲裁状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
describe --status
# 查看仲裁成员详情(包含各节点角色和状态)
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
describe --members
# 查看仲裁复制状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
describe --replication
6. 查看各节点 Controller 状态
bash
# 查看 Broker 列表及 Controller 信息
/usr/local/kafka/bin/kafka-broker-api-versions.sh \
--bootstrap-server 192.168.1.1:9092
# 通过 describe 查看 Topic 的 Leader 和 ISR 分布
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--bootstrap-server 192.168.1.1:9092 \
--topic test-topic
# 查看 __consumer_offsets 内部 Topic 状态(验证副本分布)
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--bootstrap-server 192.168.1.1:9092 \
--topic __consumer_offsets
停止集群
手动逐节点停止
bash
# 停止 node1
/usr/local/kafka/bin/kafka-server-stop.sh \
/usr/local/kafka/config/kraft/server-node1.properties
# 等待 node1 完全停止
sleep 3
# 停止 node2
/usr/local/kafka/bin/kafka-server-stop.sh \
/usr/local/kafka/config/kraft/server-node2.properties
# 等待 node2 完全停止
sleep 3
# 停止 node3
/usr/local/kafka/bin/kafka-server-stop.sh \
/usr/local/kafka/config/kraft/server-node3.properties
注意 :
kafka-server-stop.sh默认会匹配所有 Kafka 进程。若需精确停止特定节点,可通过指定配置文件路径区分,或在伪分布式环境中先获取各进程 PID 再精确 kill。
通过 PID 精确停止(推荐用于伪分布式):
bash
# 获取所有 Kafka 进程 PID
jps -l | grep kafka.Kafka
# 根据启动顺序和端口确认各节点 PID 后,逐个停止
# kill <node1_pid>
# kill <node2_pid>
# kill <node3_pid>
# 或使用以下脚本自动停止所有 Kafka 进程
jps -l | grep kafka.Kafka | awk '{print $1}' | xargs -r kill -15
# 确认进程已停止
sleep 5
jps -l | grep kafka
一键停止脚本
文件路径: /home/admin/stop-cluster.sh
bash
#!/bin/bash
# Kafka KRaft 3 节点集群一键停止脚本
KAFKA_HOME=/usr/local/kafka
LOG_FILE=/home/admin/kafka-cluster-stop.log
echo "========================================" | tee $LOG_FILE
echo "Kafka KRaft 集群停止" | tee -a $LOG_FILE
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a $LOG_FILE
echo "========================================" | tee -a $LOG_FILE
# 获取所有 Kafka 进程 PID
PIDS=$(jps -l | grep kafka.Kafka | awk '{print $1}')
if [ -z "$PIDS" ]; then
echo "没有运行中的 Kafka 进程" | tee -a $LOG_FILE
exit 0
fi
echo "当前 Kafka 进程 PID: $PIDS" | tee -a $LOG_FILE
# 逐个发送 SIGTERM 信号优雅停止
for PID in $PIDS; do
echo "正在停止进程 PID: $PID..." | tee -a $LOG_FILE
kill -15 $PID
if [ $? -eq 0 ]; then
echo " 进程 $PID 已发送停止信号" | tee -a $LOG_FILE
else
echo " 进程 $PID 停止失败!" | tee -a $LOG_FILE
fi
done
# 等待进程退出
echo "等待进程退出..." | tee -a $LOG_FILE
for i in $(seq 1 30); do
REMAINING=$(jps -l | grep kafka.Kafka | awk '{print $1}')
if [ -z "$REMAINING" ]; then
echo "所有 Kafka 进程已停止" | tee -a $LOG_FILE
break
fi
sleep 2
done
# 检查是否仍有残留进程
REMAINING=$(jps -l | grep kafka.Kafka | awk '{print $1}')
if [ -n "$REMAINING" ]; then
echo "警告: 以下进程未正常退出,将强制终止: $REMAINING" | tee -a $LOG_FILE
for PID in $REMAINING; do
kill -9 $PID
echo " 强制终止进程 $PID" | tee -a $LOG_FILE
done
sleep 3
fi
echo "" | tee -a $LOG_FILE
echo "=== 最终进程检查 ===" | tee -a $LOG_FILE
jps -l | grep -i kafka | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "集群停止完成,日志已保存至 $LOG_FILE"
赋予执行权限并运行:
bash
# 赋予执行权限
chmod +x /home/admin/stop-cluster.sh
# 执行停止脚本
/home/admin/stop-cluster.sh
常用运维操作
Topic 管理
bash
# 创建 Topic(指定分区数和副本数)
/usr/local/kafka/bin/kafka-topics.sh \
--create \
--topic my-topic \
--partitions 3 \
--replication-factor 3 \
--bootstrap-server 192.168.1.1:9092
# 查看 Topic 列表
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.1:9092
# 查看 Topic 详情
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092
# 查看 Topic 配置
/usr/local/kafka/bin/kafka-configs.sh \
--describe \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092
# 删除 Topic(需确保 delete.topic.enable=true,默认已开启)
/usr/local/kafka/bin/kafka-topics.sh \
--delete \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092
分区扩容
bash
# 将 my-topic 分区数从 3 扩展到 6(只能增加,不能减少)
/usr/local/kafka/bin/kafka-topics.sh \
--alter \
--topic my-topic \
--partitions 6 \
--bootstrap-server 192.168.1.1:9092
# 验证分区扩展结果
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092
消费者组管理
bash
# 查看所有消费者组
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--list \
--bootstrap-server 192.168.1.1:9092
# 查看消费者组详情(成员、分区分配、消费位移)
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--describe \
--group test-consumer-group \
--bootstrap-server 192.168.1.1:9092
# 重置消费者位移到最早(需先停止消费者)
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--reset-offsets \
--group test-consumer-group \
--topic test-topic \
--to-earliest \
--execute \
--bootstrap-server 192.168.1.1:9092
# 重置消费者位移到最新
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--reset-offsets \
--group test-consumer-group \
--topic test-topic \
--to-latest \
--execute \
--bootstrap-server 192.168.1.1:9092
# 重置消费者位移到指定时间
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--reset-offsets \
--group test-consumer-group \
--topic test-topic \
--to-datetime 2024-01-01T00:00:00.000 \
--execute \
--bootstrap-server 192.168.1.1:9092
# 删除消费者组
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--delete \
--group test-consumer-group \
--bootstrap-server 192.168.1.1:9092
日志清理与磁盘监控
bash
# 查看各节点数据目录磁盘占用
du -sh /tmp/kafka-kraft-node1 /tmp/kafka-kraft-node2 /tmp/kafka-kraft-node3
# 查看磁盘整体使用情况
df -h /tmp
# 查看指定 Topic 的日志段文件
ls -lh /tmp/kafka-kraft-node1/test-topic-*/
ls -lh /tmp/kafka-kraft-node2/test-topic-*/
ls -lh /tmp/kafka-kraft-node3/test-topic-*/
# 查看 Topic 的消息数(通过获取最早和最新 offset 计算)
/usr/local/kafka/bin/kafka-get-offsets.sh \
--topic test-topic \
--bootstrap-server 192.168.1.1:9092
# 修改 Topic 日志保留时间(动态配置,无需重启)
/usr/local/kafka/bin/kafka-configs.sh \
--alter \
--topic test-topic \
--add-config retention.hours=24 \
--bootstrap-server 192.168.1.1:9092
# 手动触发日志段滚动(关闭当前活跃日志段,使其可被清理)
/usr/local/kafka/bin/kafka-logs.sh \
--topic test-topic \
--bootstrap-server 192.168.1.1:9092
# 查看 Kafka 运行日志
tail -100f /usr/local/kafka/logs/server.log
# 查看 Controller 日志
tail -100f /usr/local/kafka/logs/controller.log
集群健康检查
bash
# 查看集群元数据仲裁状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
describe --status
# 查看仲裁成员
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
describe --members
# 查看 Broker API 版本(确认各节点在线状态)
/usr/local/kafka/bin/kafka-broker-api-versions.sh \
--bootstrap-server 192.168.1.1:9092
# 通过不同节点 Bootstrap 验证集群连通性
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.1:9192
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.1:9292
常见问题排查
Q1: 节点启动失败,日志报端口冲突
现象: 启动节点时进程立即退出,日志中出现 Address already in use 或 BindException 错误。
排查与解决:
bash
# 检查端口占用情况
ss -tlnp | grep -E '9092|9093|9192|9193|9292|9293'
# 如果端口被旧进程占用,查找并终止
jps -l | grep kafka
kill -15 <PID>
# 确认端口空闲后重新启动
/usr/local/kafka/bin/kafka-server-start.sh \
-daemon \
/usr/local/kafka/config/kraft/server-node1.properties
常见原因:
- 上次 Kafka 未正确停止,残留进程占用端口
- 多个节点配置了相同端口(检查
listeners配置是否各节点不同) - 其他服务占用了 9092/9192/9292 等端口
Q2: 节点启动失败,报 Cluster UUID 不一致
现象: 日志中出现 Cluster UUID doesn't match stored UUID 或 The cluster id in the metadata log doesn't match 错误。
排查与解决:
bash
# 检查各节点 meta.properties 中的 cluster.id
cat /tmp/kafka-kraft-node1/meta.properties
cat /tmp/kafka-kraft-node2/meta.properties
cat /tmp/kafka-kraft-node3/meta.properties
# 如果 cluster.id 不一致,需要重新格式化
# 1. 停止所有节点
/home/admin/stop-cluster.sh
# 2. 清理所有节点数据目录
rm -rf /tmp/kafka-kraft-node1/*
rm -rf /tmp/kafka-kraft-node2/*
rm -rf /tmp/kafka-kraft-node3/*
# 3. 重新生成 UUID 并统一格式化
CLUSTER_UUID=$(/usr/local/kafka/bin/kafka-storage.sh random-uuid)
echo $CLUSTER_UUID
/usr/local/kafka/bin/kafka-storage.sh format -t $CLUSTER_UUID -c /usr/local/kafka/config/kraft/server-node1.properties
/usr/local/kafka/bin/kafka-storage.sh format -t $CLUSTER_UUID -c /usr/local/kafka/config/kraft/server-node2.properties
/usr/local/kafka/bin/kafka-storage.sh format -t $CLUSTER_UUID -c /usr/local/kafka/config/kraft/server-node3.properties
# 4. 重新启动
/home/admin/start-cluster.sh
Q3: Controller 选举失败,集群无法形成仲裁
现象: 节点启动后日志中反复出现 Unable to begin voting 或 No leader found 等信息,kafka-metadata-quorum.sh describe --status 无法返回结果。
排查与解决:
bash
# 1. 检查 controller.quorum.voters 配置是否正确
grep controller.quorum.voters /usr/local/kafka/config/kraft/server-node*.properties
# 3 个节点的配置必须完全一致:
# controller.quorum.voters=1@localhost:9093,2@localhost:9193,3@localhost:9293
# 2. 检查 Controller 端口是否可达
ss -tlnp | grep -E '9093|9193|9293'
# 3. 检查 node.id 是否与 controller.quorum.voters 中的 ID 对应
grep node.id /usr/local/kafka/config/kraft/server-node*.properties
# node1 应为 node.id=1, node2 为 node.id=2, node3 为 node.id=3
# 4. 检查 process.roles 是否包含 controller
grep process.roles /usr/local/kafka/config/kraft/server-node*.properties
# 应为 process.roles=broker,controller
# 5. 检查 Controller 监听器配置
grep controller.listener.names /usr/local/kafka/config/kraft/server-node*.properties
# 应为 controller.listener.names=CONTROLLER
# 6. 确认至少 2 个节点已启动(3 节点集群需多数派)
jps -l | grep kafka | wc -l
Q4: 生产者连接超时,报 Connection to node could not be established
现象: 生产者或消费者连接 Kafka 时报 Connection to node -1 could not be established. Broker may not be available 或 TimeoutException。
排查与解决:
bash
# 1. 检查 advertised.listeners 配置是否为实际 IP
grep advertised.listeners /usr/local/kafka/config/kraft/server-node*.properties
# 正确配置应为 192.168.1.1(非 localhost):
# node1: advertised.listeners=PLAINTEXT://192.168.1.1:9092
# node2: advertised.listeners=PLAINTEXT://192.168.1.1:9192
# node3: advertised.listeners=PLAINTEXT://192.168.1.1:9292
# 如果配置为 localhost,外部客户端无法连接
# 修改后需重启对应节点
# 2. 验证端口可达性
telnet 192.168.1.1 9092
telnet 192.168.1.1 9192
telnet 192.168.1.1 9292
# 3. 检查防火墙是否放行端口
# UnionTech OS 可能使用 firewalld 或 ufw
systemctl status firewalld 2>/dev/null
firewall-cmd --list-ports 2>/dev/null
# 如需放行端口
firewall-cmd --permanent --add-port=9092/tcp
firewall-cmd --permanent --add-port=9093/tcp
firewall-cmd --permanent --add-port=9192/tcp
firewall-cmd --permanent --add-port=9193/tcp
firewall-cmd --permanent --add-port=9292/tcp
firewall-cmd --permanent --add-port=9293/tcp
firewall-cmd --reload
Q5: Topic 创建失败,报副本数不足
现象: 创建 Topic 时报 Replication factor: 3 larger than available brokers: 1 或类似错误。
排查与解决:
bash
# 1. 检查当前在线 Broker 数量
/usr/local/kafka/bin/kafka-broker-api-versions.sh \
--bootstrap-server 192.168.1.1:9092
# 如果只看到 1 个 Broker,说明其他节点未正常启动或未加入集群
# 2. 检查所有节点进程
jps -l | grep kafka
# 3. 检查未加入集群节点的日志
tail -100 /usr/local/kafka/logs/server.log
# 4. 确认所有节点的 controller.quorum.voters 配置一致
grep controller.quorum.voters /usr/local/kafka/config/kraft/server-node*.properties
# 5. 确认 offsets.topic.replication.factor 不超过在线 Broker 数
grep offsets.topic.replication.factor /usr/local/kafka/config/kraft/server-node1.properties
# 集群 3 节点应设为 3,但需 3 个节点全部在线
Q6: 磁盘空间不足告警
现象: 日志中出现 Disk usage exceeded 或 Kafka 写入变慢,df -h 显示磁盘使用率过高。
排查与解决:
bash
# 1. 检查磁盘使用情况
df -h /tmp
# 2. 查看各节点数据目录大小
du -sh /tmp/kafka-kraft-node1 /tmp/kafka-kraft-node2 /tmp/kafka-kraft-node3
# 3. 查看各 Topic 占用空间
du -sh /tmp/kafka-kraft-node1/* | sort -rh | head -20
# 4. 调整日志保留策略(缩短保留时间)
/usr/local/kafka/bin/kafka-configs.sh \
--alter \
--topic <topic-name> \
--add-config retention.hours=24 \
--bootstrap-server 192.168.1.1:9092
# 5. 调整日志段大小(减小单个日志段文件)
/usr/local/kafka/bin/kafka-configs.sh \
--alter \
--topic <topic-name> \
--add-config segment.bytes=536870912 \
--bootstrap-server 192.168.1.1:9092
# 6. 手动删除不再需要的 Topic
/usr/local/kafka/bin/kafka-topics.sh \
--delete \
--topic <topic-name> \
--bootstrap-server 192.168.1.1:9092
# 7. 全局调整 log.retention.hours(需修改配置文件并重启)
# 编辑 server-node1/2/3.properties:
# log.retention.hours=24 # 从默认 168 小时缩短为 24 小时
Q7: 节点日志目录权限不足
现象: 启动时报 Permission denied 或 Unable to create directory 错误。
排查与解决:
bash
# 1. 检查数据目录权限
ls -ld /tmp/kafka-kraft-node1 /tmp/kafka-kraft-node2 /tmp/kafka-kraft-node3
# 2. 确保运行 Kafka 的用户对目录有读写权限
# 如果以当前用户运行
chown -R $(whoami) /tmp/kafka-kraft-node1 /tmp/kafka-kraft-node2 /tmp/kafka-kraft-node3
# 3. 设置适当权限
chmod 755 /tmp/kafka-kraft-node1 /tmp/kafka-kraft-node2 /tmp/kafka-kraft-node3
# 4. 检查 Kafka 安装目录权限
ls -ld /usr/local/kafka
ls -ld /usr/local/kafka/config/kraft/
# 5. 确保配置文件可读
chmod 644 /usr/local/kafka/config/kraft/server-node*.properties
Q8: 消费者消费延迟(Lag)过大
现象: 消费者组消费速度跟不上生产速度,kafka-consumer-groups.sh --describe 显示 LAG 值持续增大。
排查与解决:
bash
# 1. 查看消费者组 Lag 情况
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--describe \
--group <group-name> \
--bootstrap-server 192.168.1.1:9092
# 2. 查看 Topic 各分区的消息速率
/usr/local/kafka/bin/kafka-run-class.sh \
kafka.tools.GetOffsetShell \
--topic <topic-name> \
--bootstrap-server 192.168.1.1:9092
# 3. 检查分区分布是否均匀
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic <topic-name> \
--bootstrap-server 192.168.1.1:9092
# 4. 如果分区数不足,扩展分区以提升并行度
/usr/local/kafka/bin/kafka-topics.sh \
--alter \
--topic <topic-name> \
--partitions 6 \
--bootstrap-server 192.168.1.1:9092
# 5. 增加消费者实例数(不超过分区数)
# 在另一终端启动新的消费者实例,使用相同的 group.id
# 6. 检查消费者处理逻辑是否有耗时阻塞操作
# 优化消费逻辑,减少每条消息的处理时间
附录:端口分配速查表
节点 node.id Broker 端口 Controller 端口 数据目录 配置文件 node1 1 9092 9093 /tmp/kafka-kraft-node1 /usr/local/kafka/config/kraft/server-node1.properties node2 2 9192 9193 /tmp/kafka-kraft-node2 /usr/local/kafka/config/kraft/server-node2.properties node3 3 9292 9293 /tmp/kafka-kraft-node3 /usr/local/kafka/config/kraft/server-node3.properties 脚本文件清单:
脚本 路径 功能 start-cluster.sh /home/admin/start-cluster.sh 一键启动 3 节点集群 stop-cluster.sh /home/admin/stop-cluster.sh 一键停止 3 节点集群 format-cluster.sh /home/admin/format-cluster.sh 一键格式化 3 节点目录