Kafka 分层存储落地指南:冷 Segment 自动落到 RustFS 对象存储

Kafka 的数据要留得久,broker 磁盘就跟着膨胀。三个月的订单事件、一年的埋点日志,三副本全在 broker 本地盘上,扩容节奏被 retention 绑死。KIP-405 分层存储就是为了解开这个绑定:滚过的 Segment 搬到对象存储,本地只留热尾巴。这个功能从 3.6 的 early access 走到 3.9.0 才生产可用,Apache Kafka 本身不带存储后端实现,落地要自己装一个 RemoteStorageManager 插件。这篇用 Aiven 开源的 RSM 插件,把远程层接到自建的 RustFS(S3 兼容)上,从部署到验证走一遍。

先把概念分清

两套 retention。 开了分层后每个 topic 有两个保留窗口:retention.ms / retention.bytes 管总保留量(含远端),local.retention.ms / local.retention.bytes 管本地留多久。本地保留可以缩到几小时,总保留拉到几个月。有个容易踩的点:local retention 不显式设置时会回落到总 retention 的值------数据照样会下沉(上传由 Segment 滚动触发,跟这个参数无关),但本地副本不会提前删,磁盘该涨还是涨,分层的意义直接减半,所以这两个参数必须显式配。

只搬滚过的 Segment。 active segment 永远在 broker 本地,只有滚动关闭的 Segment 会被复制到远端,且本地副本删除一定发生在上传成功之后。对生产者和消费者完全透明,客户端不用改代码,读已经离开本地盘的 offset 时,broker 从远端取回来返回。

两个插件接口加一个内部 topic。 数据面是 RemoteStorageManager(复制、取回、删除 Segment),控制面是 RemoteLogMetadataManager(记录哪个 Segment 在哪)。Kafka 自带默认的元数据管理器,把元数据存在内部 topic __remote_log_metadata 里。但存储后端 Kafka 一个都不带,源码里的 LocalTieredStorage 只用于集成测试。社区里最常见的开源实现是 Aiven 的 tiered-storage-for-apache-kafka,支持 S3、GCS、Azure Blob,开源且维护活跃。

版本门槛:3.6.0 early access,3.9.0 生产可用。KIP-950(按 topic 关闭分层)、KIP-956(上传/下载配额)、KIP-1005(远程 offset 暴露给客户端)也都在 3.9 落地。admin 工具要求客户端 3.0 以上。

第一步:起一个 RustFS 当远程层

任何 S3 兼容对象存储都能当远程层,自托管场景用 RustFS 就一行 docker 命令的事。按官方文档:

bash 复制代码
docker pull rustfs/rustfs:latest
docker volume create rustfs-data

docker run -d \
  --name rustfs \
  --restart unless-stopped \
  -p 9000:9000 \
  -p 9001:9001 \
  -v rustfs-data:/data \
  -e RUSTFS_ACCESS_KEY="<your-access-key>" \
  -e RUSTFS_SECRET_KEY="<your-secret-key>" \
  -e RUSTFS_ADDRESS=":9000" \
  -e RUSTFS_CONSOLE_ADDRESS=":9001" \
  -e RUSTFS_CONSOLE_ENABLE=true \
  rustfs/rustfs:latest \
  /data

验证健康检查,然后用 AWS CLI 建一个专用的桶:

bash 复制代码
curl --fail http://localhost:9000/health

export AWS_ACCESS_KEY_ID=<your-access-key>
export AWS_SECRET_ACCESS_KEY=<your-secret-key>
aws --endpoint-url http://localhost:9000 s3 mb s3://kafka-tiered

有一个设计上的事提前说清楚:这个桶里的对象由 Kafka 自己管理,Segment 过期时 broker 会主动删对象。所以不要在这个桶上再叠一层 lifecycle expiration 规则,两套删除逻辑叠加,轻则多余请求,重则元数据还指向对象、对象已经被外部规则删掉。

第二步:装 Aiven RSM 插件

从 tiered-storage-for-apache-kafka 的 GitHub Releases 下载两个包:core-<version>.tgz 和 s3-<version>.tgz(版本号以 releases 页当前为准),解压到 Kafka broker 的插件目录,结构大致是:

复制代码
/opt/kafka/plugins/aiven-tiered-storage/
├── core/          # core 包的 jar
└── s3/            # s3 包的 jar(S3StorageBackend 及 AWS SDK 依赖)

第三步:配置 broker

server.properties 里加这些(多 broker 滚动重启生效):

properties 复制代码
remote.log.storage.system.enable=true

