Kafka 集群安装与运维实战:消费组排查、Offset 重置与副本重分配

Kafka 集群安装与运维实战:消费组排查、Offset 重置与副本重分配

Kafka 的日常运维里,真正让人头疼的往往不是「怎么启动」,而是三件事:消费组积压了怎么定位、位移(Offset)乱了怎么重置、__consumer_offsets 这种内部主题副本数不够怎么改。这三件事各自都有坑,而且坑基本都藏在客户端工具的版本差异和参数语义里。

本文按「安装 → 配置 → 使用 → 排查」的顺序,把一套 3 节点 Kafka 集群从部署到应急处理的完整操作整理成一份可以照着敲的手册。文中所有主机名、IP 都做了脱敏(用 kafka1 / kafka2 / kafka3 代替),实际执行时替换成自己的地址即可。

一、速查:本文覆盖的运维场景

场景 关键命令 所在章节
集群部署与参数调整 kafka-server-start.sh
主题查看与创建 kafka-topics.sh
消费组积压排查 kafka-consumer-groups.sh --describe
重置消费位移 kafka-consumer-groups.sh --reset-offsets
内部主题副本数调整 kafka-reassign-partitions.sh
数据保留与磁盘治理 log.retention.*
常见报错定位 ------

二、集群安装与核心配置

1、分发安装包

集群规模上来以后逐台 scp 很费劲,用 SaltStack 之类的批量工具下发更省事;没有批量工具时把下面每条命令逐台执行,效果一样。

bash 复制代码
# 批量分发并解压安装包
salt -N 'kafka' cp.get_file salt://kafka_2.12-2.2.1.tgz /data/kafka_2.12-2.2.1.tgz
salt -N 'kafka' cmd.run 'cd /data && tar xf kafka_2.12-2.2.1.tgz'

# 日志目录从默认的 /tmp 改到数据盘
sed -i 's#log.dirs=/tmp/kafka-logs#log.dirs=/data/kafka_2.12-2.2.1/logs#g' \
  /data/kafka_2.12-2.2.1/config/server.properties

# 创建日志目录
mkdir -p /data/kafka_2.12-2.2.1/logs

# 指向 ZooKeeper 集群
sed -i 's#zookeeper.connect=localhost:2181#zookeeper.connect=kafka1:2181,kafka2:2181,kafka3:2181#g' \
  /data/kafka_2.12-2.2.1/config/server.properties

# broker.id 必须每台不同,手工确认
vi /data/kafka_2.12-2.2.1/config/server.properties

# 后台启动
/data/kafka_2.12-2.2.1/bin/kafka-server-start.sh -daemon \
  /data/kafka_2.12-2.2.1/config/server.properties

2、server.properties 关键参数

参数 建议值 说明
broker.id 0 / 1 / 2 集群内唯一,改动后数据目录会错乱,务必先规划
log.dirs /data/kafka/logs 生产环境千万别留在 /tmp,重启即丢
zookeeper.connect kafka1:2181,kafka2:2181,kafka3:2181 老架构依赖 ZK,新架构见下方提示
num.partitions 3 新建主题的默认分区数
default.replication.factor 3 新建主题的默认副本数,集群只有 1 台时创建主题会直接失败
offsets.topic.replication.factor 3 __consumer_offsets 的副本数,只在首次创建时生效
transaction.state.log.replication.factor 3 事务主题副本数
transaction.state.log.min.isr 2 事务写入要求的最小同步副本数
message.max.bytes 20971520 单条消息上限,默认 1 MB;调大后服务端和客户端都要改

架构提示 :Kafka 2.8 起引入 KRaft(去 ZooKeeper)模式,3.3 之后达到生产可用,4.0 已彻底移除 ZooKeeper。新建集群建议直接用 KRaft ,可以少维护一套 ZK;本文仍以经典的 ZK 架构讲解,因为存量集群大多是这一套,而且下面第六节的 __consumer_offsets 处理方式两者完全一致。

