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 台独立物理机器 上部署 3 个 Kafka KRaft 节点 ,构成真分布式集群。每台机器运行一个 broker+controller 联合节点,各节点使用统一的端口(9092/9093),通过不同 IP 地址区分。

bash 复制代码
    ┌──────────────────────┐   ┌──────────────────────┐   ┌──────────────────────┐
    │   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 环境。

bash 复制代码
# 本机 (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 安装包分发](#Kafka 安装包分发 "#kafka-%E5%AE%89%E8%A3%85%E5%8C%85%E5%88%86%E5%8F%91") 章节中的 Java 安装步骤。

2. 网络连通性测试

确保 3 台机器之间网络互通:

bash 复制代码
# 从 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 端口均未被占用:

bash 复制代码
# 本机 (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 数据目录需要足够的磁盘空间:

bash 复制代码
# 本机 (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 安装路径确认

bash 复制代码
# 确认 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)必须被各节点防火墙放行,否则会导致节点间无法通信、客户端无法连接等问题。

检查远程防火墙状态

bash 复制代码
# 检查 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 端口:

bash 复制代码
# 放行 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,使用以下命令:

bash 复制代码
# 放行 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"

验证端口放行结果

bash 复制代码
# 验证 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/tcp9093/tcp,表示端口已成功放行。


SSH 免密登录配置

多机部署需要频繁通过 SSH 在各节点间执行命令,配置免密登录可避免每次输入密码,大幅提升操作效率。

1. 本机生成 SSH 密钥对

在 node1(本机)以 admin 用户身份生成密钥对:

bash 复制代码
# 生成 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 的授权密钥文件中:

bash 复制代码
# 分发公钥到 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. 验证免密登录

bash 复制代码
# 验证到 node2 免密登录
ssh root@192.168.1.2 "hostname && whoami"

# 验证到 node3 免密登录
ssh root@192.168.1.3 "hostname && whoami"

预期结果:无需输入密码即可登录,并输出远程机器的主机名和当前用户名。

4. 常见问题

首次连接 known_hosts 确认

首次 SSH 连接新主机时,会提示确认主机指纹:

vbnet 复制代码
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 对目录权限要求严格,权限不正确会导致免密登录失败:

bash 复制代码
# 在远程机器上检查并修复权限(若免密登录不生效)
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 文件中追加映射内容:

bash 复制代码
# 需要追加的 hosts 内容
192.168.1.1  kafka-node1
192.168.1.2  kafka-node2
192.168.1.3  kafka-node3
在本机 (node1) 配置
bash 复制代码
# 追加 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 配置
bash 复制代码
# 通过 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 配置
bash 复制代码
# 通过 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 解析

bash 复制代码
# 在本机验证解析
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 分发安装包到远程机器

bash 复制代码
# 在远程机器创建目标目录
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

bash 复制代码
# 在 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 安装

bash 复制代码
# 验证 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 手动安装:

bash 复制代码
# 检查 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 环境变量已配置:

bash 复制代码
# 检查 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 手动安装(推荐,与本机安装方式一致):

bash 复制代码
# 在 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"
bash 复制代码
# 在 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 而失败。

bash 复制代码
# 在 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 配置:

bash 复制代码
# 验证 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 进程

bash 复制代码
# 检查本机 (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"

若有残留进程,先停止:

bash 复制代码
# 停止本机 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 元数据清理

本机可能存在之前伪分布式部署的残留数据,必须清理:

bash 复制代码
# 清理本机所有残留 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. 远程机器残留数据清理

bash 复制代码
# 清理远程 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

创建数据目录

bash 复制代码
# 在本机创建数据目录
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

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

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

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.idadvertised.listeners 两项不同,其余配置完全一致。所有节点均已启用 SASL_PLAINTEXT 认证(Broker 端口 9092),Controller 端口 9093 保持明文通信。

逐项关键配置说明

controller.quorum.voters 格式
css 复制代码
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.factortransaction.state.log.replication.factor 设为 3,确保每个内部 Topic 的副本分布在所有节点上,实现最高容错能力(允许 1 个节点故障时数据仍可用)。

