2. Loki 架构回顾:核心组件、数据流与查询路径

在深入版本漂移校准之前,必须对 Loki 的经典架构形成肌肉记忆。Loki 采用 微服务 + 共享存储 架构,核心组件围绕“写路径”和“读路径”解耦设计。版本升级导致的接口差异,本质上就是这些组件之间 RPC 契约或 HTTP API 的变更。

2.1 核心组件一览

组件 职责 关键接口 版本漂移敏感点
Distributor 接收日志推送,验证数据,分片转发至 Ingester /loki/api/v1/push (gRPC/HTTP) Push 协议字段变更、压缩算法切换
Ingester 内存缓冲、压缩、持久化日志块到对象存储 gRPC Push、Flush、Transfer Chunk 编码格式、索引 schema 版本
Querier 从 Ingester 和存储中读取日志,执行 LogQL 引擎 /loki/api/v1/query_range (gRPC/HTTP) 查询语法、聚合函数签名、时间戳精度
QueryFrontend 查询缓存、分片、合并、去重,加速读路径 gRPC Frontend-to-Querier 缓存键格式、分片算法、响应合并逻辑
SRE 视角: 版本升级时,Distributor → Ingester 的 gRPC 协议版本和 QueryFrontend → Querier 的查询分片协议是最容易产生“静默不兼容”的区域。即使 HTTP 路由不变,protobuf 字段编号或枚举值的变更也会导致解析失败。

2.2 写路径数据流

日志从客户端到持久化存储的完整路径,涉及两次数据转换和一次确认回传:

  1. 客户端推送:Promtail / Grafana Agent / SDK 通过 HTTP /loki/api/v1/push 发送日志流(Stream)到 Distributor。
  2. Distributor 验证与分片:校验标签合法性、租户 ID、时间戳范围;使用一致性哈希环(Consistent Hash Ring)将日志流路由到对应的 Ingester 副本。
  3. Ingester 接收与缓冲:将日志追加到内存中的 Chunk(块),按时间戳和标签组织。Chunk 达到阈值(如 1MB 或 1 小时)后标记为“就绪”。
  4. 持久化:Ingester 将就绪 Chunk 压缩、索引后写入对象存储(S3 / GCS / MinIO),并更新索引(Boltdb-shipper 或 TSDB)。
  5. 确认回传:Ingester 向 Distributor 返回 ACK,Distributor 再向客户端返回 204。
Loki 写路径数据流 客户端 Distributor Ingester 对象存储 写路径关键点: ① 客户端 → Distributor:HTTP Push,版本漂移常发生在 Content-Encoding 或 protobuf schema。 ② Distributor → Ingester:gRPC 流式推送,ring 哈希算法变更会导致数据分布不一致。

2.3 读路径查询流

查询路径比写路径更复杂,涉及 QueryFrontend 的智能路由 和 Querier 的分布式执行:

  1. 查询请求:客户端(Grafana / CLI)发送 LogQL 查询到 QueryFrontend。
  2. QueryFrontend 分片与缓存:将大查询按时间范围拆分为多个子查询;检查缓存(结果缓存 / 索引缓存),命中则直接返回。
  3. Querier 执行:每个子查询被发送到 Querier。Querier 同时查询 Ingester(内存数据) 和 对象存储(历史数据),执行 LogQL 引擎。
  4. 结果合并:QueryFrontend 收集所有 Querier 返回的子结果,按时间戳排序、去重、聚合,返回最终响应。
Loki 读路径查询流 Grafana QueryFrontend Querier Ingester 对象存储 读路径关键点: ① QueryFrontend 的分片算法(time-based splitting)在版本间可能调整,导致缓存键失效。 ② Querier 同时查询 Ingester(实时)和存储(历史),两个数据源的 LogQL 引擎版本必须一致。

2.4 查询路径的版本漂移风险矩阵

版本升级时,以下接口最容易出现“新旧集群混跑”导致的查询差异:

接口 / 协议 常见变更 影响范围 检测手段
/loki/api/v1/query_range 新增 step 参数校验、direction 枚举值 Grafana 面板、CLI 查询 对比新旧集群相同查询的响应体
gRPC QueryService protobuf 字段重命名、service 方法签名 QueryFrontend ↔ Querier gRPC reflection 版本比对
Chunk 编码格式 压缩算法切换(gzip → snappy → lz4) Ingester 写入 / Querier 读取 存储中 Chunk 的 magic number
索引 schema v11 → v12 → v13 字段增减 Querier 索引查询 loki-index-stats 命令
升级避坑: 如果集群采用 滚动升级,新旧版本的 Querier 和 Ingester 会短暂共存。此时必须确保 所有 Querier 的 LogQL 引擎版本一致,否则同一个查询在不同 Querier 上可能返回不同结果,导致 QueryFrontend 合并出错。

2.5 架构总结与校准切入点

Loki 的微服务架构通过 gRPC + HTTP 双协议解耦组件。版本漂移校准的核心在于:

  • 写路径:Distributor 与 Ingester 之间的 Push 协议版本。
  • 读路径:QueryFrontend 与 Querier 之间的查询分片协议,以及 Querier 自身的 LogQL 引擎版本。
  • 存储层:Chunk 编码格式和索引 schema 版本,影响历史数据可读性。