注意 offsets.topic.replication.factor 这类参数只在主题第一次被创建时生效 。事后改配置文件重启,__consumer_offsets 的副本数并不会跟着变------这正是下一节要用重分配工具的原因。

3、启动后自检

bash 复制代码
# 确认 broker 已注册
zookeeper-shell.sh kafka1:2181 ls /brokers/ids

# 列出集群内所有主题
kafka-topics.sh --bootstrap-server kafka1:9092 --list

三、日常运维命令速查

用途 命令
列出全部主题 kafka-topics.sh --bootstrap-server kafka1:9092 --list
查看主题详情 kafka-topics.sh --bootstrap-server kafka1:9092 --describe --topic test-topic
创建主题 kafka-topics.sh --bootstrap-server kafka1:9092 --create --topic test-topic --partitions 6 --replication-factor 3
增加分区 kafka-topics.sh --bootstrap-server kafka1:9092 --alter --topic test-topic --partitions 12
列出消费组 kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --list
查看消费组详情 kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --group apigw-group1 --describe
列出所有消费组详情 kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --all-groups --describe
查看主题数据 kafka-console-consumer.sh --bootstrap-server kafka1:9092 --topic test-topic --from-beginning
生产测试数据 kafka-console-producer.sh --bootstrap-server kafka1:9092 --topic test-topic

几个使用要点:

  • 主题分区数只能增加,不能减少。想减少只能新建主题再迁移数据,规划时留足余量。
  • kafka-console-consumer.sh --from-beginning 会把整个主题从头拉一遍。生产环境主题数据量大时不要执行 ,会瞬间打满网络和磁盘 IO,需要抽样确认用 --max-messages 限制条数。
  • 主题、消费组数量一多,--describe 的输出会很长,配合 grep / awk 过滤更实用。

四、消费组积压排查

1、读懂 --describe 的输出

bash 复制代码
kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --group apigw-group1 --describe
字段 含义
GROUP 消费组名
TOPIC 主题名
PARTITION 分区号
CURRENT-OFFSET 该分区当前已提交的位移
LOG-END-OFFSET 该分区最新消息的位移(可理解为「总共有多少条」)
LAG 积压条数,等于 LOG-END-OFFSET - CURRENT-OFFSET
CONSUMER-ID 正在消费该分区的消费者实例,- 表示没有消费者在消费

判断逻辑很直接:

  • 某分区 CONSUMER-ID-LAG 持续增长 → 没有消费者认领这个分区,通常是实例挂了或消费组一直卡在重平衡;
  • LAG 平稳但不下降 → 消费速度追不上生产速度,需要扩容消费者或优化处理逻辑;
  • LAG 来回跳变 → 多半是在反复重平衡。

2、快速筛出有积压的组

bash 复制代码
kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --all-groups --describe \
  | awk 'NR==1 || $6>0' | sort -k6 -nr | head -30

--all-groups 输出中 GROUP 是第 1 列,LAG 是第 6 列,所以 $6>0 就能过滤掉全部消费完毕的分区。

3、几个容易被忽略的约束

  • 同一消费组内,消费者实例数超过分区数就是浪费。多出来的实例会一直空转,日志上看不到任何错误,但扩容完全没有效果。遇到这种情况要先加分区数。
  • 消费者处理太慢导致被踢出组,看的是 max.poll.interval.ms(默认 5 分钟);心跳超时看 session.timeout.ms。这两个参数别混。
  • 消费组「正在重平衡」时执行任何 Offset 操作都会失败,见下一节。

五、Offset 重置

位移重置是事故处理里的高频操作,也是最容易出事的一个:执行前一定要先 --dry-run 看一眼

1、前置条件

条件 说明
消费组必须处于 inactive 状态 组内还有消费者在线时会报 Assignments can only be reset if the group ... is inactive,必须先停掉全部消费者实例
--bootstrap-server 而不是 --zookeeper --zookeeper 从 2.5 起弃用,4.0 已移除,新命令一律走 bootstrap
--dry-run--execute --dry-run 只打印即将变更的结果,不会真正提交

2、标准流程

