Kafka 磁盘空间治理实战:retention 保留策略、log.dirs 在线扩容、分区跨盘迁移与不停机整体换盘

消息队列 Kafka 用得越久,磁盘水位告警来得越勤:日志类 topic 日增几十 GB、大分区全挤在一块盘上、系统盘被 server.log 慢慢吃光。本文把生产环境沉淀的四类存储治理手段整理成一套完整 SOP------保留策略让数据"删得快"、数据目录扩容让集群"装得下"、分区跨盘迁移让磁盘"搬得平"、整体换盘让硬件"换得走",全部支持滚动操作不停机,附踩坑清单。命令在 Kafka 2.5 与 3.9 集群上均验证过。
📚 相关系列:《Kafka 集群开启 SASL 认证实战(一):内置 ZooKeeper 场景》《Kafka 2.8→3.9.1 跨大版本升级实战》《云原生日志采集与检索架构实战:Kafka + Elasticsearch》------本文专注存储与磁盘维度,认证与升级问题看前两篇。

治理全景:先分清四种场景

收到磁盘告警,先判断属于哪一类,再对症下药:

现象 根因 对应方案 是否停机
磁盘水位持续上涨,历史数据没人消费 保留策略缺失或太长 方案一:保留策略 否
新增磁盘已挂载,但 Kafka 用不上 log.dirs 只配了旧目录 方案二:目录扩容 滚动重启
某块盘快满,其他盘很空 分区分布不均 方案三:分区跨盘迁移 否
磁盘要下线/换 SSD/迁移存储 硬件变更 方案四:整体换盘 滚动迁移

方案一:保留策略------让 Kafka 自动删数据

Kafka 清理策略分两个级别:全局配置 作用于所有主题(server.properties),主题级配置只作用于自身并覆盖全局。日增量大的日志类消息队列,两个级别通常都要配。

1.1 全局配置

修改每个节点的 server.properties,然后滚动重启:

properties 复制代码
# 数据最大保留小时数(示例 7 天,默认也是 7 天)
log.retention.hours=168

# 数据最大保留字节数(示例 10GB)
log.retention.bytes=10737418240

# 每个节点的保留检查间隔(默认 5 分钟)
log.retention.check.interval.ms=300000

语义是"或 "的关系:分区内数据的时间超过 7 天,或者分区总大小超过 10GB,满足任一条件就会触发删除------所以流量大的分区实际按大小删,流量小的分区按时间删。

1.2 主题级配置(不用重启)

全局配置改一次要滚动重启整个集群,主题级的调整用 kafka-configs.sh 动态生效,单机或集群执行一次即可:

bash 复制代码
# 设置某个主题数据最大保留毫秒数(示例 24 小时)
./bin/kafka-configs.sh --alter \
  --add-config retention.ms=86400000 \
  --bootstrap-server=192.168.10.31:9092 \
  --topic app-log \
  --command-config ./config/producer.properties

# 设置某个主题数据最大保留字节数(示例 10GB)
./bin/kafka-configs.sh --alter \
  --add-config retention.bytes=10737418240 \
  --bootstrap-server=192.168.10.31:9092 \
  --topic app-log \
  --command-config ./config/producer.properties

--command-config 指向带认证信息的配置文件,开了 SASL 的集群必带(怎么生成这个文件见系列第一篇);没开认证的集群可以省略。

1.3 验证配置是否生效

bash 复制代码
./bin/kafka-configs.sh --bootstrap-server 192.168.10.31:9092 \
  --describe --all --topic app-log \
  --command-config ./config/producer.properties | grep "retention"

输出中 retention.ms=86400000 即 24 小时、retention.bytes=10737418240 即 10GB,说明主题级覆盖已生效。

1.4 三个高频踩坑

坑一:retention.bytes 是"每分区"上限,不是主题总量。 topic 有 6 个分区、每分区限 10GB,主题实际能囤 60GB。想控制主题总量,要用 每分区上限 = 主题期望总量 / 分区数 反推。

坑二:设了保留 1 小时,数据却活了 7 天。 删除的最小单位是日志段(segment)文件,不是单条消息 ,且活跃段永远不删------只有 roll 出新段后,旧段才可能过期。低流量 topic 长时间写不满一个段(默认 1GB),时间早就"过期"了却一直删不掉。解法是配小段,让段滚动起来:

properties 复制代码
# 段文件大小(低流量主题调小,如 128MB)
log.roll.bytes=134217728
# 或按时间滚动段(如 15 分钟)
log.roll.ms=900000