配置文件部署方式

本机 (node1) 直接编辑
bash 复制代码
# 备份原始配置文件
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 分发到远程机器:

bash 复制代码
# 在本机创建临时目录存放各节点配置
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 配置
bash 复制代码
# 修改本机 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
验证所有节点配置
bash 复制代码
# 验证 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=1advertised.listeners=SASL_PLAINTEXT://192.168.1.1:9092listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093
  • node2:node.id=2advertised.listeners=SASL_PLAINTEXT://192.168.1.2:9092listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093
  • node3:node.id=3advertised.listeners=SASL_PLAINTEXT://192.168.1.3:9092listeners=SASL_PLAINTEXT://:9092,CONTROLLER://:9093
  • 3 个节点的 controller.quorum.voters 配置完全一致
  • 3 个节点均已启用 SASL 认证(sasl.enabled.mechanisms=PLAINinter.broker.listener.name=SASL_PLAINTEXT

JAAS 认证配置文件

启用 SASL 认证后,每个 Kafka 节点需要配置 JAAS(Java Authentication and Authorization Service)文件,定义服务端认证信息。

在本机 (node1) 创建 JAAS 配置文件
bash 复制代码
# 创建 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 配置文件到远程节点
bash 复制代码
# 分发 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 配置文件中 usernamepassword 用于 Broker 间通信认证,user_admin 定义了客户端连接时使用的用户凭证。所有节点使用相同的 JAAS 配置。

客户端 SASL 配置文件

所有 Kafka CLI 工具(kafka-topics.shkafka-console-producer.sh 等)连接启用 SASL 认证的集群时,需要提供客户端认证配置。通过 --command-config 参数指定配置文件路径。

在本机创建客户端 SASL 配置文件
bash 复制代码
# 创建客户端 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 配置文件到远程节点
bash 复制代码
# 分发到 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.shkafka-console-producer.shkafka-console-consumer.sh 等)必须追加 --command-config /tmp/gw_data/kafka_client_sasl.properties 参数,否则将因认证失败而无法连接集群。后续文档中所有 CLI 命令均已包含此参数。


KRaft 集群初始化

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

重要:记录此 UUID 值,后续 3 个节点格式化时必须使用同一个值。

步骤 2:使用同一 UUID 格式化本机 node1

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.properties

步骤 3:将 UUID 传递到远程机器并格式化 node2 和 node3

通过 SSH 远程执行格式化命令,将 $CLUSTER_UUID 变量作为参数传入:

bash 复制代码
# 将上一步生成的 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 字面量嵌入命令中更简单,例如:

bash 复制代码
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 节点格式化结果

bash 复制代码
# 检查本机 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。

一键格式化脚本

bash 复制代码
#!/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')"

赋予执行权限:

bash 复制代码
chmod +x /home/admin/format-cluster.sh

启动集群

手动逐节点启动

按顺序依次启动 3 个节点,使用 -daemon 参数后台运行:

bash 复制代码
# 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

启动顺序说明

  1. 先启动 node1:作为第一个 Controller,node1 会发起 Raft 选举并成为初始 Leader
  2. 再启动 node2:加入仲裁集群,参与投票
  3. 最后启动 node3:加入后集群达到 3 节点,满足多数派(2/3)条件,仲裁稳定

注意:虽然启动顺序不影响最终集群状态,但按顺序启动可以减少选举抖动,便于观察启动过程。启动间隔建议 5-10 秒,确保前一个节点的 Controller 服务就绪后再启动下一个。

一键启动脚本

文件路径: /home/admin/start-cluster.sh

bash 复制代码
#!/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"

赋予执行权限并运行:

bash 复制代码
# 赋予执行权限
chmod +x /home/admin/start-cluster.sh

# 执行启动脚本
/home/admin/start-cluster.sh

启动后验证

bash 复制代码
# 检查本机 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 节点正常

查看启动日志方法

bash 复制代码
# 查看本机 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 仲裁状态

bash 复制代码
# 查看集群元数据仲裁状态
/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 副本)