bash 复制代码
# 第一步:预览(不执行)
kafka-consumer-groups.sh \
  --bootstrap-server kafka1:9092 \
  --group apigw-group1 \
  --reset-offsets \
  --to-earliest \
  --topic test-topic-a1 \
  --dry-run

# 第二步:确认无误后执行
kafka-consumer-groups.sh \
  --bootstrap-server kafka1:9092 \
  --group apigw-group1 \
  --reset-offsets \
  --to-earliest \
  --topic test-topic-a1 \
  --execute

常用重置策略:

参数 效果
--to-earliest 回到最早可用位移,相当于「全部重新消费一遍」
--to-latest 跳到最新位移,相当于「丢弃全部积压」
--to-offset 100 重置到指定位移,适合对齐到某个业务时间点
--to-datetime 2026-09-01T00:00:00.000 按时间点重置,格式为 ISO8601
--shift-by -1000 在当前位移上回退 1000 条,适合小范围重放
--from-file offsets.csv 从文件批量导入位移,适合跨集群搬迁

风险提示--to-latest 会让积压数据永久跳过,--to-earliest 会触发全量重放,对下游数据库的压力可能远大于预期。执行前先确认下游能不能承受重复消费,幂等没做好就不要动。

3、服务端与客户端版本不一致的情况

老集群(例如 HDP 2.6.4 自带的 Kafka 0.10)的服务端可能不支持新参数,但客户端工具是可以「借用」的------客户端不需要启动,只需要有二进制包

bash 复制代码
# 用旧版本客户端查询(兼容老服务端)
cd /usr/hdp/2.6.4.0-91/kafka/bin/

kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --list
kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --group apigw-groupv2 --describe

# 用新版本客户端执行重置(工具本身不做版本校验)
cd /data/opt/kafka_2.12-2.2.1/bin

kafka-consumer-groups.sh \
  --bootstrap-server kafka1:9092 \
  --group apigw-group99 \
  --reset-offsets \
  --to-offset 100 \
  --topic test-topic-a1 \
  --execute

六、修改 __consumer_offsets 副本数

__consumer_offsets 是 Kafka 存放消费者位移的内部主题,默认 50 个分区、cleanup.policy=compact。它的默认副本数由 offsets.topic.replication.factor 决定------如果集群刚建起来时只有 1 台 broker,这个主题就会以单副本创建,之后多加 broker 也不会自动补副本

单副本的后果很严重:承载该分区的 broker 一挂,消费组的位移就不可读,整个组卡死无法重平衡。而由于 offsets.topic.replication.factor 事后修改无效,只能通过分区重分配来补救。

1、修改 Kafka 配置

server.properties配置变更后需要重启

properties 复制代码
default.replication.factor=2
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2

2、创建待迁移主题列表

创建 topics.json

json 复制代码
{
  "topics": [
    {
      "topic": "__consumer_offsets"
    }
  ],
  "version": 1
}

3、生成分区重新分配方案

bash 复制代码
kafka-reassign-partitions.sh \
  --zookeeper kafka1:2181,kafka2:2181,kafka3:2181 \
  --topics-to-move-json-file topics.json \
  --broker-list "0,1,2" \
  --generate | awk 'END {print}' | jq '
  def rotate(arr; n):
    arr[n:] + arr[:n];

  .partitions |= map(
    .replicas = rotate([0,1,2]; (.partition % 3)) |
    .log_dirs = ["any", "any", "any"]
  )
' > reassignment-plan.json

这里做了两件事:--generate 会输出「当前分配」和「建议分配」两段 JSON,awk 'END {print}' 取最后一行(即建议方案);后面用 jq 把副本列表按分区号轮转,让副本均匀落在 3 台 broker 上,而不是全部堆在 0,1,2 这个固定顺序上。

生成后务必人工检查 reassignment-plan.json 是否符合预期:

bash 复制代码
less reassignment-plan.json

4、执行重新分配

bash 复制代码
kafka-reassign-partitions.sh \
  --zookeeper kafka1:2181,kafka2:2181,kafka3:2181 \
  --reassignment-json-file reassignment-plan.json \
  --execute