坑三:改完 retention 磁盘没立刻下降。 过期段先被改名为 .deleted,默认再等 log.segment.delete.delay.ms(60 秒)才真正从文件系统删除;同时受 log.retention.check.interval.ms 检查周期影响,最长延迟 = 检查间隔 + 删除延迟。生产上"改完 10 分钟内磁盘下降"都算正常,别急着反复改参数。

方案二:数据目录在线扩容------log.dirs 追加与运行日志分离

新磁盘挂载好了,Kafka 不会自动用上:分区只会写进 log.dirs 里声明的目录。扩容分三步。

2.1 新目录准备

bash 复制代码
mkdir -p /data/kafka/datalogs
chown -R kafka:kafka /data/kafka

属主必须是运行 Kafka 的系统用户,否则 broker 启动后写不了日志段,直接报 IO 异常。

2.2 修改 log.dirs:逗号追加,不是覆盖

properties 复制代码
# 修改前
log.dirs=/opt/bigdata/kafka/kafka-logs-1

# 修改后
log.dirs=/opt/bigdata/kafka/kafka-logs-1,/data/kafka/datalogs

注意是追加。手滑写成覆盖,重启后 broker 找不到旧目录里的分区副本,会触发全量副本同步甚至脱机。

Kafka 对多目录的分配规则:新分区优先分给"分区数最少"的目录 。所以扩容后新数据会自动流向新盘,但已存在的分区不会挪窝------老分区还压在旧盘上,这是方案三要解决的问题。

2.3 顺手分离运行日志目录

数据目录管的是消息日志段,Kafka 自身的运行日志(server.log 等)默认写在安装目录的 logs/ 下,一样会吃系统盘。修改 bin/kafka-run-class.sh:

bash 复制代码
# Log directory to use
# 在这行注释下面插入
LOG_DIR=/data/kafka/logs
if [ "x$LOG_DIR" = "x" ]; then
  LOG_DIR="$base_dir/logs"
fi

2.4 滚动重启

逐台重启,每台稳定后再动下一台:

bash 复制代码
su - kafka
export KAFKA_HOME=/opt/bigdata/kafka/kafka_2.13-3.9.1

$KAFKA_HOME/bin/kafka-server-stop.sh
# 等待 30 秒让进程退出干净
$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties

每台重启后观察两件事:/data/kafka/datalogs 下有无新增文件(说明新目录被识别),以及消费端 lag 是否回落(说明副本已追平)。一般 3 分钟左右稳定,再迁下一台。

方案三:分区跨盘迁移------kafka-reassign-partitions.sh 的 log_dirs 用法

目录扩容只解决"以后的新分区",已存在的大分区还在旧盘上越涨越快。kafka-reassign-partitions.sh 除了重排副本,还能只挪目录、不动副本拓扑 ------秘诀在 JSON 里的 log_dirs 字段。

3.1 查看现状

bash 复制代码
bin/kafka-topics.sh --bootstrap-server 192.168.10.31:9092 \
  --describe --topic task-log \
  --command-config ./config/producer.properties

输出里每行一个分区:Partition: 0 Leader: 2 Replicas: 2,0 Isr: 2,0。记下每个分区的副本分布。

3.2 编写迁移 JSON

把 task-log 的各分区都搬到 /data/kafka/datalogs(replicas 原样照抄,只改 log_dirs):

json 复制代码
{
  "version": 1,
  "partitions": [
    {"topic": "task-log", "partition": 0, "replicas": [2,0], "log_dirs": ["/data/kafka/datalogs","/data/kafka/datalogs"]},
    {"topic": "task-log", "partition": 1, "replicas": [1,2], "log_dirs": ["/data/kafka/datalogs","/data/kafka/datalogs"]},
    {"topic": "task-log", "partition": 2, "replicas": [0,1], "log_dirs": ["/data/kafka/datalogs","/data/kafka/datalogs"]}
  ]
}

三个细节:

  • replicas 与 log_dirs 一一对应、数量必须相等:第 N 个目录就是第 N 个副本要去的地方;
  • replicas 数组照抄 --describe 查出来的现有值,别顺手改顺序------第一个元素是首选 Leader;
  • 不想指定某个副本的目录时可以写 "any",Kafka 自行决定。

3.3 执行与验证

bash 复制代码
# 执行迁移
bin/kafka-reassign-partitions.sh \
  --bootstrap-server 192.168.10.31:9092 \
  --command-config ./config/producer.properties \
  --execute --reassignment-json-file migrate-tasklog.json