remote.log.storage.manager.class.name=io.aiven.kafka.tieredstorage.RemoteStorageManager
remote.log.storage.manager.class.path=/opt/kafka/plugins/aiven-tiered-storage/core/*:/opt/kafka/plugins/aiven-tiered-storage/s3/*

remote.log.storage.manager.impl.prefix=rsm.config.

remote.log.metadata.manager.class.name=org.apache.kafka.server.log.remote.metadata.storage.TopicBasedRemoteLogMetadataManager
remote.log.metadata.manager.listener.name=PLAINTEXT

rlmm.config.remote.log.metadata.topic.replication.factor=1
rlmm.config.remote.log.metadata.topic.min.isr=1

rsm.config.storage.backend.class=io.aiven.kafka.tieredstorage.storage.s3.S3StorageBackend
rsm.config.storage.s3.bucket.name=kafka-tiered
rsm.config.storage.s3.region=us-east-1
rsm.config.storage.s3.endpoint.url=http://<rustfs-host>:9000
rsm.config.storage.s3.path.style.access.enabled=true
rsm.config.aws.access.key.id=<your-access-key>
rsm.config.aws.secret.access.key=<your-secret-key>
rsm.config.chunk.size=4194304

几个 key 单独说一下:

  • s3.endpoint.url:官方文档写明这个 key 就是为 S3 兼容后端准备的(文档举例是 SeaweedFS,S3 兼容的 RustFS 同理),不设则连 AWS S3。
  • s3.path.style.access.enabled=true:自托管对象存储显式开成 true 最稳。留空时由 SDK 自动探测,Amazon S3 默认走 virtual-host 风格(bucket 当子域名),IP 或内部域名的自建服务解析不了这种地址。
  • chunk.size:Segment 会被切块存储以支持按范围取回,官方建议从 4 MiB 起步做基准测试。
  • storage.s3.multipart.upload.part.size 默认 25 MiB,一般不用动。
  • 如果升级后遇到校验类报错,有个兼容开关 rsm.config.storage.s3.legacy.md5.plugin.enabled,是为依赖旧 MD5 校验行为的老 S3 兼容存储准备的。

第四步:给 topic 打开分层

bash 复制代码
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
  --create --topic order-events \
  --config remote.storage.enable=true \
  --config segment.bytes=536870912 \
  --config local.retention.ms=86400000 \
  --config retention.ms=2592000000

这组配置的含义:本地留 1 天,总量留 30 天,剩下 29 天的数据在对象存储里。segment.bytes 调小能让下沉更及时(Segment 滚动才上传),代价是对象数量和请求数变多。已有 topic 用 kafka-configs.sh --alter 加同样的 config 即可。

验证

按顺序验证:

bash 复制代码
bin/kafka-topics.sh --bootstrap-server localhost:9092 --list | grep remote

aws --endpoint-url http://localhost:9000 s3 ls s3://kafka-tiered --recursive

bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic order-events --offset <old-offset> --partition 0

监控上看 RemoteLogManager 的 JMX 指标(上传滞后、复制耗时、错误计数),RLM 落后意味着该下沉的数据堆在本地盘,分层就白开了。

退出路径(KIP-950,仅 KRaft)

3.9 之前分层开了就关不掉,KIP-950 给了两条退路:

bash 复制代码
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
  --alter --entity-type topics --entity-name order-events \
  --add-config 'remote.log.copy.disable=true,local.retention.ms=-2,local.retention.bytes=-2'

bin/kafka-configs.sh --bootstrap-server localhost:9092 \
  --alter --entity-type topics --entity-name order-events \
  --add-config 'remote.storage.enable=false,remote.log.delete.on.disable=true'

-2 的作用是把本地保留对齐到总保留。另外有个单向门:broker 级关掉 remote.log.storage.system.enable 之前,必须先删光所有分层 topic,否则 broker 起不来。

限制清单(官方文档列的,别撞上)

Kafka 官方 tiered storage 运维文档把这几条列得很直白:

  • 不支持 compacted topics,开了分层的 topic 也不能再把 cleanup.policy 改成 compact。K-V 状态回放类的压缩主题走不了这条路。
  • Kafka 2.8 之前创建的 topic 可能缺 producer snapshot 文件,这类老 Segment 不被支持。
  • broker 级关闭要先删光分层 topic(上面说过)。
  • 读远端数据依赖存活的 broker,remote.fetch.max.wait.ms 默认 500ms,远端读慢的场景可能要调大。

还有一条要心里有数的:分层存储不是备份。远端 Segment 仍是活日志的一部分,topic 删除会连着删远端数据,误写入的坏数据远端一样有,跨集群恢复没有机制。它解决的是保留成本,不是可恢复性,后者要独立的备份方案。

什么场景值得上

对照着判断:

  • retention 拉到月级、年级,broker 磁盘成本是痛点 → 值得
  • 历史数据读得少(偶尔回填、审计),尾读为主 → 值得,对象存储的成本结构正好匹配这种访问模式
  • topic 是 delete 策略的日志流 → 适合
  • compacted topic、频繁全量回放、retention 只有几天 → 本地盘更划算,别折腾

分层存储把"留多久"和"买多少盘"解耦了,自托管侧加一个 S3 兼容的 RustFS 就能把这套架构跑起来,插件和元数据 topic 的运维成本是主要代价。retention 长的集群,这笔账不难算。

相关推荐
acd120091 小时前
域名解析缓存带来的隐性故障,很多排查都漏掉了这一层
java·运维·nginx
Z21-X1 小时前
云原生运维实战笔记:华为云计算与网络类服务全景解析
运维·笔记·学习·云原生·华为云
Sayai1 小时前
Elasticsearch ingest pipeline 被禁用之后:性能代价、安全治理与数据入库时间的四种替代方案
大数据·运维·安全·elasticsearch·搜索引擎·性能优化
kiros_wang1 小时前
通用工具模块封装:防抖节流、参数校验公共工具类
linux·运维·ubuntu
Android系统攻城狮1 小时前
Linux FFmpeg深度解析之avcodec_default_get_buffer2调用流程与实战(二十)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
深圳恒讯1 小时前
香港云主机硬盘满了怎么办?Linux和Windows在线扩容操作教程
linux·运维·windows
饭后一颗花生米1 小时前
GitHub Actions 自动化:5 个真实运维提效案例
运维·自动化·github
Android系统攻城狮1 小时前
Linux FFmpeg深度解析之av_parser_init调用流程与实战(二十一)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
孔令飞2 小时前
AI 岗位类别、要求、技能及学习路线
云原生·go·ai编程