性能提示 :重分配本质是把分区数据在节点间整体复制一遍。数据量大时务必加 --throttle 50000000(单位字节/秒,即 50 MB/s)限速,否则容易把磁盘 IO 和网络打满,在业务高峰引发连锁故障。限速后可通过 --verify 观察进度,完成后记得用 --throttle 0 或再执行一次解除限速。

5、验证分配进度

bash 复制代码
kafka-reassign-partitions.sh \
  --zookeeper kafka1:2181,kafka2:2181,kafka3:2181 \
  --reassignment-json-file reassignment-plan.json \
  --verify

输出逐分区显示状态,全部为 completed successfully 即表示完成。中途中断不会回滚,重新执行 --execute 会从断点继续。

6、检查最终副本数

bash 复制代码
kafka-topics.sh \
  --bootstrap-server kafka1:9092,kafka2:9092,kafka3:9092 \
  --describe \
  --topic __consumer_offsets

预期输出:

text 复制代码
Topic:__consumer_offsets	PartitionCount:50	ReplicationFactor:3	Configs:compression.type=producer,cleanup.policy=compact,segment.bytes=104857600
	Topic: __consumer_offsets	Partition: 0	Leader: 0	Replicas: 0,1,2	Isr: 0,1,2
	Topic: __consumer_offsets	Partition: 1	Leader: 1	Replicas: 0,1,2	Isr: 0,1,2
	Topic: __consumer_offsets	Partition: 2	Leader: 2	Replicas: 0,1,2	Isr: 0,1,2

关键看三列:ReplicationFactor 是否变成 3、每个分区的 Replicas 是否为 3 个副本、Isr 是否与 Replicas 一致。Isr 少于 Replicas 说明有副本没追上,需要继续观察。

7、Leader 重新选举(可选)

重分配完成后,Leader 分布可能仍然不均衡。触发一次优先副本选举即可让 Leader 回到 Replicas 列表的第一位:

bash 复制代码
# 生成分区列表
kafka-topics.sh \
  --bootstrap-server kafka1:9092,kafka2:9092,kafka3:9092 \
  --describe \
  --topic __consumer_offsets | \
  grep "Partition:" | awk '{print $4}' | \
  jq -Rn '[inputs|{"topic":"__consumer_offsets","partition":(tonumber)}] | {"partitions": .}' \
  > partitions.json

# 执行优先副本选举
kafka-preferred-replica-election.sh \
  --bootstrap-server kafka1:9092,kafka2:9092,kafka3:9092 \
  --path-to-json-file partitions.json

再次 --describe 确认 Leader 是否均匀分布。注意 kafka-preferred-replica-election.sh 在 2.4 之后也有对应的 --zookeeper--bootstrap-server 两种形态,新版本只能用后者。

七、日志清理与数据保留

Kafka 的数据不会自动消失,磁盘写满是最常见的生产事故之一。在 server.properties 中配置:

properties 复制代码
# 开启日志清理
log.cleaner.enable=true

# 日志保留小时数,默认 7 天(168 小时)
log.retention.hours=168

# 日志保留总字节数,超过会清理
log.retention.bytes=419430400

# 每个日志分段文件大小
log.segment.bytes=1073741824

# 日志清理检查间隔
log.retention.check.interval.ms=300000

几个关键语义,理解错了很容易误判:

  • log.retention.bytes 限制的是每个分区的日志总量,不是整台 broker 的总量。50 个分区 × 400 MB = 20 GB,算容量时要按分区数乘。
  • 实际生效的保留策略是「时间」和「大小」取先满足的那个,任一条件触发就会删除分段。
  • 删除的最小单位是分段(segment) ,不是单条消息。所以 log.segment.bytes 设得太大,会导致 log.retention.hours 明显滞后生效------一个还没写满的活跃分段不会被删。
  • cleanup.policy=delete 是按保留策略删除;compact 是按 key 保留最新值。__consumer_offsets 用的是 compact,不要随意改动。
  • log.retention.check.interval.ms 决定清理任务的扫描频率,默认 5 分钟。磁盘告警时把值调小可以更快释放空间,但会增加 IO 压力。