# 查看进度,输出 successfully 字样即完成
bin/kafka-reassign-partitions.sh \
  --bootstrap-server 192.168.10.31:9092 \
  --command-config ./config/producer.properties \
  --verify --reassignment-json-file migrate-tasklog.json

迁移是搬文件 + 改元数据,数据量大时耗时取决于磁盘 IO。操作前建议先停掉该主题相关的生产消费应用,搬完再启动,避免迁移期间 IO 争抢影响业务(不停机也能做,只是慢)。

3.4 同族操作:扩分区与扩副本

同一套工具还能干另外两件事,一并记下。

扩分区(只能从小往大,往小改直接报错):

bash 复制代码
bin/kafka-topics.sh --bootstrap-server 192.168.10.31:9092 \
  --alter --partitions 6 --topic task-log \
  --command-config ./config/producer.properties

注意:新增分区后,按 key 发送的老消息仍走旧分区,只有新 key 的路由会散到新分区,依赖 key 有序的消费逻辑要评估影响。

扩副本 :写一份不带 log_dirs 意图的 reassignment JSON(replicas 写成目标布局,log_dirs 全填 "any"),同样 --execute。两个实测细节:一是副本要均匀摊到各 broker,别所有分区都压同一组机器;二是执行后迟迟不同步(Isr 迟迟不齐)时,重启目标机器上的 Kafka 节点通常立刻开始同步,测试环境反复验证过。

方案四:主题重建------删错了数据格式时的兜底

有时不是磁盘问题,而是 topic 的数据格式/分区数彻底错了,需要删掉重建。

4.1 前提:打开删除开关

删除前确认 server.properties 里配了 delete.topic.enable=true(集群每台都要配),否则删除只是"标记",不做真删:

properties 复制代码
delete.topic.enable=true

4.2 删除与重建

bash 复制代码
# 删除
./bin/kafka-topics.sh --delete --bootstrap-server 192.168.10.31:9092 \
  --topic demo-topic --command-config ./config/producer.properties

# 重建(分区数、副本数按新规划来)
./bin/kafka-topics.sh --bootstrap-server 192.168.10.31:9092 \
  --create --partitions 6 --replication-factor 2 \
  --topic demo-topic \
  --command-config ./config/producer.properties

删除成功的返回是 Topic demo-topic is marked for deletion------注意措辞是"标记",副本文件在后台异步清掉,--list 里还会残留一会儿,属正常。

删除前先停该主题的生产与消费端 :一是避免删除瞬间客户端报错;二是很多集群开着 auto.create.topics.enable,生产端一不留神就会把刚删掉的 topic 又自动建出来,带着默认分区数"诈尸",前功尽弃。另外两个红线:__consumer_offsets 这类内部 topic 永远不要删;老版本集群(2.x 前)删除走 ZooKeeper 元数据 + 重启 broker,节奏完全不同,别照搬本文。

方案五:整体换盘不停机------逐台滚动迁移

硬件更换、系统盘换数据盘、迁移到 SSD,都要把整个 Kafka 安装目录搬到新磁盘。核心思路:一台一台来,迁移完一台、副本同步追平,再动下一台,集群全程可用。

以 Kafka 安装在 /opt/bigdata/kafka、要迁到 /data 为例:

bash 复制代码
# ① 停本机 broker
su - kafka
$KAFKA_HOME/bin/kafka-server-stop.sh

# ② 移动安装目录(root 执行)
mv /opt/bigdata/kafka /data/.

# ③ 批量替换路径引用:配置、启动脚本、环境变量三处
cd /data/kafka/kafka_2.13-3.9.1
sed -i 's#/opt/bigdata/kafka#/data/kafka#g' config/server.properties
sed -i 's#/opt/bigdata/kafka#/data/kafka#g' bin/kafka-run-class.sh

# ④ 启动
export KAFKA_HOME=/data/kafka/kafka_2.13-3.9.1
$KAFKA_HOME/bin/kafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties

验证两件事再迁下一台:

bash 复制代码
# 本机 broker 已入列 Isr
bin/kafka-topics.sh --bootstrap-server 192.168.10.32:9092 --describe \
  --topic task-log --command-config ./config/producer.properties

# 消费端有最新数据进来
bin/kafka-console-consumer.sh --bootstrap-server 192.168.10.32:9092 \
  --topic task-log --from-beginning --max-messages 1 \
  --command-config ./config/producer.properties

踩坑提醒 :路径引用不止 server.properties 一处------log.dirs、KAFKA_HOME 环境变量(systemd unit、profile 里写死的)、以及脚本里写死的绝对路径都要过一遍,grep -r '/opt/bigdata/kafka' /data/kafka 确认清零再启动,否则 broker 起来找不到副本目录,会被误判为数据丢失。

