OpenObserve 的日志别堆本地盘:用 RustFS 当它的对象存储底座

OpenObserve 跑起来默认把日志转成的 Parquet 文件写在本地盘上。比如你给三四个服务接了日志,本地盘眼看着要满,又不想为了日志专门养一套分布式存储,这层本地存储就成了绕不开的瓶颈。把对象存储当成它唯一的持久层,OpenObserve 的节点就彻底无状态了:日志进来落进 RustFS 桶,节点挂了换一台照常查,扩容只要加桶容量而不是挪磁盘。把对象存储当成它唯一的持久层,OpenObserve 的节点就彻底无状态了:日志进来落进 RustFS 桶,节点挂了换一台照常查,扩容只要加桶容量而不是挪磁盘。

OpenObserve 的存储是怎么分的

OpenObserve(开源项目)走的是存算分离:日志先写进预写日志(WAL,一段先落盘缓冲、攒够再刷的日志),攒够一批后切成带全文索引的 Parquet 文件上传到对象存储;查询时按文件列表元数据定位远端 Parquet,按需下载到本地缓存再算。Parquet 是一种列式存储文件,把同一列的数据挨在一起存,聚合查询时不用整行读,扫描量小很多。对象存储在这里是它唯一的持久化数据层,本地盘只放缓冲和缓存。

官方已经给出 OpenObserve 接 RustFS 的指南,用的就是这套 WAL 到 Parquet 再到桶的路径。所以这篇文章不重复从零搭建,重点放在两件事上:怎么把你已经跑着的 OpenObserve 指向你已经有的 RustFS 集群,以及官方那份单机 demo 里哪些假设到了生产要换掉。

接进已有的 RustFS,最小改动是哪几行

官方 demo 是为了本地验证方便,把 RustFS 单节点和 OpenObserve 单实例塞进同一个 docker compose。生产里你不会这么干:RustFS 要换成真实集群,OpenObserve 也可能是已有的服务。改动很小,就是一组 ZO_S3_* 环境变量。

最关键的一句:ZO_LOCAL_MODE_STORAGE=s3。OpenObserve 单节点模式下存储后端默认是 disk,你不设这个变量,它就会把 Parquet 写进 /data/wal/files/ 然后彻底忽略所有 ZO_S3_* 配置,这是最容易踩的坑。设成 s3 之后,下面这些才会生效:

yaml 复制代码
ZO_LOCAL_MODE: "true"
ZO_LOCAL_MODE_STORAGE: "s3"
ZO_S3_PROVIDER: s3
ZO_S3_SERVER_URL: http://rustfs:9000
ZO_S3_REGION_NAME: us-east-1
ZO_S3_ACCESS_KEY: ${RUSTFS_ACCESS_KEY}
ZO_S3_SECRET_KEY: ${RUSTFS_SECRET_KEY}
ZO_S3_BUCKET_NAME: ${RUSTFS_BUCKET_NAME}

RustFS 这边走的是路径风格(path-style)寻址加 SigV4 签名,OpenObserve 底层的 object_store 客户端默认就是这套,所以不用额外开关。桶要提前建好,官方 demo 用 rc mb 在 RustFS 健康检查通过后建 my-bucket。

官方 demo 里要换掉的几个假设

demo 能跑通不等于能上线,这几处要改成你自己的环境:

  • RustFS 不再是单节点 :把 ZO_S3_SERVER_URL 从 rustfs:9000 换成你集群的负载均衡地址或某个节点地址,集群内部用 EC 冗余,不用再管单盘寿命;
  • 凭证要专用:demo 直接塞了管理员密钥,生产里按 RustFS 的 IAM 访问密钥管理建一个只对这个桶有读写权的子凭证,密钥走环境变量不落文件;
  • 上传节奏别照抄 :ZO_MAX_FILE_RETENTION_TIME 默认 600 秒,demo 为了让你立刻看到文件改成了 60 秒,生产用回默认,否则 ingester 会过于频繁地刷写小文件。

上传节奏和桶里长什么样

OpenObserve 不会每条日志都上传,而是等 WAL 里的记录达到大小阈值、或者超过 ZO_MAX_FILE_RETENTION_TIME 后才把 Parquet 推到桶里。

