UOS系统 Kafka KRaft 集群安装指南

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

启动顺序说明

  1. 先启动 node1:作为第一个 Controller,node1 会发起 Raft 选举并成为初始 Leader
  2. 再启动 node2:加入仲裁集群,参与投票
  3. 最后启动 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 useBindException 错误。

排查与解决:

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 UUIDThe 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 votingNo 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 availableTimeoutException

排查与解决:

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 deniedUnable 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 节点目录
相关推荐
rannn_1111 小时前
Java 后端面试题总结:点赞功能怎么实现?
java·后端·面试
__zRainy__1 小时前
Node系列 · 数据库:MySQL 安装
数据库·后端·mysql·node.js
Zane19941 小时前
threading、multiprocessing、asyncio 到底怎么选?一张图 + 三组实测数据说清楚
后端·python
程序员cxuan1 小时前
DeepSeek 多模态深度实测,确实有东西的
人工智能·后端·程序员
evans在进步2 小时前
Spring Boot 核心机制详解:自动装配、事件监听、异步任务与请求映射
java·spring boot·后端
程序员cxuan2 小时前
一招让你的 Claude 被封了也能用
人工智能·后端·程序员
Zane19942 小时前
HashSet、TreeSet、LinkedHashSet:底层都是"套壳"
java·后端
sigterM先生2 小时前
Ollama + Go 搭建 AI 告警分析管道
后端·ai编程
AI多Agent协作实战派2 小时前
AI多Agent协作系统实战(四十九):它说唤醒了agent,agent却还在睡觉
后端