2. Loki 架构深度解析:核心组件职责与数据流
Loki 采用微服务架构,将日志的接收、存储、查询、压缩等职责解耦到多个独立组件中。理解每个组件的职责及其之间的数据流,是进行容量规划、性能压测和故障定位的基础。本章将逐一剖析 Distributor、Ingester、Querier、QueryFrontend 和 Compactor 这五大核心组件。
2.1 组件全景与数据流总览
下图展示了 Loki 集群中日志数据从写入到查询的完整生命周期,以及各组件之间的协作关系。
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 查询,从多个数据源聚合日志结果。其查询路径如下:
- 接收查询请求:从 QueryFrontend 或直接来自 Grafana 的 HTTP/gRPC 请求。
- 并行查询 Ingester:对于近期未刷盘的日志,Querier 直接向所有 Ingester 发起实时查询,获取内存中的最新数据。
- 查询对象存储:对于已持久化的历史数据,Querier 根据时间范围和标签索引,从对象存储中读取对应的 Chunk 文件。
- 结果去重与合并:由于数据存在副本,Querier 需要对来自不同 Ingester 或存储的相同日志流进行去重,并按时间戳排序后返回。
性能压测指标: Querier 的并发查询能力、P99 查询延迟、以及从对象存储读取 Chunk 的吞吐量。如果查询慢,优先检查索引是否命中,以及对象存储的 IOPS 是否受限。
2.5 QueryFrontend:查询加速与缓存
QueryFrontend 是查询路径的前置代理,主要功能是加速大规模日志查询,降低 Querier 的负载。其核心机制包括:
- 查询拆分:将一个大时间范围的查询(如 7 天)拆分为多个小时间片(如 1 小时)的子查询,并行下发到 Querier。
- 结果缓存:缓存子查询的结果(包括日志内容和元数据),相同查询直接返回缓存,避免重复计算。
- 查询重试:当某个 Querier 返回错误或超时时,QueryFrontend 自动将子查询重新调度到其他健康的 Querier 实例。
- 主动限流:根据后端 Querier 的负载情况,对客户端请求进行排队或限流,防止查询风暴压垮集群。
下图展示了 QueryFrontend 如何将一个复杂查询拆解并并行执行:
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。