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 台独立物理机器上部署 3 个 Kafka KRaft 节点,构成真分布式集群。每台机器运行一个 broker+controller 联合节点,各节点使用统一的端口(9092/9093),通过不同 IP 地址区分。
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ node1 │ │ node2 │ │ node3 │
│ 192.168.1.1 │ │ 192.168.1.2 │ │ 192.168.1.3 │
│ 用户: admin │ │ 用户: root │ │ 用户: root │
│ │ │ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ id=1 │ │ │ │ id=2 │ │ │ │ id=3 │ │
│ │ B:9092 │ │ │ │ B:9092 │ │ │ │ B:9092 │ │
│ │ C:9093 │ │ │ │ C:9093 │ │ │ │ C:9093 │ │
│ └──────┬───────┘ │ │ └──────┬───────┘ │ │ └──────┬───────┘ │
│ │ │ │ │ │ │ │ │
│ /tmp/kafka-kraft-logs│ │ /tmp/kafka-kraft-logs│ │ /tmp/kafka-kraft-logs│
└──────────┼───────────┘ └──────────┼───────────┘ └──────────┼───────────┘
│ │ │
└─────────── Raft 仲裁协议(跨网络通信)───────────┘
B = Broker 端口 C = Controller 端口
每个节点均配置为 broker,controller 联合模式,3 个 Controller 节点跨机器组成 Raft 仲裁集群。与伪分布式不同,各节点通过不同 IP 寻址,端口统一为 9092(Broker)和 9093(Controller),无需使用不同端口区分。
节点角色说明
| 节点 | IP | 用户名 | 角色 | 说明 |
|---|---|---|---|---|
| node1 | 192.168.1.1 | admin | broker+controller | 本机,已安装 Kafka 3.8.1 + Java 21 |
| node2 | 192.168.1.2 | root | broker+controller | 远程机器 (v1) |
| node3 | 192.168.1.3 | root | broker+controller | 远程机器 (Uos) |
与伪分布式部署的关键差异
| 对比项 | 伪分布式(单机多节点) | 真分布式(多机多节点) |
|---|---|---|
| 节点运行位置 | 同一台机器 | 3 台独立机器 |
| 端口分配 | 每节点不同端口(9092/9192/9292) | 端口统一(9092/9093),IP 不同 |
| controller.quorum.voters | 1@localhost:9093,2@localhost:9193,3@localhost:9293 | 1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093 |
| 配置文件路径 | 每节点独立文件(server-node1/2/3.properties) | 每台机器统一路径(server.properties) |
| 数据目录 | 带节点后缀(/tmp/kafka-kraft-node1/2/3) | 统一路径(/tmp/kafka-kraft-logs) |
| advertised.listeners | 均为本机 IP + 不同端口 | 各节点使用各自实际可达 IP + 统一端口 |
| 额外操作 | 无 | 需 SSH 免密、hosts 映射、Kafka 分发、Java 环境确认 |
| 网络通信 | localhost 回环 | 跨机器真实网络,需确保网络连通和防火墙放行 |
安装方式说明:本指南中,Java 和 Kafka 的远程安装采用原始 tar.gz 安装包方式(从本机 /tmp/gw_data/ 上传到远程机器),而非打包本机已安装目录。这确保远程机器获得完整的官方发行版目录结构,且安装路径与本机保持一致(Java:
/usr/local/java/jdk-21.0.10+7,Kafka:/usr/local/kafka)。
前置条件检查
1. Java 版本检查
Kafka 3.8.x 要求 Java 17+,本机已安装 Java 21,远程机器需确认 Java 环境。
# 本机 (node1) 检查 Java 版本
java -version
# 验证 JAVA_HOME 环境变量
echo $JAVA_HOME
# 远程 node2 检查 Java 版本
ssh root@192.168.1.2 "java -version"
# 远程 node3 检查 Java 版本
ssh root@192.168.1.3 "java -version"
要求:3 台机器均需安装 Java 17 或更高版本。若远程机器未安装 Java,参见 Kafka 安装包分发 章节中的 Java 安装步骤。
2. 网络连通性测试
确保 3 台机器之间网络互通:
# 从 node1 测试到 node2
ping -c 3 192.168.1.2
# 从 node1 测试到 node3
ping -c 3 192.168.1.3
# 从 node2 测试到 node3(需在 node2 上执行)
ssh root@192.168.1.2 "ping -c 3 192.168.1.3"
# 从 node2 测试到 node1
ssh root@192.168.1.2 "ping -c 3 192.168.1.1"
# 从 node3 测试到 node1
ssh root@192.168.1.3 "ping -c 3 192.168.1.1"
# 从 node3 测试到 node2
ssh root@192.168.1.3 "ping -c 3 192.168.1.2"
预期结果:所有 ping 均应正常响应,延迟在可接受范围内(通常 < 1ms 局域网)。
3. 端口可用性检查
确保 3 台机器的 9092 和 9093 端口均未被占用:
# 本机 (node1) 检查端口
ss -tlnp | grep -E '9092|9093'
# 远程 node2 检查端口
ssh root@192.168.1.2 "ss -tlnp | grep -E '9092|9093'"
# 远程 node3 检查端口
ssh root@192.168.1.3 "ss -tlnp | grep -E '9092|9093'"
预期结果:以上命令应无输出,表示端口空闲。若有输出,说明端口被占用,需先停止占用进程或调整配置。
4. 磁盘空间检查
Kafka 数据目录需要足够的磁盘空间:
# 本机 (node1) 检查磁盘
df -h /tmp
# 远程 node2 检查磁盘
ssh root@192.168.1.2 "df -h /tmp"
# 远程 node3 检查磁盘
ssh root@192.168.1.3 "df -h /tmp"
建议:每台机器 /tmp 分区至少有 10GB 可用空间。
5. 本机 Kafka 安装路径确认
# 确认 Kafka 安装目录存在
ls -la /usr/local/kafka/bin/ | head -20
# 确认 Kafka 版本
/usr/local/kafka/bin/kafka-storage.sh version
# 确认关键脚本可用
ls /usr/local/kafka/bin/kafka-server-start.sh
ls /usr/local/kafka/bin/kafka-server-stop.sh
ls /usr/local/kafka/bin/kafka-storage.sh
ls /usr/local/kafka/bin/kafka-topics.sh
预期结果:Kafka 安装路径为
/usr/local/kafka,版本为 3.8.1,所有关键脚本文件均存在。
防火墙端口放行
多机部署中,Kafka 的 Broker 端口(9092)和 Controller 端口(9093)必须被各节点防火墙放行,否则会导致节点间无法通信、客户端无法连接等问题。
检查远程防火墙状态
# 检查 node2 防火墙状态
ssh root@192.168.1.2 "firewall-cmd --state" 2>/dev/null
# 检查 node3 防火墙状态
ssh root@192.168.1.3 "firewall-cmd --state" 2>/dev/null
预期结果:输出
running表示防火墙已开启,需要放行端口;输出not running或命令不存在表示防火墙未启用,无需额外操作。
使用 firewall-cmd 放行端口(firewalld)
若防火墙状态为 running,执行以下命令放行 Broker 和 Controller 端口:
# 放行 node2 端口
ssh root@192.168.1.2 "firewall-cmd --permanent --add-port=9092/tcp && firewall-cmd --permanent --add-port=9093/tcp && firewall-cmd --reload"
# 放行 node3 端口
ssh root@192.168.1.3 "firewall-cmd --permanent --add-port=9092/tcp && firewall-cmd --permanent --add-port=9093/tcp && firewall-cmd --reload"
注意:本机(node1)若也启用了防火墙,需在本地执行相同命令(无需 ssh)。
使用 iptables 放行端口(无 firewalld)
若系统使用 iptables 而非 firewalld,使用以下命令:
# 放行 node2 端口
ssh root@192.168.1.2 "iptables -I INPUT -p tcp --dport 9092 -j ACCEPT && iptables -I INPUT -p tcp --dport 9093 -j ACCEPT && iptables-save > /etc/iptables.rules"
# 放行 node3 端口
ssh root@192.168.1.3 "iptables -I INPUT -p tcp --dport 9092 -j ACCEPT && iptables -I INPUT -p tcp --dport 9093 -j ACCEPT && iptables-save > /etc/iptables.rules"
验证端口放行结果
# 验证 node2 端口放行
ssh root@192.168.1.2 "firewall-cmd --list-ports" 2>/dev/null
# 验证 node3 端口放行
ssh root@192.168.1.3 "firewall-cmd --list-ports" 2>/dev/null
预期结果:输出中应包含
9092/tcp和9093/tcp,表示端口已成功放行。
SSH 免密登录配置
多机部署需要频繁通过 SSH 在各节点间执行命令,配置免密登录可避免每次输入密码,大幅提升操作效率。
1. 本机生成 SSH 密钥对
在 node1(本机)以 admin 用户身份生成密钥对:
# 生成 RSA 密钥对(默认 3072 位,若已有密钥可跳过)
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N ""
# 验证密钥文件已生成
ls -la ~/.ssh/id_rsa ~/.ssh/id_rsa.pub
说明:
-N ""表示不设置密钥密码(空密码),适合自动化场景。若需要更高安全性,可设置密码短语并通过ssh-agent管理。
2. 分发公钥到远程机器
将本机公钥复制到 node2 和 node3 的授权密钥文件中:
# 分发公钥到 node2(root 用户)
# 首次连接将提示输入远程用户密码
ssh-copy-id root@192.168.1.2
# 分发公钥到 node3(root 用户)
# 首次连接将提示输入远程用户密码
ssh-copy-id root@192.168.1.3
注意:
ssh-copy-id执行时,首次连接会提示Are you sure you want to continue connecting (yes/no)?,输入yes后再输入远程用户密码。
3. 验证免密登录
# 验证到 node2 免密登录
ssh root@192.168.1.2 "hostname && whoami"
# 验证到 node3 免密登录
ssh root@192.168.1.3 "hostname && whoami"
预期结果:无需输入密码即可登录,并输出远程机器的主机名和当前用户名。
4. 常见问题
首次连接 known_hosts 确认
首次 SSH 连接新主机时,会提示确认主机指纹:
The authenticity of host '192.168.1.2 (192.168.1.2)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxx.
Are you sure you want to continue connecting (yes/no)?
输入 yes 即可,主机信息会被记录到 ~/.ssh/known_hosts,后续连接不再提示。
SSH 目录权限问题
SSH 对目录权限要求严格,权限不正确会导致免密登录失败:
# 在远程机器上检查并修复权限(若免密登录不生效)
ssh root@192.168.1.2 "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
ssh root@192.168.1.3 "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
# 本机也需确认权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
权限要求:
~/.ssh目录:700(drwx------)~/.ssh/authorized_keys文件:600(-rw-------)~/.ssh/id_rsa私钥文件:600(-rw-------)~/.ssh/id_rsa.pub公钥文件:644(-rw-r--r--)
hosts 主机名映射配置
为方便操作和配置,为 3 台机器定义主机名映射,使主机名可直接解析为对应 IP。
1. 主机名定义
| 主机名 | IP 地址 | 对应节点 |
|---|---|---|
| kafka-node1 | 192.168.1.1 | node1 |
| kafka-node2 | 192.168.1.2 | node2 |
| kafka-node3 | 192.168.1.3 | node3 |
2. 配置 hosts 映射
在 3 台机器的 /etc/hosts 文件中追加映射内容:
# 需要追加的 hosts 内容
192.168.1.1 kafka-node1
192.168.1.2 kafka-node2
192.168.1.3 kafka-node3
在本机 (node1) 配置
# 追加 hosts 映射(需 sudo 权限)
sudo bash -c 'cat >> /etc/hosts << EOF
192.168.1.1 kafka-node1
192.168.1.2 kafka-node2
192.168.1.3 kafka-node3
EOF'
# 验证本机 hosts 配置
tail -3 /etc/hosts
在远程 node2 配置
# 通过 SSH 远程追加 hosts 映射
ssh root@192.168.1.2 "bash -c 'cat >> /etc/hosts << EOF
192.168.1.1 kafka-node1
192.168.1.2 kafka-node2
192.168.1.3 kafka-node3
EOF'"
# 验证远程 hosts 配置
ssh root@192.168.1.2 "tail -3 /etc/hosts"
在远程 node3 配置
# 通过 SSH 远程追加 hosts 映射
ssh root@192.168.1.3 "bash -c 'cat >> /etc/hosts << EOF
192.168.1.1 kafka-node1
192.168.1.2 kafka-node2
192.168.1.3 kafka-node3
EOF'"
# 验证远程 hosts 配置
ssh root@192.168.1.3 "tail -3 /etc/hosts"
注意:追加前可先检查是否已存在相同映射,避免重复添加:
grep kafka-node /etc/hosts
3. 验证 hosts 解析
# 在本机验证解析
ping -c 1 kafka-node1
ping -c 1 kafka-node2
ping -c 1 kafka-node3
# 在远程 node2 验证解析
ssh root@192.168.1.2 "ping -c 1 kafka-node1 && ping -c 1 kafka-node2 && ping -c 1 kafka-node3"
# 在远程 node3 验证解析
ssh root@192.168.1.3 "ping -c 1 kafka-node1 && ping -c 1 kafka-node2 && ping -c 1 kafka-node3"
预期结果:使用主机名
kafka-node1/kafka-node2/kafka-node3可正常 ping 通对应 IP 地址。
Kafka 安装包分发
本机已安装 Kafka 3.8.1 和 Java 21,需要将安装包分发到两台远程机器。
说明:实际安装使用原始 tar.gz 安装包(而非打包本机已安装目录),确保远程机器获得完整的官方发行版目录结构。
1. 安装包信息
安装包位于本机 /tmp/gw_data/ 目录:
| 软件包 | 文件名 | 大小 | 来源路径 |
|---|---|---|---|
| Kafka 3.8.1 | kafka_2.13-3.8.1.tgz | 116MB | /tmp/gw_data/kafka_2.13-3.8.1.tgz |
| OpenJDK 21 | OpenJDK21U-jdk_x64_linux_hotspot_21.0.10_7.tar.gz | 198MB | /tmp/gw_data/OpenJDK21U-jdk_x64_linux_hotspot_21.0.10_7.tar.gz |
2. SCP 分发安装包到远程机器
# 在远程机器创建目标目录
ssh root@192.168.1.2 "mkdir -p /tmp/gw_data"
ssh root@192.168.1.3 "mkdir -p /tmp/gw_data"
# 分发 Kafka 安装包到 node2(192.168.1.2)
scp /tmp/gw_data/kafka_2.13-3.8.1.tgz root@192.168.1.2:/tmp/gw_data/
# 分发 Kafka 安装包到 node3(192.168.1.3)
scp /tmp/gw_data/kafka_2.13-3.8.1.tgz root@192.168.1.3:/tmp/gw_data/
# 分发 JDK 安装包到 node2
scp /tmp/gw_data/OpenJDK21U-jdk_x64_linux_hotspot_21.0.10_7.tar.gz root@192.168.1.2:/tmp/gw_data/
# 分发 JDK 安装包到 node3
scp /tmp/gw_data/OpenJDK21U-jdk_x64_linux_hotspot_21.0.10_7.tar.gz root@192.168.1.3:/tmp/gw_data/
3. 在远程机器解压并安装 Kafka
# 在 node2(192.168.1.2)解压 Kafka
ssh root@192.168.1.2 "tar -xzf /tmp/gw_data/kafka_2.13-3.8.1.tgz -C /usr/local/"
# 重命名目录(若 /usr/local/kafka 已存在则先备份)
ssh root@192.168.1.2 "if [ -d /usr/local/kafka ]; then mv /usr/local/kafka /usr/local/kafka.bak; fi && mv /usr/local/kafka_2.13-3.8.1 /usr/local/kafka"
# 在 node3(192.168.1.3)解压 Kafka
ssh root@192.168.1.3 "tar -xzf /tmp/gw_data/kafka_2.13-3.8.1.tgz -C /usr/local/"
# 重命名目录
ssh root@192.168.1.3 "if [ -d /usr/local/kafka ]; then mv /usr/local/kafka /usr/local/kafka.bak; fi && mv /usr/local/kafka_2.13-3.8.1 /usr/local/kafka"
4. 验证远程机器 Kafka 安装
# 验证 node2 的 Kafka 安装
ssh root@192.168.1.2 "ls /usr/local/kafka/bin/kafka-server-start.sh"
ssh root@192.168.1.2 "/usr/local/kafka/bin/kafka-storage.sh version"
# 验证 node3 的 Kafka 安装
ssh root@192.168.1.3 "ls /usr/local/kafka/bin/kafka-server-start.sh"
ssh root@192.168.1.3 "/usr/local/kafka/bin/kafka-storage.sh version"
预期结果:远程机器的
/usr/local/kafka目录结构完整,版本与本机一致(3.8.1)。
5. 远程机器 Java 环境处理
先确认远程机器的 Java 环境,若未安装则使用 tar.gz 手动安装:
# 检查 node2 的 Java 环境
ssh root@192.168.1.2 "java -version 2>&1"
# 检查 node3 的 Java 环境
ssh root@192.168.1.3 "java -version 2>&1"
若 Java 已安装且版本 >= 17
确认 JAVA_HOME 环境变量已配置:
# 检查 JAVA_HOME
ssh root@192.168.1.2 "echo \$JAVA_HOME"
ssh root@192.168.1.3 "echo \$JAVA_HOME"
若 JAVA_HOME 为空,需查找 Java 安装路径并配置(见下方步骤)。
若 Java 未安装或版本低于 17
使用 tar.gz 手动安装(推荐,与本机安装方式一致):
# 在 node2(192.168.1.2)安装 Java 21
# 1. 创建安装目录
ssh root@192.168.1.2 "mkdir -p /usr/local/java"
# 2. 解压 JDK
ssh root@192.168.1.2 "tar -xzf /tmp/gw_data/OpenJDK21U-jdk_x64_linux_hotspot_21.0.10_7.tar.gz -C /usr/local/java/"
# 3. 验证解压结果
ssh root@192.168.1.2 "ls /usr/local/java/jdk-21.0.10+7/bin/java"
# 在 node3(192.168.1.3)安装 Java 21(步骤同上)
ssh root@192.168.1.3 "mkdir -p /usr/local/java"
ssh root@192.168.1.3 "tar -xzf /tmp/gw_data/OpenJDK21U-jdk_x64_linux_hotspot_21.0.10_7.tar.gz -C /usr/local/java/"
ssh root@192.168.1.3 "ls /usr/local/java/jdk-21.0.10+7/bin/java"
注意:JDK tar.gz 解压后的目录名为
jdk-21.0.10+7,安装路径为/usr/local/java/jdk-21.0.10+7,与本机保持一致。
配置 JAVA_HOME 环境变量
将 JAVA_HOME 同时写入 /etc/profile 和 ~/.bashrc,确保 login shell 和非 login shell 均能正确识别。
重要:SSH 远程命令默认为非 login shell,不会自动加载
/etc/profile,因此必须同时写入~/.bashrc,否则后续通过 SSH 启动 Kafka 时会因找不到JAVA_HOME而失败。
# 在 node2(192.168.1.2)配置 JAVA_HOME
# 写入 /etc/profile
ssh root@192.168.1.2 'grep -q "JAVA_HOME=/usr/local/java/jdk-21.0.10+7" /etc/profile || cat >> /etc/profile << "EOF"
export JAVA_HOME=/usr/local/java/jdk-21.0.10+7
export PATH=$JAVA_HOME/bin:$PATH
EOF'
# 写入 ~/.bashrc
ssh root@192.168.1.2 'grep -q "JAVA_HOME=/usr/local/java/jdk-21.0.10+7" ~/.bashrc || cat >> ~/.bashrc << "EOF"
export JAVA_HOME=/usr/local/java/jdk-21.0.10+7
export PATH=$JAVA_HOME/bin:$PATH
EOF'
# 在 node3(192.168.1.3)配置 JAVA_HOME
ssh root@192.168.1.3 'grep -q "JAVA_HOME=/usr/local/java/jdk-21.0.10+7" /etc/profile || cat >> /etc/profile << "EOF"
export JAVA_HOME=/usr/local/java/jdk-21.0.10+7
export PATH=$JAVA_HOME/bin:$PATH
EOF'
ssh root@192.168.1.3 'grep -q "JAVA_HOME=/usr/local/java/jdk-21.0.10+7" ~/.bashrc || cat >> ~/.bashrc << "EOF"
export JAVA_HOME=/usr/local/java/jdk-21.0.10+7
export PATH=$JAVA_HOME/bin:$PATH
EOF'
验证 Java 安装和 JAVA_HOME 配置:
# 验证 node2
ssh root@192.168.1.2 "source /etc/profile && java -version 2>&1"
ssh root@192.168.1.2 "echo \$JAVA_HOME"
# 验证 node3
ssh root@192.168.1.3 "source /etc/profile && java -version 2>&1"
ssh root@192.168.1.3 "echo \$JAVA_HOME"
预期结果:输出
openjdk version "21.0.10",JAVA_HOME=/usr/local/java/jdk-21.0.10+7。
注意:若远程机器已有
/etc/profile.d/java.sh文件,需检查其中 JAVA_HOME 路径是否正确。如有错误,需修正为/usr/local/java/jdk-21.0.10+7。
旧数据清理
在首次启动集群前,需确保所有机器上无残留的 Kafka 进程和数据目录,否则可能导致 Cluster UUID 不一致或端口冲突等启动失败问题。
1. 确认 3 台机器均无残留 Kafka 进程
# 检查本机 (node1) Kafka 进程
jps -l | grep kafka
# 检查远程 node2 Kafka 进程
ssh root@192.168.1.2 "jps -l 2>/dev/null | grep kafka || ps aux | grep kafka | grep -v grep"
# 检查远程 node3 Kafka 进程
ssh root@192.168.1.3 "jps -l 2>/dev/null | grep kafka || ps aux | grep kafka | grep -v grep"
若有残留进程,先停止:
# 停止本机 Kafka 进程
/usr/local/kafka/bin/kafka-server-stop.sh 2>/dev/null
jps -l | grep kafka.Kafka | awk '{print $1}' | xargs -r kill -15
# 停止远程 node2 Kafka 进程
ssh root@192.168.1.2 "/usr/local/kafka/bin/kafka-server-stop.sh 2>/dev/null; jps -l 2>/dev/null | grep kafka.Kafka | awk '{print \$1}' | xargs -r kill -15"
# 停止远程 node3 Kafka 进程
ssh root@192.168.1.3 "/usr/local/kafka/bin/kafka-server-stop.sh 2>/dev/null; jps -l 2>/dev/null | grep kafka.Kafka | awk '{print \$1}' | xargs -r kill -15"
# 等待进程退出
sleep 5
# 确认所有机器无残留进程
jps -l | grep kafka
ssh root@192.168.1.2 "jps -l 2>/dev/null | grep kafka"
ssh root@192.168.1.3 "jps -l 2>/dev/null | grep kafka"
2. 本机残留 KRaft 元数据清理
本机可能存在之前伪分布式部署的残留数据,必须清理:
# 清理本机所有残留 Kafka 数据目录
rm -rf /tmp/kraft-combined-logs/*
rm -rf /tmp/kafka-logs/*
rm -rf /tmp/kafka-kraft-node1/*
rm -rf /tmp/kafka-kraft-node2/*
rm -rf /tmp/kafka-kraft-node3/*
rm -rf /tmp/kafka-kraft-logs/*
# 确认清理结果
ls -la /tmp/ | grep -E 'kraft|kafka'
3. 远程机器残留数据清理
# 清理远程 node2 残留数据
ssh root@192.168.1.2 "rm -rf /tmp/kraft-combined-logs/* /tmp/kafka-logs/* /tmp/kafka-kraft-logs/* /tmp/kafka-kraft-node*/*"
# 清理远程 node3 残留数据
ssh root@192.168.1.3 "rm -rf /tmp/kraft-combined-logs/* /tmp/kafka-logs/* /tmp/kafka-kraft-logs/* /tmp/kafka-kraft-node*/*"
# 确认远程清理结果
ssh root@192.168.1.2 "ls -la /tmp/ | grep -E 'kraft|kafka'"
ssh root@192.168.1.3 "ls -la /tmp/ | grep -E 'kraft|kafka'"
重要:3 台机器均需确认无残留数据和进程,否则后续格式化或启动可能失败。
配置文件准备
以下为 3 个节点的完整配置文件。所有配置基于 /usr/local/kafka/config/kraft/server.properties 模板修改,针对多机真分布式 3 节点集群进行了适配。
目录规划说明
每台机器上运行一个 Kafka 节点,因此数据目录不再需要节点编号后缀区分:
| 节点 | node.id | IP | 数据目录 | 配置文件路径 | Broker 端口 | Controller 端口 |
|---|---|---|---|---|---|---|
| node1 | 1 | 192.168.1.1 | /tmp/kafka-kraft-logs | /usr/local/kafka/config/kraft/server.properties | 9092 | 9093 |
| node2 | 2 | 192.168.1.2 | /tmp/kafka-kraft-logs | /usr/local/kafka/config/kraft/server.properties | 9092 | 9093 |
| node3 | 3 | 192.168.1.3 | /tmp/kafka-kraft-logs | /usr/local/kafka/config/kraft/server.properties | 9092 | 9093 |
创建数据目录
# 在本机创建数据目录
mkdir -p /tmp/kafka-kraft-logs
# 在远程 node2 创建数据目录
ssh root@192.168.1.2 "mkdir -p /tmp/kafka-kraft-logs"
# 在远程 node3 创建数据目录
ssh root@192.168.1.3 "mkdir -p /tmp/kafka-kraft-logs"
# 验证目录创建结果
ls -ld /tmp/kafka-kraft-logs
ssh root@192.168.1.2 "ls -ld /tmp/kafka-kraft-logs"
ssh root@192.168.1.3 "ls -ld /tmp/kafka-kraft-logs"
node1 (192.168.1.1) 完整 server.properties
文件路径: /usr/local/kafka/config/kraft/server.properties
############################# 角色与节点标识 #############################
# 节点同时承担 broker 和 controller 角色(联合节点)
process.roles=broker,controller
# 节点唯一 ID,集群内不可重复
node.id=1
# Controller 仲裁投票者列表,列出所有 controller 节点的 id@host:port
# 多机部署:使用各机器实际 IP,端口统一为 9093
controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093
############################# 监听器配置 #############################
# 节点监听的端口:Broker 使用 9092(SASL_PLAINTEXT),Controller 使用 9093(明文)
# 多机部署:不同机器通过 IP 区分,端口统一
# 注意:Controller 间通信保持明文,仅 Broker 端口启用 SASL 认证
listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093
# Broker 间通信使用的监听器名称
inter.broker.listener.name=SASL_PLAINTEXT
# 对外宣告的 Broker 地址,必须配置为本节点实际可达 IP
# 客户端和其他 Broker 通过此地址连接本节点
# ⚠️ 多机部署关键配置:每台机器必须使用自己的实际 IP
advertised.listeners=SASL_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-logs
# 新建 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
############################# SASL 认证配置 #############################
# 启用的 SASL 认证机制
sasl.enabled.mechanisms=PLAIN
# Broker 间通信使用的 SASL 机制
sasl.mechanism.inter.broker.protocol=PLAIN
node2 (192.168.1.2) 完整 server.properties
文件路径: /usr/local/kafka/config/kraft/server.properties
############################# 角色与节点标识 #############################
# 节点同时承担 broker 和 controller 角色(联合节点)
process.roles=broker,controller
# 节点唯一 ID,集群内不可重复
node.id=2
# Controller 仲裁投票者列表,3 个节点共用同一配置
# 多机部署:使用各机器实际 IP,端口统一为 9093
controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093
############################# 监听器配置 #############################
# 节点监听的端口:Broker 使用 9092(SASL_PLAINTEXT),Controller 使用 9093(明文)
# 多机部署:不同机器通过 IP 区分,端口统一
# 注意:Controller 间通信保持明文,仅 Broker 端口启用 SASL 认证
listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093
# Broker 间通信使用的监听器名称
inter.broker.listener.name=SASL_PLAINTEXT
# 对外宣告的 Broker 地址,必须配置为本节点实际可达 IP
# ⚠️ 多机部署关键配置:每台机器必须使用自己的实际 IP
advertised.listeners=SASL_PLAINTEXT://192.168.1.2:9092
# 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-logs
# 新建 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
############################# SASL 认证配置 #############################
# 启用的 SASL 认证机制
sasl.enabled.mechanisms=PLAIN
# Broker 间通信使用的 SASL 机制
sasl.mechanism.inter.broker.protocol=PLAIN
node3 (192.168.1.3) 完整 server.properties
文件路径: /usr/local/kafka/config/kraft/server.properties
############################# 角色与节点标识 #############################
# 节点同时承担 broker 和 controller 角色(联合节点)
process.roles=broker,controller
# 节点唯一 ID,集群内不可重复
node.id=3
# Controller 仲裁投票者列表,3 个节点共用同一配置
# 多机部署:使用各机器实际 IP,端口统一为 9093
controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093
############################# 监听器配置 #############################
# 节点监听的端口:Broker 使用 9092(SASL_PLAINTEXT),Controller 使用 9093(明文)
# 多机部署:不同机器通过 IP 区分,端口统一
# 注意:Controller 间通信保持明文,仅 Broker 端口启用 SASL 认证
listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093
# Broker 间通信使用的监听器名称
inter.broker.listener.name=SASL_PLAINTEXT
# 对外宣告的 Broker 地址,必须配置为本节点实际可达 IP
# ⚠️ 多机部署关键配置:每台机器必须使用自己的实际 IP
advertised.listeners=SASL_PLAINTEXT://192.168.1.3:9092
# 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-logs
# 新建 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
############################# SASL 认证配置 #############################
# 启用的 SASL 认证机制
sasl.enabled.mechanisms=PLAIN
# Broker 间通信使用的 SASL 机制
sasl.mechanism.inter.broker.protocol=PLAIN
三个节点配置差异对照
| 配置项 | node1 (192.168.1.1) | node2 (192.168.1.2) | node3 (192.168.1.3) |
|---|---|---|---|
node.id | 1 | 2 | 3 |
advertised.listeners | SASL_PLAINTEXT://192.168.1.1:9092 | SASL_PLAINTEXT://192.168.1.2:9092 | SASL_PLAINTEXT://192.168.1.3:9092 |
controller.quorum.voters | 相同 | 相同 | 相同 |
listeners | SASL_PLAINTEXT://:9092,CONTROLLER://:9093 | 相同 | 相同 |
inter.broker.listener.name | SASL_PLAINTEXT | 相同 | 相同 |
sasl.enabled.mechanisms | PLAIN | 相同 | 相同 |
sasl.mechanism.inter.broker.protocol | PLAIN | 相同 | 相同 |
log.dirs | 相同 | 相同 | 相同 |
| 其他配置 | 相同 | 相同 | 相同 |
核心差异:每个节点仅有
node.id和advertised.listeners两项不同,其余配置完全一致。所有节点均已启用 SASL_PLAINTEXT 认证(Broker 端口 9092),Controller 端口 9093 保持明文通信。
逐项关键配置说明
controller.quorum.voters 格式
controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093
格式为 node.id@host:controller_port,逗号分隔。3 个节点必须配置完全一致。多机部署中,host 必须使用实际可达 IP(不能使用 localhost),否则 Controller 之间无法通信。
advertised.listeners 必须用可达 IP
advertised.listeners 是 Broker 向客户端和其他 Broker 宣告的地址。若配置为 localhost,其他机器的客户端无法连接。每台机器必须使用自己的实际 IP 地址:
- node1:
SASL_PLAINTEXT://192.168.1.1:9092 - node2:
SASL_PLAINTEXT://192.168.1.2:9092 - node3:
SASL_PLAINTEXT://192.168.1.3:9092
replication.factor 设为 3 的原因
3 节点集群将 offsets.topic.replication.factor 和 transaction.state.log.replication.factor 设为 3,确保每个内部 Topic 的副本分布在所有节点上,实现最高容错能力(允许 1 个节点故障时数据仍可用)。
配置文件部署方式
本机 (node1) 直接编辑
# 备份原始配置文件
cp /usr/local/kafka/config/kraft/server.properties /usr/local/kafka/config/kraft/server.properties.bak
# 编辑 node1 配置文件(可直接使用上述完整配置覆盖)
vi /usr/local/kafka/config/kraft/server.properties
远程节点通过 SCP 分发
先在本地准备好配置文件,再通过 SCP 分发到远程机器:
# 在本机创建临时目录存放各节点配置
mkdir -p /tmp/kafka-configs
# 创建 node2 配置文件(基于 node1 配置修改差异项)
cp /usr/local/kafka/config/kraft/server.properties /tmp/kafka-configs/server-node2.properties
sed -i 's/^node.id=.*/node.id=2/' /tmp/kafka-configs/server-node2.properties
sed -i 's|^advertised.listeners=.*|advertised.listeners=SASL_PLAINTEXT://192.168.1.2:9092|' /tmp/kafka-configs/server-node2.properties
sed -i 's|^listeners=.*|listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093|' /tmp/kafka-configs/server-node2.properties
sed -i 's/^inter.broker.listener.name=.*/inter.broker.listener.name=SASL_PLAINTEXT/' /tmp/kafka-configs/server-node2.properties
sed -i 's/^controller.quorum.voters=.*/controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093/' /tmp/kafka-configs/server-node2.properties
sed -i 's|^log.dirs=.*|log.dirs=/tmp/kafka-kraft-logs|' /tmp/kafka-configs/server-node2.properties
sed -i 's/^offsets.topic.replication.factor=.*/offsets.topic.replication.factor=3/' /tmp/kafka-configs/server-node2.properties
sed -i 's/^transaction.state.log.replication.factor=.*/transaction.state.log.replication.factor=3/' /tmp/kafka-configs/server-node2.properties
sed -i 's/^transaction.state.log.min.isr=.*/transaction.state.log.min.isr=3/' /tmp/kafka-configs/server-node2.properties
sed -i 's/^num.partitions=.*/num.partitions=3/' /tmp/kafka-configs/server-node2.properties
# 追加 SASL 认证配置
echo "sasl.enabled.mechanisms=PLAIN" >> /tmp/kafka-configs/server-node2.properties
echo "sasl.mechanism.inter.broker.protocol=PLAIN" >> /tmp/kafka-configs/server-node2.properties
# 创建 node3 配置文件
cp /usr/local/kafka/config/kraft/server.properties /tmp/kafka-configs/server-node3.properties
sed -i 's/^node.id=.*/node.id=3/' /tmp/kafka-configs/server-node3.properties
sed -i 's|^advertised.listeners=.*|advertised.listeners=SASL_PLAINTEXT://192.168.1.3:9092|' /tmp/kafka-configs/server-node3.properties
sed -i 's|^listeners=.*|listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093|' /tmp/kafka-configs/server-node3.properties
sed -i 's/^inter.broker.listener.name=.*/inter.broker.listener.name=SASL_PLAINTEXT/' /tmp/kafka-configs/server-node3.properties
sed -i 's/^controller.quorum.voters=.*/controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093/' /tmp/kafka-configs/server-node3.properties
sed -i 's|^log.dirs=.*|log.dirs=/tmp/kafka-kraft-logs|' /tmp/kafka-configs/server-node3.properties
sed -i 's/^offsets.topic.replication.factor=.*/offsets.topic.replication.factor=3/' /tmp/kafka-configs/server-node3.properties
sed -i 's/^transaction.state.log.replication.factor=.*/transaction.state.log.replication.factor=3/' /tmp/kafka-configs/server-node3.properties
sed -i 's/^transaction.state.log.min.isr=.*/transaction.state.log.min.isr=3/' /tmp/kafka-configs/server-node3.properties
sed -i 's/^num.partitions=.*/num.partitions=3/' /tmp/kafka-configs/server-node3.properties
# 追加 SASL 认证配置
echo "sasl.enabled.mechanisms=PLAIN" >> /tmp/kafka-configs/server-node3.properties
echo "sasl.mechanism.inter.broker.protocol=PLAIN" >> /tmp/kafka-configs/server-node3.properties
# 通过 SCP 分发到远程机器
scp /tmp/kafka-configs/server-node2.properties root@192.168.1.2:/usr/local/kafka/config/kraft/server.properties
scp /tmp/kafka-configs/server-node3.properties root@192.168.1.3:/usr/local/kafka/config/kraft/server.properties
同步修改本机 node1 配置
# 修改本机 server.properties
sed -i 's/^controller.quorum.voters=.*/controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093/' /usr/local/kafka/config/kraft/server.properties
sed -i 's|^log.dirs=.*|log.dirs=/tmp/kafka-kraft-logs|' /usr/local/kafka/config/kraft/server.properties
sed -i 's/^offsets.topic.replication.factor=.*/offsets.topic.replication.factor=3/' /usr/local/kafka/config/kraft/server.properties
sed -i 's/^transaction.state.log.replication.factor=.*/transaction.state.log.replication.factor=3/' /usr/local/kafka/config/kraft/server.properties
sed -i 's/^transaction.state.log.min.isr=.*/transaction.state.log.min.isr=3/' /usr/local/kafka/config/kraft/server.properties
sed -i 's/^num.partitions=.*/num.partitions=3/' /usr/local/kafka/config/kraft/server.properties
验证所有节点配置
# 验证 node1 关键配置
echo "=== node1 (192.168.1.1) 关键配置 ==="
grep -E '^(process\.roles|node\.id|controller\.quorum\.voters|listeners|advertised\.listeners|log\.dirs|offsets\.topic\.replication\.factor|num\.partitions)' /usr/local/kafka/config/kraft/server.properties
# 验证 node2 关键配置
echo "=== node2 (192.168.1.2) 关键配置 ==="
ssh root@192.168.1.2 "grep -E '^(process\.roles|node\.id|controller\.quorum\.voters|listeners|advertised\.listeners|log\.dirs|offsets\.topic\.replication\.factor|num\.partitions)' /usr/local/kafka/config/kraft/server.properties"
# 验证 node3 关键配置
echo "=== node3 (192.168.1.3) 关键配置 ==="
ssh root@192.168.1.3 "grep -E '^(process\.roles|node\.id|controller\.quorum\.voters|listeners|advertised\.listeners|log\.dirs|offsets\.topic\.replication\.factor|num\.partitions)' /usr/local/kafka/config/kraft/server.properties"
预期验证结果:
- node1:
node.id=1,advertised.listeners=SASL_PLAINTEXT://192.168.1.1:9092,listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093- node2:
node.id=2,advertised.listeners=SASL_PLAINTEXT://192.168.1.2:9092,listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093- node3:
node.id=3,advertised.listeners=SASL_PLAINTEXT://192.168.1.3:9092,listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093- 3 个节点的
controller.quorum.voters配置完全一致- 3 个节点均已启用 SASL 认证(
sasl.enabled.mechanisms=PLAIN,inter.broker.listener.name=SASL_PLAINTEXT)
JAAS 认证配置文件
启用 SASL 认证后,每个 Kafka 节点需要配置 JAAS(Java Authentication and Authorization Service)文件,定义服务端认证信息。
在本机 (node1) 创建 JAAS 配置文件
# 创建 JAAS 配置文件
cat > /usr/local/kafka/config/kafka_server_jaas.conf << 'EOF'
KafkaServer {
org.apache.kafka.common.security.plain.PlainLoginModule required
username="admin"
password="your_password_here"
user_admin="your_password_here";
};
EOF
# 设置文件权限(仅所有者可读写,保护敏感凭证)
chmod 600 /usr/local/kafka/config/kafka_server_jaas.conf
# 验证文件内容和权限
cat /usr/local/kafka/config/kafka_server_jaas.conf
ls -la /usr/local/kafka/config/kafka_server_jaas.conf
通过 SCP 分发 JAAS 配置文件到远程节点
# 分发 JAAS 配置文件到 node2(192.168.1.2)
scp /usr/local/kafka/config/kafka_server_jaas.conf root@192.168.1.2:/usr/local/kafka/config/kafka_server_jaas.conf
# 分发 JAAS 配置文件到 node3(192.168.1.3)
scp /usr/local/kafka/config/kafka_server_jaas.conf root@192.168.1.3:/usr/local/kafka/config/kafka_server_jaas.conf
# 在远程机器设置文件权限
ssh root@192.168.1.2 "chmod 600 /usr/local/kafka/config/kafka_server_jaas.conf"
ssh root@192.168.1.3 "chmod 600 /usr/local/kafka/config/kafka_server_jaas.conf"
# 验证远程 JAAS 文件
ssh root@192.168.1.2 "cat /usr/local/kafka/config/kafka_server_jaas.conf"
ssh root@192.168.1.3 "cat /usr/local/kafka/config/kafka_server_jaas.conf"
说明:JAAS 配置文件中
username和password用于 Broker 间通信认证,user_admin定义了客户端连接时使用的用户凭证。所有节点使用相同的 JAAS 配置。
客户端 SASL 配置文件
所有 Kafka CLI 工具(kafka-topics.sh、kafka-console-producer.sh 等)连接启用 SASL 认证的集群时,需要提供客户端认证配置。通过 --command-config 参数指定配置文件路径。
在本机创建客户端 SASL 配置文件
# 创建客户端 SASL 配置文件
cat > /tmp/gw_data/kafka_client_sasl.properties << 'EOF'
security.protocol=SASL_PLAINTEXT
sasl.mechanism=PLAIN
sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="admin" password="your_password_here";
EOF
# 验证文件内容
cat /tmp/gw_data/kafka_client_sasl.properties
通过 SCP 分发客户端 SASL 配置文件到远程节点
# 分发到 node2(192.168.1.2)
scp /tmp/gw_data/kafka_client_sasl.properties root@192.168.1.2:/tmp/gw_data/
# 分发到 node3(192.168.1.3)
scp /tmp/gw_data/kafka_client_sasl.properties root@192.168.1.3:/tmp/gw_data/
重要:启用 SASL 认证后,所有 Kafka CLI 命令(kafka-topics.sh、kafka-console-producer.sh、kafka-console-consumer.sh 等)必须追加
--command-config /tmp/gw_data/kafka_client_sasl.properties参数,否则将因认证失败而无法连接集群。后续文档中所有 CLI 命令均已包含此参数。
KRaft 集群初始化
KRaft 模式要求在首次启动前对每个节点的数据目录进行格式化(format),生成并绑定 Cluster UUID。3 个节点必须使用同一个 Cluster UUID,否则无法组成集群。
步骤 1:在本机生成 Cluster UUID
# 生成集群唯一标识 UUID
CLUSTER_UUID=$(/usr/local/kafka/bin/kafka-storage.sh random-uuid)
echo "Cluster UUID: $CLUSTER_UUID"
# 将 UUID 保存到变量供后续使用(或手动记录)
# 示例输出:Cluster UUID: fPvXyZabc123defGhiJklMnoPqrStuVw
重要:记录此 UUID 值,后续 3 个节点格式化时必须使用同一个值。
步骤 2:使用同一 UUID 格式化本机 node1
# 将生成的 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.properties
步骤 3:将 UUID 传递到远程机器并格式化 node2 和 node3
通过 SSH 远程执行格式化命令,将 $CLUSTER_UUID 变量作为参数传入:
# 将上一步生成的 UUID 替换下面命令中的 <YOUR_CLUSTER_UUID>
CLUSTER_UUID=<YOUR_CLUSTER_UUID>
# 格式化远程 node2(192.168.1.2)
ssh root@192.168.1.2 "CLUSTER_UUID=$CLUSTER_UUID && /usr/local/kafka/bin/kafka-storage.sh format -t \$CLUSTER_UUID -c /usr/local/kafka/config/kraft/server.properties"
# 格式化远程 node3(192.168.1.3)
ssh root@192.168.1.3 "CLUSTER_UUID=$CLUSTER_UUID && /usr/local/kafka/bin/kafka-storage.sh format -t \$CLUSTER_UUID -c /usr/local/kafka/config/kraft/server.properties"
注意:SSH 远程命令中,
$CLUSTER_UUID在本机展开后传递到远程,\$CLUSTER_UUID中的反斜杠确保远程端也能正确引用该变量。实际操作时,可直接将 UUID 字面量嵌入命令中更简单,例如:ssh root@192.168.1.2 "/usr/local/kafka/bin/kafka-storage.sh format -t fPvXyZabc123defGhiJklMnoPqrStuVw -c /usr/local/kafka/config/kraft/server.properties"
步骤 4:验证 3 节点格式化结果
# 检查本机 node1 的 meta.properties
cat /tmp/kafka-kraft-logs/meta.properties
# 检查远程 node2 的 meta.properties
ssh root@192.168.1.2 "cat /tmp/kafka-kraft-logs/meta.properties"
# 检查远程 node3 的 meta.properties
ssh root@192.168.1.3 "cat /tmp/kafka-kraft-logs/meta.properties"
# 确认 3 个节点的 cluster.id 值一致
echo "=== node1 cluster.id ==="
grep cluster.id /tmp/kafka-kraft-logs/meta.properties
echo "=== node2 cluster.id ==="
ssh root@192.168.1.2 "grep cluster.id /tmp/kafka-kraft-logs/meta.properties"
echo "=== node3 cluster.id ==="
ssh root@192.168.1.3 "grep cluster.id /tmp/kafka-kraft-logs/meta.properties"
预期结果:3 个节点的
cluster.id值完全一致,node.id分别为 1、2、3。
一键格式化脚本
#!/bin/bash
# 文件路径: /home/admin/format-cluster.sh
# 功能: 生成 Cluster UUID 并格式化 3 个节点的数据目录
KAFKA_HOME=/usr/local/kafka
CONFIG_FILE=$KAFKA_HOME/config/kraft/server.properties
NODE2_HOST=root@192.168.1.2
NODE3_HOST=root@192.168.1.3
# 生成 Cluster UUID
CLUSTER_UUID=$($KAFKA_HOME/bin/kafka-storage.sh random-uuid)
echo "生成的 Cluster UUID: $CLUSTER_UUID"
# 格式化本机 node1
echo "正在格式化 node1 (192.168.1.1)..."
$KAFKA_HOME/bin/kafka-storage.sh format \
-t $CLUSTER_UUID \
-c $CONFIG_FILE
if [ $? -eq 0 ]; then
echo " node1 格式化成功"
else
echo " node1 格式化失败!"
exit 1
fi
# 格式化远程 node2
echo "正在格式化 node2 (192.168.1.2)..."
ssh $NODE2_HOST "$KAFKA_HOME/bin/kafka-storage.sh format -t $CLUSTER_UUID -c $CONFIG_FILE"
if [ $? -eq 0 ]; then
echo " node2 格式化成功"
else
echo " node2 格式化失败!"
exit 1
fi
# 格式化远程 node3
echo "正在格式化 node3 (192.168.1.3)..."
ssh $NODE3_HOST "$KAFKA_HOME/bin/kafka-storage.sh format -t $CLUSTER_UUID -c $CONFIG_FILE"
if [ $? -eq 0 ]; then
echo " node3 格式化成功"
else
echo " node3 格式化失败!"
exit 1
fi
echo ""
echo "所有节点格式化完成,Cluster UUID: $CLUSTER_UUID"
echo ""
echo "=== 验证 cluster.id 一致性 ==="
echo "node1: $(grep cluster.id /tmp/kafka-kraft-logs/meta.properties)"
echo "node2: $(ssh $NODE2_HOST 'grep cluster.id /tmp/kafka-kraft-logs/meta.properties')"
echo "node3: $(ssh $NODE3_HOST 'grep cluster.id /tmp/kafka-kraft-logs/meta.properties')"
赋予执行权限:
chmod +x /home/admin/format-cluster.sh
启动集群
手动逐节点启动
按顺序依次启动 3 个节点,使用 -daemon 参数后台运行:
# 1. 启动本机 node1
export KAFKA_OPTS="-Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf"
/usr/local/kafka/bin/kafka-server-start.sh \
-daemon \
/usr/local/kafka/config/kraft/server.properties
# 等待 node1 启动完成(约 5-10 秒)
sleep 10
# 2. 远程启动 node2(192.168.1.2)
ssh root@192.168.1.2 "export KAFKA_OPTS='-Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf' && /usr/local/kafka/bin/kafka-server-start.sh -daemon /usr/local/kafka/config/kraft/server.properties"
# 等待 node2 启动完成
sleep 10
# 3. 远程启动 node3(192.168.1.3)
ssh root@192.168.1.3 "export KAFKA_OPTS='-Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf' && /usr/local/kafka/bin/kafka-server-start.sh -daemon /usr/local/kafka/config/kraft/server.properties"
# 等待 node3 启动完成
sleep 10
启动顺序说明
- 先启动 node1:作为第一个 Controller,node1 会发起 Raft 选举并成为初始 Leader
- 再启动 node2:加入仲裁集群,参与投票
- 最后启动 node3:加入后集群达到 3 节点,满足多数派(2/3)条件,仲裁稳定
注意:虽然启动顺序不影响最终集群状态,但按顺序启动可以减少选举抖动,便于观察启动过程。启动间隔建议 5-10 秒,确保前一个节点的 Controller 服务就绪后再启动下一个。
一键启动脚本
文件路径: /home/admin/start-cluster.sh
#!/bin/bash
# Kafka KRaft 多机集群一键启动脚本
KAFKA_HOME=/usr/local/kafka
CONFIG_FILE=$KAFKA_HOME/config/kraft/server.properties
NODE2_HOST=root@192.168.1.2
NODE3_HOST=root@192.168.1.3
LOG_FILE=/home/admin/kafka-cluster-start.log
KAFKA_OPTS="-Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf"
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
# 启动本机 node1
echo "启动 node1 (192.168.1.1)..." | tee -a $LOG_FILE
export KAFKA_OPTS
$KAFKA_HOME/bin/kafka-server-start.sh -daemon $CONFIG_FILE
if [ $? -eq 0 ]; then
echo " node1 启动命令已发送" | tee -a $LOG_FILE
else
echo " node1 启动失败!" | tee -a $LOG_FILE
exit 1
fi
sleep 10
# 启动远程 node2
echo "启动 node2 (192.168.1.2)..." | tee -a $LOG_FILE
ssh $NODE2_HOST "export KAFKA_OPTS='-Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf' && $KAFKA_HOME/bin/kafka-server-start.sh -daemon $CONFIG_FILE"
if [ $? -eq 0 ]; then
echo " node2 启动命令已发送" | tee -a $LOG_FILE
else
echo " node2 启动失败!" | tee -a $LOG_FILE
exit 1
fi
sleep 10
# 启动远程 node3
echo "启动 node3 (192.168.1.3)..." | tee -a $LOG_FILE
ssh $NODE3_HOST "export KAFKA_OPTS='-Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf' && $KAFKA_HOME/bin/kafka-server-start.sh -daemon $CONFIG_FILE"
if [ $? -eq 0 ]; then
echo " node3 启动命令已发送" | tee -a $LOG_FILE
else
echo " node3 启动失败!" | tee -a $LOG_FILE
exit 1
fi
# 等待集群完成 Controller 选举
echo "等待 Controller 选举完成..." | tee -a $LOG_FILE
sleep 15
# 验证启动结果
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 "=== 远程 node2 进程检查 ===" | tee -a $LOG_FILE
ssh $NODE2_HOST "jps -l 2>/dev/null | grep -i kafka" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "=== 远程 node3 进程检查 ===" | tee -a $LOG_FILE
ssh $NODE3_HOST "jps -l 2>/dev/null | grep -i kafka" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "=== 本机端口检查 ===" | tee -a $LOG_FILE
ss -tlnp | grep -E '9092|9093' | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "=== 远程 node2 端口检查 ===" | tee -a $LOG_FILE
ssh $NODE2_HOST "ss -tlnp | grep -E '9092|9093'" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "=== 远程 node3 端口检查 ===" | tee -a $LOG_FILE
ssh $NODE3_HOST "ss -tlnp | grep -E '9092|9093'" | 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 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --status | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "集群启动完成,日志已保存至 $LOG_FILE"
赋予执行权限并运行:
# 赋予执行权限
chmod +x /home/admin/start-cluster.sh
# 执行启动脚本
/home/admin/start-cluster.sh
启动后验证
# 检查本机 Kafka 进程(应看到 1 个 Kafka 进程)
jps -l | grep kafka
# 检查远程 node2 Kafka 进程
ssh root@192.168.1.2 "jps -l | grep kafka"
# 检查远程 node3 Kafka 进程
ssh root@192.168.1.3 "jps -l | grep kafka"
# 检查本机端口监听状态(应看到 9092 和 9093)
ss -tlnp | grep -E '9092|9093'
# 检查远程 node2 端口监听状态
ssh root@192.168.1.2 "ss -tlnp | grep -E '9092|9093'"
# 检查远程 node3 端口监听状态
ssh root@192.168.1.3 "ss -tlnp | grep -E '9092|9093'"
# 查看集群仲裁状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --status
预期结果:
- 每台机器各 1 个
kafka.Kafka进程- 每台机器各监听 9092(Broker)和 9093(Controller)两个端口
- 仲裁状态显示 Leader 和 Follower 节点正常
查看启动日志方法
# 查看本机 Kafka 运行日志
tail -100f /usr/local/kafka/logs/server.log
# 查看本机 Controller 日志
tail -100f /usr/local/kafka/logs/controller.log
# 查看远程 node2 Kafka 运行日志
ssh root@192.168.1.2 "tail -100 /usr/local/kafka/logs/server.log"
# 查看远程 node3 Kafka 运行日志
ssh root@192.168.1.3 "tail -100 /usr/local/kafka/logs/server.log"
# 查看 KRaft 元数据日志
tail -50 /tmp/kafka-kraft-logs/server.log
ssh root@192.168.1.2 "tail -50 /tmp/kafka-kraft-logs/server.log"
ssh root@192.168.1.3 "tail -50 /tmp/kafka-kraft-logs/server.log"
集群验证
1. 查看 Raft 仲裁状态
# 查看集群元数据仲裁状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --status
# 查看仲裁成员详情(包含各节点角色和状态)
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --members
# 查看仲裁复制状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --replication
2. 创建测试 Topic(3 分区 3 副本)
# 创建 3 分区、3 副本的测试 Topic
/usr/local/kafka/bin/kafka-topics.sh \
--create \
--topic test-cluster \
--partitions 3 \
--replication-factor 3 \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
3. 查看 Topic 详情和副本分布
# 查看所有 Topic 列表
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 查看 Topic 详细信息(分区、副本分布、Leader)
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic test-cluster \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 查看所有 Topic 详情
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
预期结果:
test-clusterTopic 有 3 个分区,每个分区的副本分布在 3 个不同 Broker 上(Leader 在其中 1 个),ISR 列表与副本列表一致。
4. 生产消息测试
# 启动控制台生产者,发送消息到 test-cluster
/usr/local/kafka/bin/kafka-console-producer.sh \
--topic test-cluster \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 在交互式命令行中输入消息(每行一条),例如:
# Hello Kafka KRaft Multi-Node Cluster!
# This is message from node1
# This is message 3
# 按 Ctrl+C 退出
5. 消费消息测试
# 启动控制台消费者,从头开始消费 test-cluster 的消息
/usr/local/kafka/bin/kafka-console-consumer.sh \
--topic test-cluster \
--from-beginning \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 确认能消费到之前生产的消息后,按 Ctrl+C 退出
6. 验证跨节点通信
从不同 Broker 地址连接,验证集群内数据可跨节点访问:
# 通过 node1 的 Broker 连接生产消息
/usr/local/kafka/bin/kafka-console-producer.sh \
--topic test-cluster \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 输入: message-via-node1
# 按 Ctrl+C 退出
# 通过 node2 的 Broker 连接消费消息
/usr/local/kafka/bin/kafka-console-consumer.sh \
--topic test-cluster \
--from-beginning \
--bootstrap-server 192.168.1.2:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 通过 node3 的 Broker 连接消费消息
/usr/local/kafka/bin/kafka-console-consumer.sh \
--topic test-cluster \
--from-beginning \
--bootstrap-server 192.168.1.3:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
预期结果:无论通过哪个 Broker 连接,都能消费到所有消息,验证了跨节点的数据复制和通信正常。
7. 查看内部 Topic 副本分布
# 查看 __consumer_offsets 内部 Topic 状态(验证副本分布在 3 个节点)
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
--topic __consumer_offsets
# 查看 Broker API 版本(确认 3 个节点均在线)
/usr/local/kafka/bin/kafka-broker-api-versions.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
停止集群
手动逐节点停止
多机集群停止时,建议先停远程节点再停本机:
# 1. 远程停止 node3(192.168.1.3)
ssh root@192.168.1.3 "/usr/local/kafka/bin/kafka-server-stop.sh"
# 等待 node3 完全停止
sleep 5
# 2. 远程停止 node2(192.168.1.2)
ssh root@192.168.1.2 "/usr/local/kafka/bin/kafka-server-stop.sh"
# 等待 node2 完全停止
sleep 5
# 3. 停止本机 node1
/usr/local/kafka/bin/kafka-server-stop.sh
# 等待 node1 完全停止
sleep 5
注意:
kafka-server-stop.sh在每台机器上只有 1 个 Kafka 进程,因此可直接使用而无需指定配置文件(区别于伪分布式环境)。
确认所有进程已停止
# 确认本机无 Kafka 进程
jps -l | grep kafka
# 确认远程 node2 无 Kafka 进程
ssh root@192.168.1.2 "jps -l 2>/dev/null | grep kafka || echo '无 Kafka 进程'"
# 确认远程 node3 无 Kafka 进程
ssh root@192.168.1.3 "jps -l 2>/dev/null | grep kafka || echo '无 Kafka 进程'"
预期结果:3 台机器均无 Kafka 进程。若有残留进程,可通过 PID 强制终止:
# 本机强制终止残留 Kafka 进程
jps -l | grep kafka.Kafka | awk '{print $1}' | xargs -r kill -15
# 远程 node2 强制终止
ssh root@192.168.1.2 "jps -l 2>/dev/null | grep kafka.Kafka | awk '{print \$1}' | xargs -r kill -15"
# 远程 node3 强制终止
ssh root@192.168.1.3 "jps -l 2>/dev/null | grep kafka.Kafka | awk '{print \$1}' | xargs -r kill -15"
一键停止脚本
文件路径: /home/admin/stop-cluster.sh
#!/bin/bash
# Kafka KRaft 多机集群一键停止脚本
KAFKA_HOME=/usr/local/kafka
NODE2_HOST=root@192.168.1.2
NODE3_HOST=root@192.168.1.3
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
# 停止远程 node3
echo "停止 node3 (192.168.1.3)..." | tee -a $LOG_FILE
ssh $NODE3_HOST "$KAFKA_HOME/bin/kafka-server-stop.sh" 2>/dev/null
echo " node3 停止命令已发送" | tee -a $LOG_FILE
sleep 5
# 停止远程 node2
echo "停止 node2 (192.168.1.2)..." | tee -a $LOG_FILE
ssh $NODE2_HOST "$KAFKA_HOME/bin/kafka-server-stop.sh" 2>/dev/null
echo " node2 停止命令已发送" | tee -a $LOG_FILE
sleep 5
# 停止本机 node1
echo "停止 node1 (192.168.1.1)..." | tee -a $LOG_FILE
$KAFKA_HOME/bin/kafka-server-stop.sh 2>/dev/null
echo " node1 停止命令已发送" | tee -a $LOG_FILE
# 等待进程退出
echo "等待所有进程退出..." | tee -a $LOG_FILE
sleep 10
# 检查残留进程并强制终止
for HOST_DESC in "node1:local" "node2:$NODE2_HOST" "node3:$NODE3_HOST"; do
NODE_NAME=$(echo $HOST_DESC | cut -d: -f1)
HOST=$(echo $HOST_DESC | cut -d: -f2)
if [ "$HOST" = "local" ]; then
REMAINING=$(jps -l 2>/dev/null | grep kafka.Kafka | awk '{print $1}')
if [ -n "$REMAINING" ]; then
echo "警告: $NODE_NAME 有残留进程 $REMAINING,强制终止" | tee -a $LOG_FILE
echo "$REMAINING" | xargs -r kill -9
fi
else
REMAINING=$(ssh $HOST "jps -l 2>/dev/null | grep kafka.Kafka | awk '{print \$1}'")
if [ -n "$REMAINING" ]; then
echo "警告: $NODE_NAME 有残留进程 $REMAINING,强制终止" | tee -a $LOG_FILE
ssh $HOST "jps -l 2>/dev/null | grep kafka.Kafka | awk '{print \$1}' | xargs -r kill -9"
fi
fi
done
echo "" | tee -a $LOG_FILE
echo "=== 最终进程检查 ===" | tee -a $LOG_FILE
echo "node1: $(jps -l 2>/dev/null | grep -i kafka || echo '无 Kafka 进程')" | tee -a $LOG_FILE
echo "node2: $(ssh $NODE2_HOST "jps -l 2>/dev/null | grep -i kafka || echo '无 Kafka 进程'")" | tee -a $LOG_FILE
echo "node3: $(ssh $NODE3_HOST "jps -l 2>/dev/null | grep -i kafka || echo '无 Kafka 进程'")" | tee -a $LOG_FILE
echo "" | tee -a $LOG_FILE
echo "集群停止完成,日志已保存至 $LOG_FILE"
赋予执行权限:
chmod +x /home/admin/stop-cluster.sh
常用运维操作
Topic 管理
# 创建 Topic(指定分区数和副本数)
/usr/local/kafka/bin/kafka-topics.sh \
--create \
--topic my-topic \
--partitions 3 \
--replication-factor 3 \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 查看 Topic 列表
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 查看 Topic 详情
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 查看 Topic 配置
/usr/local/kafka/bin/kafka-configs.sh \
--describe \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 删除 Topic(需确保 delete.topic.enable=true,默认已开启)
/usr/local/kafka/bin/kafka-topics.sh \
--delete \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
分区扩容
# 将 my-topic 分区数从 3 扩展到 6(只能增加,不能减少)
/usr/local/kafka/bin/kafka-topics.sh \
--alter \
--topic my-topic \
--partitions 6 \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 验证分区扩展结果
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic my-topic \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
消费者组查看
# 查看所有消费者组
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--list \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 查看消费者组详情(成员、分区分配、消费位移)
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--describe \
--group test-consumer-group \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 重置消费者位移到最早(需先停止消费者)
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--reset-offsets \
--group test-consumer-group \
--topic test-cluster \
--to-earliest \
--execute \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 重置消费者位移到最新
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--reset-offsets \
--group test-consumer-group \
--topic test-cluster \
--to-latest \
--execute \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 删除消费者组
/usr/local/kafka/bin/kafka-consumer-groups.sh \
--delete \
--group test-consumer-group \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
磁盘使用监控
# 查看本机数据目录磁盘占用
du -sh /tmp/kafka-kraft-logs
# 查看远程 node2 数据目录磁盘占用
ssh root@192.168.1.2 "du -sh /tmp/kafka-kraft-logs"
# 查看远程 node3 数据目录磁盘占用
ssh root@192.168.1.3 "du -sh /tmp/kafka-kraft-logs"
# 查看磁盘整体使用情况
df -h /tmp
# 查看各 Topic 占用空间(本机)
du -sh /tmp/kafka-kraft-logs/* | sort -rh | head -20
# 修改 Topic 日志保留时间(动态配置,无需重启)
/usr/local/kafka/bin/kafka-configs.sh \
--alter \
--topic test-cluster \
--add-config retention.hours=24 \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
集群元数据查看
# 查看集群元数据仲裁状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --status
# 查看仲裁成员
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --members
# 查看仲裁复制状态
/usr/local/kafka/bin/kafka-metadata-quorum.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties \
describe --replication
# 查看 Broker API 版本(确认各节点在线状态)
/usr/local/kafka/bin/kafka-broker-api-versions.sh \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 通过不同节点 Bootstrap 验证集群连通性
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.2:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
/usr/local/kafka/bin/kafka-topics.sh \
--list \
--bootstrap-server 192.168.1.3:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
常见问题排查
Q1: SSH 连接失败
现象:
执行 ssh root@192.168.1.2 时提示 Permission denied、Connection refused 或 Connection timed out。
排查与解决:
# 1. 检查网络连通性
ping -c 3 192.168.1.2
# 2. 检查 SSH 服务是否运行
ssh root@192.168.1.2 "systemctl status sshd" 2>/dev/null
# 或
ssh root@192.168.1.2 "systemctl status ssh" 2>/dev/null
# 3. 检查防火墙是否放行 SSH(22 端口)
# 若无法通过 SSH 连接,需直接在远程机器上操作
# firewall-cmd --list-ports
# firewall-cmd --permanent --add-port=22/tcp
# firewall-cmd --reload
# 4. 检查 SSH 密钥权限
ls -la ~/.ssh/id_rsa ~/.ssh/id_rsa.pub
# 私钥应为 600,公钥应为 644
# 5. 检查远程机器 authorized_keys 权限
ssh root@192.168.1.2 "ls -la ~/.ssh/authorized_keys"
# 应为 600,~/.ssh 目录应为 700
# 6. 重新分发公钥(若免密失效)
ssh-copy-id root@192.168.1.2
常见原因:
- 远程机器 SSH 服务未启动
- 防火墙拦截了 SSH 连接
- 公钥未正确写入
authorized_keys或权限不正确 ~/.ssh目录权限不正确(需 700)
Q2: 远程机器 Kafka 启动失败
现象: 远程机器执行启动命令后,进程未启动或立即退出。
排查与解决:
# 1. 确认远程机器 Java 环境
ssh root@192.168.1.2 "java -version 2>&1"
# 若报 "command not found",需安装 Java(见 Kafka 安装包分发章节)
# 2. 确认 JAVA_HOME 已配置
ssh root@192.168.1.2 "echo \$JAVA_HOME"
# 若为空,需配置 JAVA_HOME(见 Kafka 安装包分发章节)
# 3. 确认 Kafka 安装路径正确
ssh root@192.168.1.2 "ls /usr/local/kafka/bin/kafka-server-start.sh"
# 4. 查看远程机器 Kafka 启动日志
ssh root@192.168.1.2 "tail -100 /usr/local/kafka/logs/server.log"
# 5. 检查远程机器配置文件是否正确
ssh root@192.168.1.2 "grep -E '^(node\.id|advertised\.listeners|controller\.quorum\.voters)' /usr/local/kafka/config/kraft/server.properties"
# 6. 检查远程机器数据目录是否存在并有写入权限
ssh root@192.168.1.2 "ls -ld /tmp/kafka-kraft-logs"
# 7. 检查远程机器端口是否被占用
ssh root@192.168.1.2 "ss -tlnp | grep -E '9092|9093'"
常见原因:
- Java 未安装或
JAVA_HOME未配置 - Kafka 安装目录路径不正确或文件不完整
server.properties配置错误(node.id、advertised.listeners等未正确修改)- 数据目录权限不足
- 端口被其他进程占用
Q3: Controller 选举失败
现象:
节点启动后日志中反复出现 Unable to begin voting 或 No leader found 等信息,kafka-metadata-quorum.sh describe --status 无法返回结果。
排查与解决:
# 1. 检查 3 个节点的 controller.quorum.voters 配置是否一致
echo "=== node1 ==="
grep controller.quorum.voters /usr/local/kafka/config/kraft/server.properties
echo "=== node2 ==="
ssh root@192.168.1.2 "grep controller.quorum.voters /usr/local/kafka/config/kraft/server.properties"
echo "=== node3 ==="
ssh root@192.168.1.3 "grep controller.quorum.voters /usr/local/kafka/config/kraft/server.properties"
# 3 个节点必须完全一致:
# controller.quorum.voters=1@192.168.1.1:9093,2@192.168.1.2:9093,3@192.168.1.3:9093
# 2. 检查 Controller 端口是否可达(从 node1 到其他节点)
telnet 192.168.1.2 9093
telnet 192.168.1.3 9093
# 3. 检查防火墙是否放行 Controller 端口(9093)
# 在远程机器上执行:
ssh root@192.168.1.2 "firewall-cmd --list-ports 2>/dev/null || iptables -L -n | grep 9093"
ssh root@192.168.1.3 "firewall-cmd --list-ports 2>/dev/null || iptables -L -n | grep 9093"
# 若需放行端口:
ssh root@192.168.1.2 "firewall-cmd --permanent --add-port=9093/tcp && firewall-cmd --reload"
ssh root@192.168.1.3 "firewall-cmd --permanent --add-port=9093/tcp && firewall-cmd --reload"
# 详细防火墙配置步骤参见「防火墙端口放行」章节
# 4. 检查 node.id 是否与 controller.quorum.voters 中的 ID 对应
echo "=== node1 ==="
grep node.id /usr/local/kafka/config/kraft/server.properties
echo "=== node2 ==="
ssh root@192.168.1.2 "grep node.id /usr/local/kafka/config/kraft/server.properties"
echo "=== node3 ==="
ssh root@192.168.1.3 "grep node.id /usr/local/kafka/config/kraft/server.properties"
# 5. 确认至少 2 个节点已启动(3 节点集群需多数派)
echo "本机 Kafka 进程:"
jps -l | grep kafka | wc -l
echo "远程 node2 Kafka 进程:"
ssh root@192.168.1.2 "jps -l 2>/dev/null | grep kafka | wc -l"
echo "远程 node3 Kafka 进程:"
ssh root@192.168.1.3 "jps -l 2>/dev/null | grep kafka | wc -l"
常见原因:
controller.quorum.voters配置在 3 个节点上不一致- Controller 端口(9093)被防火墙拦截
node.id与controller.quorum.voters中的 ID 不对应- 不足 2 个节点启动,无法达成多数派
Q4: 生产者连接超时
现象:
生产者或消费者连接 Kafka 时报 Connection to node -1 could not be established. Broker may not be available 或 TimeoutException。
排查与解决:
# 1. 检查 advertised.listeners 配置是否使用实际可达 IP
echo "=== node1 ==="
grep advertised.listeners /usr/local/kafka/config/kraft/server.properties
echo "=== node2 ==="
ssh root@192.168.1.2 "grep advertised.listeners /usr/local/kafka/config/kraft/server.properties"
echo "=== node3 ==="
ssh root@192.168.1.3 "grep advertised.listeners /usr/local/kafka/config/kraft/server.properties"
# 正确配置应为各节点实际 IP:
# node1: advertised.listeners=SASL_PLAINTEXT://192.168.1.1:9092
# node2: advertised.listeners=SASL_PLAINTEXT://192.168.1.2:9092
# node3: advertised.listeners=SASL_PLAINTEXT://192.168.1.3:9092
# 若配置为 localhost 或 127.0.0.1,外部客户端无法连接
# 2. 验证 Broker 端口可达性
telnet 192.168.1.1 9092
telnet 192.168.1.2 9092
telnet 192.168.1.3 9092
# 3. 检查防火墙是否放行 Broker 端口(9092)
ssh root@192.168.1.2 "firewall-cmd --list-ports 2>/dev/null"
ssh root@192.168.1.3 "firewall-cmd --list-ports 2>/dev/null"
# 若需放行端口:
ssh root@192.168.1.2 "firewall-cmd --permanent --add-port=9092/tcp && firewall-cmd --reload"
ssh root@192.168.1.3 "firewall-cmd --permanent --add-port=9092/tcp && firewall-cmd --reload"
# 详细防火墙配置步骤参见「防火墙端口放行」章节
# 4. 修改 advertised.listeners 后需重启对应节点
常见原因:
advertised.listeners未配置为实际可达 IP(最常见原因)- 防火墙未放行 9092 端口
- Broker 进程未启动或端口未监听
Q5: Cluster UUID 不一致
现象:
节点启动失败,日志中出现 Cluster UUID doesn't match stored UUID 或 The cluster id in the metadata log doesn't match 错误。
排查与解决:
# 1. 检查各节点 meta.properties 中的 cluster.id
echo "=== node1 ==="
cat /tmp/kafka-kraft-logs/meta.properties
echo "=== node2 ==="
ssh root@192.168.1.2 "cat /tmp/kafka-kraft-logs/meta.properties"
echo "=== node3 ==="
ssh root@192.168.1.3 "cat /tmp/kafka-kraft-logs/meta.properties"
# 2. 如果 cluster.id 不一致,需要重新格式化
# 2.1 停止所有节点
/home/admin/stop-cluster.sh
# 2.2 清理所有节点数据目录
rm -rf /tmp/kafka-kraft-logs/*
ssh root@192.168.1.2 "rm -rf /tmp/kafka-kraft-logs/*"
ssh root@192.168.1.3 "rm -rf /tmp/kafka-kraft-logs/*"
# 2.3 重新生成 UUID 并统一格式化
CLUSTER_UUID=$(/usr/local/kafka/bin/kafka-storage.sh random-uuid)
echo "新 Cluster UUID: $CLUSTER_UUID"
# 格式化 node1
/usr/local/kafka/bin/kafka-storage.sh format \
-t $CLUSTER_UUID \
-c /usr/local/kafka/config/kraft/server.properties
# 格式化 node2
ssh root@192.168.1.2 "/usr/local/kafka/bin/kafka-storage.sh format -t $CLUSTER_UUID -c /usr/local/kafka/config/kraft/server.properties"
# 格式化 node3
ssh root@192.168.1.3 "/usr/local/kafka/bin/kafka-storage.sh format -t $CLUSTER_UUID -c /usr/local/kafka/config/kraft/server.properties"
# 2.4 重新启动
/home/admin/start-cluster.sh
常见原因:
- 3 个节点使用了不同的 UUID 格式化(未使用同一 UUID)
- 某节点存在旧格式化数据(残留
meta.properties),新格式化未生效 - 数据目录未清理干净就重新格式化
Q6: Topic 副本分布不均
现象:
创建 Topic 后,kafka-topics.sh --describe 显示所有分区的 Leader 和副本集中在少数节点上。
排查与解决:
# 1. 查看副本分布
/usr/local/kafka/bin/kafka-topics.sh \
--describe \
--topic test-cluster \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 2. 配置 broker.rack 实现机架感知(可选)
# 在各节点 server.properties 中添加 broker.rack 配置:
# node1: broker.rack=rack1
# node2: broker.rack=rack2
# node3: broker.rack=rack3
# Kafka 会尽量将副本分布到不同 rack
# 3. 使用 kafka-reassign-partitions.sh 手动重新分配分区
# 先生成迁移计划 JSON 文件,再执行迁移
常见原因:
- 未配置
broker.rack,Kafka 默认按轮询分配(通常均匀,但非保证) - 某些节点启动较晚,创建 Topic 时尚未加入集群
- 集群中实际在线的 Broker 数不足 3 个
Q7: 磁盘空间不足
现象:
日志中出现 Disk usage exceeded 或 Kafka 写入变慢,df -h 显示磁盘使用率过高。
排查与解决:
# 1. 检查各节点磁盘使用情况
echo "=== node1 ==="
df -h /tmp
du -sh /tmp/kafka-kraft-logs
echo "=== node2 ==="
ssh root@192.168.1.2 "df -h /tmp && du -sh /tmp/kafka-kraft-logs"
echo "=== node3 ==="
ssh root@192.168.1.3 "df -h /tmp && du -sh /tmp/kafka-kraft-logs"
# 2. 查看各 Topic 占用空间(本机示例)
du -sh /tmp/kafka-kraft-logs/* | sort -rh | head -20
# 3. 调整日志保留策略(缩短保留时间)
/usr/local/kafka/bin/kafka-configs.sh \
--alter \
--topic <topic-name> \
--add-config retention.hours=24 \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 4. 调整日志段大小(减小单个日志段文件)
/usr/local/kafka/bin/kafka-configs.sh \
--alter \
--topic <topic-name> \
--add-config segment.bytes=536870912 \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 5. 手动删除不再需要的 Topic
/usr/local/kafka/bin/kafka-topics.sh \
--delete \
--topic <topic-name> \
--bootstrap-server 192.168.1.1:9092 \
--command-config /tmp/gw_data/kafka_client_sasl.properties
# 6. 全局调整 log.retention.hours(需修改配置文件并重启)
# 编辑各节点的 server.properties:
# log.retention.hours=24 # 从默认 168 小时缩短为 24 小时
Q8: 远程机器 KRaft 元数据残留导致启动失败
现象:
远程机器启动 Kafka 时报 Fencing metadata epoch 或 Cannot format directory that already contains meta.properties 等错误。
排查与解决:
# 1. 检查远程机器是否有残留 meta.properties
ssh root@192.168.1.2 "ls -la /tmp/kafka-kraft-logs/meta.properties 2>/dev/null"
ssh root@192.168.1.3 "ls -la /tmp/kafka-kraft-logs/meta.properties 2>/dev/null"
# 2. 检查是否有残留的 KRaft 元数据目录
ssh root@192.168.1.2 "ls -la /tmp/ | grep -E 'kraft|kafka'"
ssh root@192.168.1.3 "ls -la /tmp/ | grep -E 'kraft|kafka'"
# 3. 清理残留数据
ssh root@192.168.1.2 "rm -rf /tmp/kraft-combined-logs/* /tmp/kafka-logs/* /tmp/kafka-kraft-logs/* /tmp/kafka-kraft-node*/*"
ssh root@192.168.1.3 "rm -rf /tmp/kraft-combined-logs/* /tmp/kafka-logs/* /tmp/kafka-kraft-logs/* /tmp/kafka-kraft-node*/*"
# 4. 本机也需清理残留数据(如有伪分布式部署历史)
rm -rf /tmp/kraft-combined-logs/*
rm -rf /tmp/kafka-logs/*
rm -rf /tmp/kafka-kraft-logs/*
rm -rf /tmp/kafka-kraft-node1/* /tmp/kafka-kraft-node2/* /tmp/kafka-kraft-node3/*
# 5. 重新使用统一 UUID 格式化所有节点(见 KRaft 集群初始化章节)
常见原因:
- 远程机器之前运行过 Kafka(伪分布式或其他集群),数据目录残留旧元数据
- 多次格式化导致
meta.properties中 UUID 不一致 - 数据目录中有旧
kraft-combined-logs残留(本机特有)
Q9: SASL 认证失败
现象:
启动 Kafka 时日志报 org.apache.kafka.common.errors.SaslAuthenticationException 或 Authentication failed: Invalid username or password;CLI 命令报 ERROR SASL authentication failed 或连接被拒绝。
排查与解决:
# 1. 检查 JAAS 配置文件是否存在且内容正确
ls -la /usr/local/kafka/config/kafka_server_jaas.conf
cat /usr/local/kafka/config/kafka_server_jaas.conf
# 应包含 KafkaServer 块,username/password/user_admin 配置
# 2. 检查远程节点 JAAS 文件
ssh root@192.168.1.2 "cat /usr/local/kafka/config/kafka_server_jaas.conf"
ssh root@192.168.1.3 "cat /usr/local/kafka/config/kafka_server_jaas.conf"
# 3. 检查 KAFKA_OPTS 是否设置(启动时必须包含 JAAS 配置路径)
echo $KAFKA_OPTS
# 应输出: -Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf
# 4. 检查远程节点 KAFKA_OPTS 是否通过 SSH 正确传递
# 启动命令格式应为:
# ssh root@<IP> "export KAFKA_OPTS='-Djava.security.auth.login.config=/usr/local/kafka/config/kafka_server_jaas.conf' && ..."
# 5. 检查客户端 SASL 配置文件是否存在
ls -la /tmp/gw_data/kafka_client_sasl.properties
cat /tmp/gw_data/kafka_client_sasl.properties
# 应包含 security.protocol=SASL_PLAINTEXT, sasl.mechanism=PLAIN, sasl.jaas.config
# 6. 检查远程节点客户端配置文件
ssh root@192.168.1.2 "cat /tmp/gw_data/kafka_client_sasl.properties"
ssh root@192.168.1.3 "cat /tmp/gw_data/kafka_client_sasl.properties"
# 7. 确认所有 CLI 命令均带 --command-config 参数
# 错误示例: kafka-topics.sh --list --bootstrap-server 192.168.1.1:9092
# 正确示例: kafka-topics.sh --list --bootstrap-server 192.168.1.1:9092 \
# --command-config /tmp/gw_data/kafka_client_sasl.properties
# 8. 检查 JAAS 文件权限(应为 600)
ls -la /usr/local/kafka/config/kafka_server_jaas.conf
ssh root@192.168.1.2 "ls -la /usr/local/kafka/config/kafka_server_jaas.conf"
ssh root@192.168.1.3 "ls -la /usr/local/kafka/config/kafka_server_jaas.conf"
# 9. 检查 server.properties 中 SASL 相关配置
grep -E 'sasl\.(enabled\.mechanisms|mechanism\.inter\.broker\.protocol)' /usr/local/kafka/config/kraft/server.properties
# 应输出:
# sasl.enabled.mechanisms=PLAIN
# sasl.mechanism.inter.broker.protocol=PLAIN
常见原因:
- JAAS 配置文件缺失或内容错误(username/password 与 server.properties 不匹配)
- 启动时未设置
KAFKA_OPTS环境变量(JAAS 文件路径未传递给 JVM) - 客户端 SASL 配置文件缺失或未通过
--command-config参数指定 - JAAS 文件中
user_admin的密码与客户端配置中的password不一致 - 远程节点未分发 JAAS 文件或客户端配置文件
server.properties中缺少sasl.enabled.mechanisms=PLAIN配置
参考:JAAS 配置文件创建参见「JAAS 认证配置文件」章节,客户端配置文件创建参见「客户端 SASL 配置文件」章节。
附录:端口分配速查表
节点 node.id IP Broker 端口 Controller 端口 数据目录 配置文件 node1 1 192.168.1.1 9092 9093 /tmp/kafka-kraft-logs /usr/local/kafka/config/kraft/server.properties node2 2 192.168.1.2 9092 9093 /tmp/kafka-kraft-logs /usr/local/kafka/config/kraft/server.properties node3 3 192.168.1.3 9092 9093 /tmp/kafka-kraft-logs /usr/local/kafka/config/kraft/server.properties 脚本文件清单:
脚本 路径 功能 format-cluster.sh /home/admin/format-cluster.sh 一键格式化 3 节点目录 start-cluster.sh /home/admin/start-cluster.sh 一键启动 3 节点集群 stop-cluster.sh /home/admin/stop-cluster.sh 一键停止 3 节点集群