顺手加固:给 JMX 端口加认证

治理磁盘时经常顺手开 JMX 端口做监控,默认 JMX 无认证,等于把集群内部指标裸奔在内网。加认证三步:

bash 复制代码
cd /data/kafka/kafka_2.13-3.9.1/config

# ① 密码文件:格式为 <用户名> <密码>
echo "monitor Jmx@Monitor123" > jmx.password
chmod 600 jmx.password && chown kafka:kafka jmx.password

# ② 访问控制文件:readonly 只读
echo "monitor readonly" > jmxremote.access
chmod 600 jmxremote.access && chown kafka:kafka jmxremote.access

在 bin/kafka-server-start.sh 倒数第二行插入:

bash 复制代码
export JMX_PORT="10992"
export KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote=true \
  -Djava.rmi.server.hostname=192.168.10.31 \
  -Dcom.sun.management.jmxremote.authenticate=true \
  -Dcom.sun.management.jmxremote.password.file=/data/kafka/kafka_2.13-3.9.1/config/jmx.password \
  -Dcom.sun.management.jmxremote.access.file=/data/kafka/kafka_2.13-3.9.1/config/jmxremote.access \
  -Dcom.sun.management.jmxremote.ssl=false"

chmod 600 是硬性要求 ------JMX 检测到密码文件权限过宽会直接拒绝启动。重启后用 JConsole 连 service:jmx:rmi:///jndi/rmi://192.168.10.31:10992/jmxrmi,输入账号密码能看到 MBean 即成功。集群其他节点照做。

总结:一张决策表 + 避坑清单

场景 首选方案 关键命令
历史数据太多 保留策略 kafka-configs.sh --alter --add-config retention.ms=...
新盘没被利用 目录扩容 log.dirs 追加 + 滚动重启
分区分布不均 跨盘迁移 kafka-reassign-partitions.sh --execute + log_dirs
topic 建错了 删除重建 delete.topic.enable=true + --delete
换硬盘/换存储 整体迁移 逐台 stop→mv→sed→start

最后过一遍避坑清单,都是生产上真踩过的:

  1. retention.bytes 按分区算,主题总量要乘分区数;
  2. 活跃 segment 不删,低流量 topic 记得配 log.roll.bytes/log.roll.ms;
  3. 改完 retention 磁盘下降有分钟级延迟,别反复改;
  4. log.dirs 是追加不是覆盖,覆盖 = 副本全量重建;
  5. reassignment JSON 里 replicas 与 log_dirs 数量必须一致;
  6. 扩分区只增不减,且影响 key 路由有序性;
  7. 删 topic 前停生产端,防止 auto-create"诈尸";
  8. 整体换盘后 grep 确认旧路径清零再启动;
  9. JMX 密码文件权限必须 600,否则 broker 起不来。

你的 Kafka 集群磁盘治理是靠 retention 一把梭,还是也做过分区级搬迁?评论区聊聊你遇过的磁盘名场面。


操作步骤来自生产环境实测(Kafka 2.5 / 3.9 集群);文中 IP、密码均为示例。

相关推荐
erzong23139 分钟前
Linux操作系统:基于环形队列&信号量实现多生产者多消费者的简易模型
linux·运维·服务器
小羊没烦恼!44 分钟前
TypedSql:在 C# 类型系统上实现一个 SQL 查询引擎
java·linux·运维·服务器·开发语言·数据库·mysql
启博网络1 小时前
智慧档案库房工业组网:MR-1280 边缘大脑,实现温湿度、漏水、防火防盗设备智能联动教程
运维·网络·物联网·网络协议·网络安全·信息与通信·sdwan
码流子1 小时前
高速公路视频AI分析,算力该放哪:边缘、中心还是云边协同?
大数据·数据库·人工智能·物联网·音视频
海宇数据1 小时前
零信任架构实战:基于海宇普通维保查询构建自动化二手车认证展厅车况质检档案网关
运维·人工智能·架构·自动化
众壹新能源科技1 小时前
储能两充两放日收益增40%,但电池寿命缩短一半,怎么平衡
运维·人工智能·自动化
快乐的韭菜根根1 小时前
SLURM配置服务器一
运维·服务器
小冯不疯1 小时前
物料主数据同步实战:从金蝶云星空到小满OKKICRM的单一策略深度拆解
java·大数据·数据库
Z21-X1 小时前
云原生运维学习笔记:揭开虚拟化的硬核面纱,从底层架构到KVM实战
运维·笔记·学习·云原生