日志检索不一定要常驻 Elasticsearch:用 Quickwit 在对象存储上搭建可验证的搜索入口
日志检索的难点通常不在于“能不能搜到一条记录”,而在于数据保留周期拉长后,索引节点、磁盘副本和计算资源是否必须始终绑定在一起。对需要保存大量日志或链路追踪数据、却只有一部分查询是高频查询的团队而言,传统常驻搜索集群的成本模型未必合适。
Quickwit 是面向可观测性场景的云原生开源搜索引擎,采用 Apache-2.0 许可证。它的重点不是把任意工作负载都替换成 Elasticsearch,而是让索引和检索围绕对象存储组织:索引器与搜索器可以保持无状态,索引文件放入对象存储,计算层按工作负载扩缩。官方 README 还说明它覆盖了较大范围的 Elasticsearch/OpenSearch API、常见查询 DSL 和聚合能力;这应理解为迁移入口,而不是“所有 API 都完全等价”的承诺。
本文的目标是完成一次本地、可回滚的验证:启动单节点服务,创建官方样例索引,导入 NDJSON 数据,再通过 REST API 查询结果。这个过程比先规划一次大规模迁移更可靠,因为它能尽早暴露 schema、摄取格式和客户端兼容性的真实边界。
先理解它解决的是什么问题
Quickwit 把数据存储和计算执行拆开。索引被切成多个 split 并存储在对象存储中;接收文档、构建索引的 indexer 与执行检索的 searcher 可以作为不同角色运行。这样做的直接价值是:长期数据的存储成本可与峰值查询计算分开考虑,而不是为很少访问的历史数据长期保留同等规模的搜索节点。
但这不是“零运维搜索”。索引设计依然决定查询效果:哪些字段用于全文检索,哪些字段需要聚合或过滤,时间字段怎样解析,数据多久保留,都要在接入前明确。对象存储的权限、网络出口、生命周期策略和 API 访问控制也不会因为搜索器无状态而自动安全。
另一个容易被忽略的边界是高可用。Quickwit 官方 README 对索引端的 HA 有明确限定:索引端高可用仅适用于 Kafka source。因而,如果团队使用文件批量导入、HTTP 摄取或其他来源,不能把“无状态架构”直接推导为摄取端天然高可用;应为上游重试、幂等性和失败重放设计单独的流程。
用 Docker 做最小验证
官方 Quickstart 当前给出了 0.9.0 镜像。下面的命令将数据目录挂载到当前目录的 qwdata,并刻意把 HTTP 端口绑定到回环地址。它适合本机试验,避免在未配置认证和网关前把管理接口暴露到局域网或公网。
mkdir qwdata docker run --rm \ -v "$(pwd)/qwdata:/quickwit/qwdata" \ -p 127.0.0.1:7280:7280 \ quickwit/quickwit:0.9.0 run
另开一个终端,以版本接口确认进程已真正接受请求,而不要只把容器“正在运行”当作成功:
curl http://127.0.0.1:7280/api/v1/version
如果这里无法连接,优先检查端口占用、Docker 映射和容器日志。不要先改成 0.0.0.0:7280:7280 来“解决”访问问题;这会扩大暴露面,却没有解决认证、TLS 与访问策略缺失的问题。
从官方样本走完索引、摄取和搜索
Quickwit 官方教程提供了 Stack Overflow 样本的索引配置及转换后的 NDJSON 数据。先获取索引配置,然后通过 REST API 创建名为 stackoverflow 的索引:
curl -o stackoverflow-index-config.yaml \ https://raw.githubusercontent.com/quickwit-oss/quickwit/v0.9.0/config/tutorials/stackoverflow/index-config.yaml curl -X POST http://127.0.0.1:7280/api/v1/indexes \ -H "content-type: application/yaml" \ --data-binary @./stackoverflow-index-config.yaml
接着下载样本并导入:
curl -O \ https://quickwit-datasets-public.s3.amazonaws.com/stackoverflow.posts.transformed-10000.json curl -X POST \ "http://127.0.0.1:7280/api/v1/stackoverflow/ingest?commit=force" \ --data-binary @stackoverflow.posts.transformed-10000.json
教程使用的 commit=force 很适合演示:它让这次导入后立即提交,便于后续查询验证。但生产摄取不应仅因“查询马上可见”就照搬这个参数。批量大小、提交频率、失败重试、下游查询延迟和对象存储写入成本需要结合真实吞吐测试决定。
最后,用一个全文条件确认索引、摄取和查询链路都已打通:
curl "http://127.0.0.1:7280/api/v1/stackoverflow/search?query=search+AND+engine"
返回 JSON 后,至少检查 hits 是否非空、文档字段是否与 schema 预期一致。若结果为空,不要立刻把问题归咎于引擎:先核对索引名、摄取响应、字段名和查询语法;再检查样本配置里哪些字段被设为 searchable。把这一套检查写成 CI 中的冒烟测试,比迁移后才发现字段不可检索更便宜。
把样本替换成日志时,先写清数据契约
真实日志接入时,最常见的失败模式不是服务崩溃,而是 schema 与数据不一致:时间字段格式不统一,数值被作为字符串写入,嵌套字段在查询端难以过滤,或者把高基数字段错误地设计成聚合维度。建议先取一小段脱敏 NDJSON,明确以下问题:
- 哪个字段是事件时间,时区与缺失值怎样处理;
- 哪些字段用于关键字搜索,哪些只用于精确过滤;
- 哪些字段确实需要聚合,避免把任意标签都当作聚合维度;
- 索引命名、保留周期和删除策略如何映射到业务或租户边界;
- 摄取失败后由谁重放,重复文档是否可接受。
Quickwit 提供严格 schema 与 schemaless 索引两种路径。探索阶段可以利用 schemaless 降低试验门槛,但进入稳定环境前,关键过滤、排序和聚合字段仍应通过明确 schema 固化。否则,数据格式在不同服务版本之间漂移时,查询结果会变得难以解释。
兼容 Elasticsearch/OpenSearch:先测清单,后迁移
对已有 Elasticsearch 或 OpenSearch 客户端的团队,Quickwit 的兼容 API 可以降低试验成本,但兼容性不应靠宣传语判断。更稳妥的做法是建立一份调用清单:应用实际发送哪些 endpoint、查询子句、聚合、分页方式和 ingest 请求;逐项在隔离索引上回放,再比较状态码、返回结构和结果语义。
尤其要留意两类差异。第一类是查询 DSL 中较少使用但业务依赖很深的功能;第二类是客户端框架自动生成的请求,例如健康检查、索引管理或 bulk 写入。只有业务关键请求通过回放,才适合逐步把一个只读查询、一个日志流或一组 dashboard 灰度切换过去。不要把“能连上 ES-compatible endpoint”误当作完整迁移已经完成。
适用场景与上线前的最后一层检查
Quickwit 适合日志、trace 等以写入与检索为主、希望把长期数据放进对象存储、并愿意为 schema 与摄取流程做验证的场景。它也适合作为现有检索系统旁的试验入口:先用一份真实但脱敏的数据确认查询、聚合与延迟,再决定是否扩大范围。
它不适合未经兼容性测试就替换现有搜索集群,也不适合把单机 Docker 演示直接当作生产方案。上线前至少补齐对象存储最小权限、索引隔离和保留策略、API 前置认证/TLS、可观测性、摄取重试与恢复演练。还应区分演示中的本地磁盘挂载与生产中的对象存储:前者只证明服务和 API 可用,后者才会引入凭据轮换、跨区域延迟、生命周期删除和灾难恢复等约束。建议先为一个低风险索引定义恢复时间与恢复点目标,再验证重建索引、回放摄取以及权限失效时的告警行为。搜索架构的价值不在于少起几个容器,而在于当数据量、保留期和查询模式变化时,仍能清楚知道成本和故障会落在哪一层。
相关链接