2. Loki 架构回顾:核心组件职责与数据流
在深入变更门禁的数据传输优化之前,必须重新理解 Loki 的读写路径与核心组件。Loki 采用 微服务架构,各组件独立部署、职责单一,通过 gRPC 和 HTTP 通信。本章聚焦 Distributor、Ingester、Querier、Compactor 四个关键角色,以及它们如何协同完成日志的接收、存储、查询与压缩。
2.1 组件职责速览
| 组件 | 核心职责 | 关键特征 |
|---|---|---|
| Distributor | 接收客户端推送的日志流,验证、分片、复制后转发给 Ingester | 无状态、水平扩展、基于一致性哈希分发 |
| Ingester | 接收日志并写入内存中的 Chunk,定期将 Chunk 刷入对象存储 | 有状态、管理 Chunk 生命周期、支持 WAL 与 Handoff |
| Querier | 处理 LogQL 查询,从 Ingester(热数据)和对象存储(冷数据)合并结果 | 无状态、支持并行查询、缓存加速 |
| Compactor | 合并对象存储中的小 Chunk 为大 Chunk,清理过期数据,生成索引 | 后台异步任务、降低存储碎片、提升查询效率 |
架构要点: Distributor 和 Querier 是无状态组件,可以快速弹性伸缩;Ingester 是有状态组件,需要关注其内存使用与 Chunk 刷出策略。Compactor 虽然不参与实时读写路径,但对存储成本和查询性能影响显著。
2.2 数据写入路径详解
日志从客户端到持久化存储,经历以下关键步骤:
- 客户端推送:Promtail、Docker 插件、OTEL Collector 等通过 HTTP 将日志流推送到 Distributor。
- Distributor 验证与分片:校验日志格式、租户 ID、标签合法性;使用一致性哈希将日志流分片到对应的 Ingester 副本。
- Ingester 接收与缓存:日志追加到内存中的 Stream 对象,按时间与标签组织成 Chunk;同时写入预写日志(WAL)保障崩溃恢复。
- Chunk 刷出:当 Chunk 达到最大大小(默认 1.5 MB)或最大时长(默认 30 分钟)时,Ingester 将 Chunk 压缩并上传到对象存储(如 S3、GCS、MinIO)。
- Compactor 后台合并:定期扫描对象存储中的小 Chunk,合并为更大的 Chunk,并更新索引。
变更门禁关注点: 写入路径中的 Distributor → Ingester 段是数据传输优化的第一道关卡。如果 Distributor 分片不均或 Ingester 刷出延迟过高,会导致推送失败或背压,直接影响变更流水线的稳定性。
2.3 数据查询路径详解
查询路径与写入路径相对独立,但共享 Ingester 和对象存储:
- Querier 接收查询:接收 LogQL 查询请求,解析标签选择器与时间范围。
- 并行查询 Ingester:对于时间范围覆盖当前热数据的部分,Querier 直接向 Ingester 发起 gRPC 请求,获取内存中尚未刷出的 Chunk。
- 并行查询对象存储:对于历史数据,Querier 根据索引定位对象存储中的 Chunk 文件,下载并解压。
- 结果合并与返回:Querier 将来自 Ingester 和对象存储的日志条目按时间排序、去重,返回给客户端。
性能陷阱: 如果 Compactor 未及时合并小 Chunk,对象存储中会存在大量碎片文件,导致 Querier 在查询时需要发起大量 HTTP 请求,增加延迟和成本。变更门禁的查询步骤应设置合理的超时与重试策略。
2.4 核心数据流架构图
下图展示了 Distributor、Ingester、Querier、Compactor 之间的数据流向与存储依赖关系:
2.5 组件协作时序
以下时序表总结了变更门禁场景下各组件的交互顺序与关键超时参数:
| 步骤 | 发起方 | 接收方 | 协议 | 关键参数 |
|---|---|---|---|---|
| 1. 推送日志 | Promtail | Distributor | HTTP/gRPC | push_timeout: 10s |
| 2. 转发日志 | Distributor | Ingester | gRPC | grpc_compression: snappy |
| 3. 刷出 Chunk | Ingester | 对象存储 | HTTP/S3 | chunk_target_size: 1.5MB |
| 4. 合并 Chunk | Compactor | 对象存储 | HTTP/S3 | compactor_interval: 10m |
| 5. 查询日志 | Querier | Ingester + 对象存储 | gRPC + HTTP | query_timeout: 30s |
优化切入点: 在变更门禁流水线中,步骤 1 和步骤 2 的传输效率直接影响日志到达的实时性。建议启用 gRPC 压缩(snappy 或 gzip)、调整 Distributor 的 gRPC 连接池大小,并监控 Ingester 的
ingester_chunk_utilization 指标,避免刷出积压。
2.6 组件职责对比总结
下表从 状态性、扩展方式、存储依赖、故障影响 四个维度对比四个核心组件:
| 维度 | Distributor | Ingester | Querier | Compactor |
|---|---|---|---|---|
| 状态性 | 无状态 | 有状态(内存 Chunk) | 无状态 | 无状态(任务驱动) |
| 扩展方式 | 水平扩展(加实例) | 水平扩展(需调整分片) | 水平扩展(加实例) | 单实例或主备 |
| 存储依赖 | 无 | 本地磁盘(WAL)+ 对象存储 | 对象存储(读) | 对象存储(读写) |
| 故障影响 | 推送中断(快速恢复) | 丢失未刷出 Chunk(WAL 可恢复) | 查询失败(重试即可) | 合并延迟,不影响读写 |
架构决策提醒: 变更门禁场景下,Ingester 的故障恢复时间直接影响日志完整性。务必配置 WAL 并设置合理的
ingester.wal-enabled=true,同时将 WAL 目录挂载到持久化卷,避免 Pod 重建导致数据丢失。