bash 复制代码
# 创建 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 详情和副本分布

bash 复制代码
# 查看所有 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-cluster Topic 有 3 个分区,每个分区的副本分布在 3 个不同 Broker 上(Leader 在其中 1 个),ISR 列表与副本列表一致。

4. 生产消息测试

bash 复制代码
# 启动控制台生产者,发送消息到 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. 消费消息测试

bash 复制代码
# 启动控制台消费者,从头开始消费 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 地址连接,验证集群内数据可跨节点访问:

bash 复制代码
# 通过 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 副本分布

bash 复制代码
# 查看 __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

停止集群

手动逐节点停止

多机集群停止时,建议先停远程节点再停本机:

bash 复制代码
# 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 进程,因此可直接使用而无需指定配置文件(区别于伪分布式环境)。

确认所有进程已停止

bash 复制代码
# 确认本机无 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 强制终止:

bash 复制代码
# 本机强制终止残留 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

bash 复制代码
#!/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"

赋予执行权限:

bash 复制代码
chmod +x /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 \
  --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

分区扩容

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 \
  --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

消费者组查看

bash 复制代码
# 查看所有消费者组
/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

磁盘使用监控

bash 复制代码
# 查看本机数据目录磁盘占用
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

集群元数据查看

bash 复制代码
# 查看集群元数据仲裁状态
/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 deniedConnection refusedConnection timed out

排查与解决:

bash 复制代码
# 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 启动失败

现象: 远程机器执行启动命令后,进程未启动或立即退出。

排查与解决:

bash 复制代码
# 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.idadvertised.listeners 等未正确修改)
  • 数据目录权限不足
  • 端口被其他进程占用

Q3: Controller 选举失败

现象: 节点启动后日志中反复出现 Unable to begin votingNo leader found 等信息,kafka-metadata-quorum.sh describe --status 无法返回结果。

排查与解决:

bash 复制代码
# 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.idcontroller.quorum.voters 中的 ID 不对应
  • 不足 2 个节点启动,无法达成多数派

Q4: 生产者连接超时

现象: 生产者或消费者连接 Kafka 时报 Connection to node -1 could not be established. Broker may not be availableTimeoutException

排查与解决:

bash 复制代码
# 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 UUIDThe cluster id in the metadata log doesn't match 错误。

排查与解决:

bash 复制代码
# 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 和副本集中在少数节点上。

排查与解决:

bash 复制代码
# 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 显示磁盘使用率过高。

排查与解决:

bash 复制代码
# 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 epochCannot format directory that already contains meta.properties 等错误。

排查与解决:

bash 复制代码
# 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.SaslAuthenticationExceptionAuthentication failed: Invalid username or password;CLI 命令报 ERROR SASL authentication failed 或连接被拒绝。

排查与解决:

bash 复制代码
# 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 节点集群
相关推荐
小蒜学长1 小时前
基于Django的社区团购购物平台的设计与实现(代码+数据库+LW)
数据库·后端·python·django
灯澜忆梦1 小时前
【基于GO的Web开发9】gin框架返回json
前端·后端·golang·gin
kymjs张涛1 小时前
DeepSeek Harness源码分析:非 AI 开发者的 Agent 学习总结
前端·后端·面试
jj_ccwgw1 小时前
UOS系统 Kafka KRaft 集群安装指南
后端
rannn_1111 小时前
Java 后端面试题总结:点赞功能怎么实现?
java·后端·面试
__zRainy__1 小时前
Node系列 · 数据库:MySQL 安装
数据库·后端·mysql·node.js
Zane19941 小时前
threading、multiprocessing、asyncio 到底怎么选?一张图 + 三组实测数据说清楚
后端·python
程序员cxuan2 小时前
DeepSeek 多模态深度实测,确实有东西的
人工智能·后端·程序员
evans在进步2 小时前
Spring Boot 核心机制详解:自动装配、事件监听、异步任务与请求映射
java·spring boot·后端