八、常见故障排查

现象 可能原因 处理
消费组长时间卡在 PreparingRebalance 消费者处理超时被踢出组、分区数过多导致重平衡慢 调大 group.initial.rebalance.delay.mssession.timeout.ms;核对 max.poll.interval.ms 是否小于单批处理耗时
消费者反复重连、日志刷 Rebalance 心跳超时,或一次 poll 拉取数据过多 调小 max.poll.records,调大 session.timeout.ms
ISR 频繁收缩、副本同步滞后 磁盘 IO 瓶颈、网络抖动、单副本数据量过大 检查磁盘 iostat,评估调大 replica.lag.time.max.ms
RecordTooLargeException 单条消息超过上限 三处都要放开:服务端 message.max.bytes、broker 间 replica.fetch.max.bytes、消费者 fetch.message.max.bytes
创建主题报 replication factor: 3 larger than available brokers: 1 集群可用 broker 数少于副本数 补齐 broker,或按实际节点数指定 --replication-factor
__consumer_offsets 单副本告警 建库时集群只有 1 台 broker 按第六节做分区重分配
加了消费者但积压不降 实例数已超过分区数 先扩分区,再扩消费者实例
提示 Unrecognized option: --zookeeper 工具版本已移除 ZK 参数 统一改用 --bootstrap-server
磁盘使用率持续上涨不释放 活跃分段未写满,或保留策略单位理解有误 核对 log.segment.byteslog.retention.bytes 的分区级语义

总结

把上面这些串起来,Kafka 运维其实可以归纳成四条主线:

  1. 部署阶段就要把副本数定对offsets.topic.replication.factor 这种参数只在主题首次创建时生效,等事后发现 __consumer_offsets 是单副本,补救成本是整整一节的分区重分配流程。新集群建议直接上 KRaft,省掉 ZooKeeper 这一层。
  2. 消费组问题先看 LAG--all-groups --describe 配合 awk 过滤,几秒钟就能判断是没消费者、消费太慢,还是卡在重平衡,再决定是扩容、调参还是重置位移。
  3. 一切破坏性操作先 --dry-run 。位移重置前必须停掉消费者,--to-latest 会永久跳过数据,--to-earliest 会全量重放,下游不幂等就别做。
  4. 重分配一定要限速--throttle 加上去,慢一点没关系,把生产集群打挂才是真的麻烦。

磁盘和副本这两件事属于「不处理就一定会出事」的类型,建议给 log.retention.bytes__consumer_offsets 的副本状态都加上监控告警,别等到故障了再翻文档。

相关推荐
桌面运维家1 小时前
云桌面一个点位多少钱:IDV 云桌面按终端数还是并发数算预算
运维·虚拟化·云桌面
飞飞传输1 小时前
政府信创改造,怎样搭建安全可控的信创文件传输系统?
大数据·运维·安全
szxinmai主板定制专家1 小时前
NXP LS1046A 硬件方案: LS1046A+FPGA 异构加速架构实践
大数据·图像处理·人工智能·嵌入式硬件·fpga开发
IT邦德1 小时前
BIC-QA如何重新定义数据库知识服务
运维·数据库
商业看点解说1 小时前
小型创业团队开发 AI Agent 产品,哪些云平台可以降低模型接入与基础设施门槛?
大数据·人工智能
逐米时代2 小时前
BOM智能构建:全链路一致性自动校验
大数据·数据库·人工智能
计算机源码社2 小时前
基于大数据技术的台北市住宅价格影响因素挖掘与可视化分析-基于Python与Hadoop的台北市住宅价格数据仓库构建与可视化
大数据·hadoop·python·数据分析·spark·毕业设计·数据可视化
BJ_Kingfisher2 小时前
GPT Image 2.5 到底改了什么:一个单模型拆成了两条道
大数据·科技·gpt
疯狂小猫咪2 小时前
教务管理系统课时消课模块设计思路
大数据·小程序