2. Loki 架构深度解析:核心组件职责与数据流

Loki 采用微服务架构,将日志的接收、存储、查询、压缩等职责解耦到多个独立组件中。理解每个组件的职责及其之间的数据流,是进行容量规划、性能压测和故障定位的基础。本章将逐一剖析 Distributor、Ingester、Querier、QueryFrontend 和 Compactor 这五大核心组件。

2.1 组件全景与数据流总览

下图展示了 Loki 集群中日志数据从写入到查询的完整生命周期,以及各组件之间的协作关系。

Loki 核心组件与数据流总览 Loki 核心组件与数据流总览 日志客户端 Distributor Ingester (主) Ingester (副) 对象存储 QueryFrontend Querier Compactor 说明:实线代表写入/查询数据流,虚线代表副本同步或实时查询路径。

2.2 Distributor:日志入口与负载均衡

Distributor 是日志写入的第一站,负责接收来自 Promtail、Docker 插件、Fluentd 等客户端的日志流。其核心职责包括:

  • 数据验证:检查日志流的时间戳、标签(Labels)格式是否合法,拒绝无效数据。
  • 哈希分片:根据日志流的标签集计算一致性哈希,决定该流应由哪个 Ingester 实例处理。
  • 负载均衡:将日志流均匀分发到后端的 Ingester 实例,避免单点过载。
  • 副本转发:根据配置的复制因子(通常为 3),将日志流同时转发到多个 Ingester 副本,保证数据高可用。
压测关注点: Distributor 是无状态的,可以水平扩展。压测时需关注其 gRPC 连接数、请求延迟和错误率。如果 Distributor 成为瓶颈,通常表现为客户端写入超时或返回 429 限流错误。

2.3 Ingester:内存缓冲与数据持久化

Ingester 是 Loki 中唯一有状态的组件,负责将日志数据暂存于内存,并最终写入长期存储。其工作流程分为两个阶段:

阶段 状态 行为描述 数据可见性
写入阶段 Running 接收 Distributor 推送的日志流,追加到内存中的 Chunk(数据块)中。Chunk 是 Loki 的最小存储单元,包含压缩后的日志条目和索引。 实时可见(Querier 可直接查询)
切分与刷盘阶段 Flushing 当 Chunk 达到最大大小(默认 1.5MB)或最大时间跨度(默认 30 分钟)时,Ingester 将 Chunk 压缩并上传到对象存储(如 S3、GCS、MinIO)。 刷盘后仍可见
只读阶段 ReadOnly Ingester 实例即将关闭或缩容时,进入只读模式,不再接收新数据,仅处理已有 Chunk 的刷盘和查询请求。 数据完全持久化后,Ingester 可安全下线
故障定位关键: Ingester 内存使用率是核心指标。如果 Ingester 频繁 OOM,通常意味着 Chunk 刷盘速度跟不上写入速度,或 Chunk 大小配置不合理。此时应检查 ingester.chunk-idle-period 和 ingester.chunk-block-size 参数。

2.4 Querier:日志查询执行器

Querier 负责执行 LogQL 查询,从多个数据源聚合日志结果。其查询路径如下:

  1. 接收查询请求:从 QueryFrontend 或直接来自 Grafana 的 HTTP/gRPC 请求。
  2. 并行查询 Ingester:对于近期未刷盘的日志,Querier 直接向所有 Ingester 发起实时查询,获取内存中的最新数据。
  3. 查询对象存储:对于已持久化的历史数据,Querier 根据时间范围和标签索引,从对象存储中读取对应的 Chunk 文件。
  4. 结果去重与合并:由于数据存在副本,Querier 需要对来自不同 Ingester 或存储的相同日志流进行去重,并按时间戳排序后返回。
性能压测指标: Querier 的并发查询能力、P99 查询延迟、以及从对象存储读取 Chunk 的吞吐量。如果查询慢,优先检查索引是否命中,以及对象存储的 IOPS 是否受限。

2.5 QueryFrontend:查询加速与缓存

QueryFrontend 是查询路径的前置代理,主要功能是加速大规模日志查询,降低 Querier 的负载。其核心机制包括:

  • 查询拆分:将一个大时间范围的查询(如 7 天)拆分为多个小时间片(如 1 小时)的子查询,并行下发到 Querier。
  • 结果缓存:缓存子查询的结果(包括日志内容和元数据),相同查询直接返回缓存,避免重复计算。
  • 查询重试:当某个 Querier 返回错误或超时时,QueryFrontend 自动将子查询重新调度到其他健康的 Querier 实例。
  • 主动限流:根据后端 Querier 的负载情况,对客户端请求进行排队或限流,防止查询风暴压垮集群。

下图展示了 QueryFrontend 如何将一个复杂查询拆解并并行执行:

QueryFrontend 查询拆分与并行执行 QueryFrontend 查询拆分与并行执行 原始查询 (7天) 子查询 1 (第1天) 子查询 2 (第2天) 子查询 3 (第3天) ... (共7个子查询) Querier 集群 (并行执行) 结果合并

2.6 Compactor:存储优化与数据生命周期管理

Compactor 是一个后台异步组件,负责对对象存储中的 Chunk 文件进行合并、去重和索引优化,以提升查询效率并降低存储成本。

操作 触发条件 效果
Chunk 合并 定期扫描(默认每 10 分钟) 将多个小 Chunk 合并为更大的 Chunk,减少文件数量,降低对象存储的 API 调用次数。
索引重写 Chunk 合并后 重新生成更紧凑的索引文件,加速 Querier 的标签查找。
数据保留与删除 根据配置的保留期(Retention) 删除过期数据,释放存储空间。支持按时间或按标签策略进行细粒度清理。
容量规划提示: Compactor 需要足够的 CPU 和内存来处理 Chunk 合并。如果对象存储中积累了大量小 Chunk,Compactor 的合并压力会显著增加。建议在压测阶段模拟长期运行后的 Chunk 碎片情况,验证 Compactor 的处理能力。

2.7 组件职责与关键指标速查

下表总结了各组件在写入和查询链路中的核心职责,以及 SRE 需要重点监控的关键指标:

组件 写入链路职责 查询链路职责 关键监控指标
Distributor 接收、验证、分片、转发 无 接收速率 (bytes/s)、错误率、gRPC 连接数
Ingester 内存缓冲、Chunk 刷盘 提供实时数据 内存使用率、Chunk 刷盘速率、Flush 队列深度
Querier 无 执行查询、合并结果 P99 查询延迟、并发查询数、对象存储读取吞吐
QueryFrontend 无 查询拆分、缓存、限流 缓存命中率、拆分后子查询数、排队等待时间
Compactor 无 后台优化存储 合并速率、待合并 Chunk 数量、保留策略执行延迟
故障定位口诀: 写入慢查 Distributor 和 Ingester;查询慢查 QueryFrontend 和 Querier;存储膨胀查 Compactor 和 Retention 策略。内存问题优先看 Ingester,CPU 瓶颈优先看 Querier 和 Compactor。