2. Loki 核心架构:读写路径、核心组件与 Prometheus 异同

理解 Loki 的核心架构是设计日志检索 Golden Signals 指标的基础。Loki 的设计哲学与 Prometheus 高度一致:使用标签(Labels)对数据进行索引,而非对日志内容进行全文索引。这种设计大幅降低了存储成本和查询延迟,但也决定了其读写路径和组件职责的独特性。

2.1 Loki 整体架构概览

Loki 采用微服务架构,核心组件包括 Distributor、Ingester 和 Querier。它们协同工作,实现了日志数据的水平扩展和高可用。下图展示了 Loki 的核心架构与数据流。

Loki 核心架构:读写路径与组件 日志采集器 (Promtail/OTel) Distributor (验证、哈希、转发) Ingester (内存缓存、压缩、持久化到对象存储) 对象存储 (S3/GCS/MinIO) Querier (查询 Ingester 内存 + 对象存储) 查询结果 (Grafana) 写路径 (Push) 1. 采集器推送日志流 2. Distributor 验证并哈希分片 3. Ingester 内存缓存后批量写入 读路径 (Query) 1. Querier 接收 LogQL 查询 2. 查询 Ingester 内存数据 3. 查询对象存储中的历史数据 4. 合并去重后返回结果

2.2 核心组件职责详解

每个组件在读写路径中承担明确的职责,理解这些职责有助于我们定位性能瓶颈和设计告警规则。

组件 职责 关键特性 性能关注点
Distributor 接收日志写入请求,负责验证、去重、哈希分片,并将日志转发到正确的 Ingester。 无状态,可水平扩展;使用一致性哈希环确定目标 Ingester。 请求速率、验证失败率、转发延迟
Ingester 接收日志并缓存在内存中,定期将数据压缩并持久化到对象存储。负责日志的实时写入和近期数据查询。 有状态,数据在内存中;支持 WAL 防止数据丢失;数据按 chunk 组织。 内存使用率、chunk 刷写延迟、写入吞吐
Querier 接收 LogQL 查询请求,同时查询 Ingester(热数据)和对象存储(冷数据),合并结果后返回。 无状态,可水平扩展;支持查询并行化和缓存。 查询延迟、查询并发数、缓存命中率
设计启示: 在 Golden Signals 指标设计中,Distributor 的 rate 和 errors 反映写入可用性;Ingester 的 memory 和 flush latency 反映写入饱和度和延迟;Querier 的 query latency 和 error ratio 反映读取性能。

2.3 读写路径深度解析

写路径(Push Path)

  1. 日志采集:Promtail、Fluentd 或 OpenTelemetry Collector 采集日志,添加标签(如 job、instance、namespace)。
  2. Distributor 处理:验证日志流格式和标签合法性;基于标签哈希将日志流分片到对应的 Ingester 副本。
  3. Ingester 缓存:将日志追加到内存中的 chunk;当 chunk 达到大小或时间阈值时,压缩并写入对象存储。

读路径(Query Path)

  1. Querier 接收查询:Grafana 发送 LogQL 查询到 Querier。
  2. 并行查询:Querier 同时向所有 Ingester 查询内存中的热数据,并向对象存储查询历史冷数据。
  3. 结果合并:Querier 对来自多个 Ingester 和存储的结果进行去重、排序和聚合,返回最终结果。
读写路径详细流程 写路径 (Push) 1. 采集器推送日志 2. Distributor 验证/哈希 3. Ingester 内存缓存 4. 压缩写入对象存储 读路径 (Query) 1. Querier 接收 LogQL 2. 查询 Ingester 内存 3. 查询对象存储 4. 合并去重返回 关键设计 • 写路径:最终一致性 • 读路径:查询所有副本 • 数据不跨节点复制

2.4 Loki 与 Prometheus 的异同

Loki 的设计深受 Prometheus 影响,但两者在数据模型、存储和查询语言上存在本质差异。理解这些异同有助于复用已有的 Prometheus 运维经验。

维度 Prometheus Loki
数据模型 时序指标(Metric + Labels),数值型 日志流(Stream = Labels),文本型
索引方式 基于标签的倒排索引 基于标签的倒排索引(仅索引标签,不索引日志内容)
存储引擎 TSDB(时序数据库),本地存储为主 对象存储(S3/GCS/MinIO),共享存储
查询语言 PromQL(Prometheus Query Language) LogQL(Log Query Language,兼容 PromQL 语法)
数据保留 本地存储,受磁盘容量限制 对象存储,几乎无限扩展
水平扩展 通过联邦或 Thanos/Cortex 实现 原生微服务架构,组件独立扩展
典型用途 实时监控、告警、趋势分析 日志检索、故障排查、审计
核心差异总结:
  • 索引策略相同:两者都使用标签索引,避免全文索引的高成本。这意味着 Loki 的查询性能高度依赖标签设计。
  • 存储模型不同:Prometheus 是本地时序数据库,Loki 是共享对象存储。Loki 的存储成本更低,但查询延迟更高。
  • 查询语言兼容:LogQL 复用了 PromQL 的聚合语法(如 rate、sum、topk),但增加了日志过滤和解析函数。

2.5 架构设计对 Golden Signals 的启示

Loki 的架构设计直接决定了我们在设计 Golden Signals 指标时需要关注的维度:

  • 写入路径的延迟和错误:Distributor 和 Ingester 是写入瓶颈。监控 distributor_ingester_appends_total 和 ingester_chunk_utilization 可以反映写入健康度。
  • 读取路径的延迟和饱和度:Querier 的查询延迟和并发数直接影响用户体验。监控 querier_query_duration_seconds 和 querier_inflight_queries。
  • 存储层的流量和错误:对象存储的请求速率和错误率是冷数据查询的关键。监控 loki_objectstore_requests_total。
  • 标签基数控制:Loki 的索引性能与标签基数强相关。高基数标签(如 trace_id、user_ip)会导致 Distributor 和 Ingester 性能下降。
常见陷阱: 不要将 Prometheus 的指标设计经验直接照搬到 Loki。例如,Prometheus 中高基数的 request_id 标签在 Loki 中会导致严重的性能问题。Loki 的标签应保持低基数(通常 < 10 个唯一值),日志内容应通过 LogQL 的 |= 或 |~ 过滤。