2. Loki 组件拓扑全景:核心角色与交互
Loki 的架构采用微服务设计,将日志的写入、存储、查询与生命周期管理拆分为多个独立组件。理解这些组件的角色及其交互关系,是优化查询性能、定位瓶颈的前提。本章从控制平面调度视角,逐一拆解核心组件。
2.1 核心组件一览
下表列出了 Loki 生产部署中最关键的 6 个组件及其职责定位:
| 组件 | 角色定位 | 关键职责 | 调度关注点 |
|---|---|---|---|
| Distributor | 写入入口网关 | 接收日志流,校验数据,基于哈希环分发至 Ingester | 请求分发均衡、限流、重试 |
| Ingester | 写入与内存缓存 | 接收日志并写入内存 chunk,定期刷盘至对象存储 | chunk 刷新时机、内存水位、写入 QPS |
| Query Frontend | 查询入口与拆分 | 接收查询请求,拆分/缓存/合并子查询结果 | 查询拆分粒度、缓存命中、队列调度 |
| Querier | 查询执行引擎 | 从 Ingester 和对象存储拉取数据,执行 LogQL 计算 | 并行度、数据局部性、存储 IO |
| Index Gateway | 索引服务 | 提供日志流索引查询,加速标签匹配 | 索引分片、缓存命中、查询延迟 |
| Compactor | 存储合并与清理 | 合并小文件、清理过期数据、生成索引 | 合并周期、资源消耗、数据一致性 |
调度视角提示: 在控制平面调度策略中,Distributor 和 Query Frontend 是典型的“无状态网关”,适合水平扩展;Ingester 和 Index Gateway 是有状态节点,调度时需考虑哈希环与数据分片。
2.2 写入路径:Distributor → Ingester
日志写入路径是典型的“先分发、后缓存”模式:
- Distributor 接收来自 Promtail、Docker 插件或 SDK 的日志推送。
- Distributor 对日志流进行校验(标签合法性、时间戳范围),并根据 标签哈希 计算目标 Ingester。
- 通过 gRPC 将日志流发送至对应的 Ingester 实例。
- Ingester 将日志追加到内存中的 chunk,并在达到刷新阈值(时间/大小)后异步写入对象存储(如 S3、GCS)。
性能关键点: Distributor 的哈希环一致性决定了写入是否均匀。若环倾斜,部分 Ingester 会过载,导致写入延迟抖动。
2.3 查询路径:Query Frontend → Querier → Ingester + Index Gateway + 存储
查询路径比写入路径更复杂,涉及多个组件的协同:
- Query Frontend 接收 LogQL 查询,将其拆分为多个子查询(按时间范围或并行度),并放入内部队列。
- Querier 从队列中拉取子查询任务,同时向 Ingester(内存中的最新数据)和 Index Gateway(索引匹配)发起请求。
- Index Gateway 返回匹配的日志流标识,Querier 据此从对象存储中拉取对应的 chunk 数据。
- Querier 执行 LogQL 过滤、聚合,将结果返回给 Query Frontend 合并后返回客户端。
2.4 组件拓扑关系图
下图展示了 Loki 核心组件之间的写入与查询数据流:
2.5 Compactor:后台生命周期管理
Compactor 不直接参与写入或查询路径,但它是控制平面调度中不可忽视的组件:
- 合并小文件: 将对象存储中的多个小 chunk 合并为更大的文件,减少查询时的文件句柄开销。
- 索引生成: 为合并后的数据生成新的索引,供 Index Gateway 使用。
- 数据清理: 根据保留期(retention)删除过期数据,释放存储空间。
调度陷阱: Compactor 在合并期间会消耗大量 CPU 和内存。若调度策略未限制其并发度,可能导致同一节点上的 Querier 或 Ingester 资源被抢占,引发查询超时。
2.6 组件交互总结
从控制平面调度视角,可将组件分为三类:
| 类别 | 组件 | 调度策略要点 |
|---|---|---|
| 无状态网关 | Distributor, Query Frontend | 水平扩展、负载均衡、请求限流 |
| 有状态节点 | Ingester, Index Gateway | 哈希环分片、数据迁移、故障恢复 |
| 后台任务 | Compactor | 资源隔离、并发控制、定时触发 |