2. Loki 核心架构:读写路径分离与核心组件详解

Loki 的架构设计核心思想是 读写路径分离,这与传统时序数据库(如 Prometheus)的架构有本质区别。这种设计使得 Loki 能够高效处理海量日志的写入,同时保证查询的实时性与资源隔离性。本章将深入剖析 Distributor、Ingester、Querier 三大核心组件,并通过数据流图展示日志从采集到查询的完整生命周期。

2.1 读写路径分离架构总览

Loki 将数据写入与查询请求分由不同的组件链处理,避免读写相互阻塞。写入路径负责接收、验证、分发日志流;读取路径负责索引查找、数据检索与结果聚合。

路径 核心组件 主要职责 数据流向
写入路径 Distributor → Ingester 接收日志、验证标签、分片、写入内存/对象存储 实时流式写入,低延迟
读取路径 Querier → Ingester + 对象存储 解析查询、从内存和长期存储中检索日志 按需查询,支持并行扫描
设计优势: 写入路径不依赖查询索引,避免了传统日志系统(如 ELK)中写入压力大时查询性能急剧下降的问题。Ingester 的内存数据可被 Querier 实时查询,实现“写入即可见”。

2.2 核心组件详解

2.2.1 Distributor(分发器)

Distributor 是写入路径的入口,负责接收来自 Promtail、Fluentd 等采集器的日志推送。其核心工作包括:

  • 标签验证与清洗: 检查标签名是否符合规范(如不包含特殊字符),拒绝无效数据。
  • 哈希分片: 根据日志流的标签集合计算哈希值,通过一致性哈希环决定将日志转发到哪个 Ingester 实例。
  • 负载均衡与副本控制: 默认将每个日志流复制到 replication_factor(通常为 3)个 Ingester 上,保证高可用。
  • 速率限制: 对每个租户(tenant)的写入速率进行限制,防止恶意流量或突发写入压垮后端。

2.2.2 Ingester(写入器)

Ingester 是写入路径的核心状态组件,负责将日志数据暂存于内存,并最终刷写到对象存储(如 S3、GCS)或文件系统中。

阶段 行为 说明
接收 追加日志到内存中的 Chunk 每个日志流对应一个 Chunk,Chunk 是压缩后的日志块
切分 Chunk 达到大小或时间阈值后关闭 默认 1MB 或 1 小时,先到先切
刷写 将关闭的 Chunk 上传到对象存储 上传后 Chunk 变为只读,Ingester 释放内存
故障恢复 通过 WAL(Write Ahead Log)恢复未刷写数据 Ingester 重启时从本地 WAL 重放日志,防止数据丢失
关键指标: 监控 loki_ingester_chunks_flushed_total 和 loki_ingester_memory_chunks。如果内存 Chunk 数量持续增长而不刷写,说明刷写队列阻塞,可能导致 OOM。

2.2.3 Querier(查询器)

Querier 是读取路径的入口,负责处理 LogQL 查询请求。它需要同时从两个数据源获取日志:

  • Ingester 内存数据: 查询尚未刷写到对象存储的最新日志,保证实时性。
  • 对象存储中的历史 Chunk: 查询已持久化的日志,通常通过索引(如 BoltDB、TSDB)加速定位。

Querier 采用 并行查询 策略:将查询拆分为多个子任务,分发到所有 Querier 实例(或通过 Query Frontend 进行缓存和分片),最后合并结果返回给客户端。

2.3 数据流图:写入路径与读取路径

以下两张 SVG 图分别展示日志写入和查询的完整数据流,帮助理解组件间的协作关系。

图 2-1:写入路径数据流

Loki 写入路径数据流 采集器 (Promtail) Distributor 验证/分片 Ingester 内存 Chunk Ingester 内存 Chunk 对象存储 (S3/GCS) 说明:Distributor 根据标签哈希将日志流分发到多个 Ingester;Ingester 将 Chunk 刷写到对象存储。

图 2-2:读取路径数据流

Loki 读取路径数据流 客户端 (Grafana/API) Querier 查询解析/合并 Ingester 内存数据 对象存储 历史 Chunk 索引存储 (BoltDB/TSDB) 说明:Querier 同时查询 Ingester(实时数据)和对象存储(历史数据),并通过索引加速定位。

2.4 组件协作与故障排查要点

理解组件间的依赖关系,对于快速定位故障至关重要。以下列出常见故障场景及其关联组件:

故障现象 可能原因 涉及组件 排查命令/指标
日志写入延迟高 Distributor 分片不均或 Ingester 刷写慢 Distributor, Ingester loki_distributor_ingester_appends_total, loki_ingester_chunks_flush_queue_length
查询超时或返回空 Querier 无法连接 Ingester 或索引损坏 Querier, Ingester, 索引存储 loki_querier_ingester_query_duration_seconds, 检查 Ingester 是否在 ring 中
Ingester OOM 内存 Chunk 未及时刷写,或日志流过多 Ingester loki_ingester_memory_chunks, 调整 chunk_target_size
数据丢失 Ingester 崩溃且 WAL 损坏 Ingester, WAL 检查 WAL 目录完整性,确认 ingester.wal-enabled
最佳实践: 在部署 Loki 时,务必为 Distributor 和 Querier 配置独立的 ring 和 memberlist,确保组件发现稳定。同时,为 Ingester 挂载持久化卷用于 WAL,避免因 Pod 重启导致内存数据丢失。

2.5 本章小结