文件落进桶后大致长这样:files/<组织>/<流类型>/<流名>/<日期分区>/ 下是 Parquet 数据,同级的 index/ 里是全文索引的 .ttv 文件。想知道链路通没通,调一次节点级 flush 让记录离开 WAL,再用 rc ls 看 files/ 前缀下有没有 .parquet 出现:

bash 复制代码
curl -s -u "root@example.com:Complexpass#123" -X PUT "http://localhost:5080/node/flush"
docker compose run --rm --entrypoint /bin/sh init-bucket -c \
  '/usr/bin/rc alias set rustfs http://rustfs:9000 "$RUSTFS_ACCESS_KEY" "$RUSTFS_SECRET_KEY" >/dev/null && /usr/bin/rc ls rustfs/my-bucket --recursive'

怎么确认持久层真的是 RustFS

验证闭环最直接的一招:把 OpenObserve 的本地缓存目录删了,重启容器再查同一条日志,结果照样回来。因为数据是从 RustFS 重新下载的,不是本地盘给的。这一招能确认你真的把持久层换成了对象存储,而不是又悄悄写回了本地。

RustFS 当前稳定版是 1.0.4(2026-10-25),从单节点到多节点集群都覆盖 OpenObserve 需要的这些 S3 操作。官方 GA 基准里 4KiB 小对象 PUT 大约能到 MinIO 的 2.3 倍,对日志、索引这类小文件密集的写入是实打实的优势。控制台在 /rustfs/console/ 路径下,进去能直接翻 files/ 前缀确认对象到位。顺带一提,因为持久层在远端,OpenObserve 节点的 CPU 和内存规划可以独立于数据量来做,扩容计算资源不会牵动存储,这是存算分离带来的第二个好处。

存储上云之后还能接着做

把 Parquet 落进 RustFS 只是第一步,对象存储的好处在后面才显出来。数据已经是无状态的文件,你可以直接给桶设 lifecycle 规则做冷热分层,或者把桶做跨地域复制防单机房故障,都不用动 OpenObserve 本身。要做合规留存,桶级访问日志能单独保留,和应用日志分开管理。还有两个细节容易踩:本地 WAL 只是临时攒批层,真正的耐久性来自桶,所以 ZO_MAX_FILE_RETENTION_TIME 要按恢复点目标定,调小查询更新鲜但对象更碎,调大对象更整但可能丢最近几分钟;另外 OpenObserve v1.0.x 强制密码复杂度,ZO_ROOT_USER_PASSWORD 必须 8 到 128 位且含大小写字母、数字和特殊字符,否则容器反复重启报弱密码。

上线前过一遍这几项

  • ZO_LOCAL_MODE_STORAGE 确实是 s3,不是默认的 disk;
  • ZO_S3_* 里的桶已经用专用凭证建好,别拿管理员密钥凑数;
  • ZO_MAX_FILE_RETENTION_TIME 用回默认 600,除非你有立刻验证的需求;
  • RustFS 控制台里能看到 files/ 前缀下的 Parquet 和索引文件。

接好之后,OpenObserve 的节点就谁都能随时起停,真正的日志全在 RustFS 桶里。

官方把 OpenObserve 接进 RustFS 的完整步骤在 OpenObserve 集成指南 里,包含摄入、flush 与查询的示例命令。

相关推荐
新*2 小时前
ES 7.17 三节点 Docker 集群接入 IK 自定义词典:一次从丢插件到热更新的完整排障
运维·elasticsearch·docker·中文分词·ik分词
风翼靓崽2 小时前
jenkins搭建与简单使用
运维·jenkins
阿里云云原生18 小时前
11月14日杭州见丨COSCon’26 第十一届中国开源年会,阿里云四场 Agent 技术分享
云原生
SL_staff19 小时前
JVS-APS有限产能建模的七个刚性支点:面向产线真实约束的技术实现解析
java·开源·全栈
阿里云云原生19 小时前
关于 DeepSeek 的 SuperAgent,以及我们的实践
云原生·agent
mengge.cloud20 小时前
prometheus+k8s+弹性伸缩,综合项目
云原生·容器·kubernetes·prometheus
SL_staff21 小时前
为什么企业级自动化不能只靠AI编排?——逻辑引擎在确定性业务场景中的不可替代性
java·开源·产品
Z21-X1 天前
云原生运维学习笔记:ECShop全链路容器化部署与Docker底层资源隔离深度剖析
运维·笔记·学习·云原生