2. Loki 架构基础:整体架构、日志存储模型与 Prometheus 异同
在深入部署 Agent 之前,必须理解 Loki 的架构设计。Loki 的架构灵感来源于 Prometheus,但针对日志数据的特点做了根本性调整。本章将拆解其核心组件、存储模型,并与 Prometheus 进行对比,帮助你建立清晰的架构认知。
2.1 Loki 整体架构:三大核心组件
Loki 采用微服务架构,其读写路径分离,主要由三个组件构成:Distributor(分发器)、Ingester(写入器)和 Querier(查询器)。它们协同工作,实现了日志的高效写入与查询。
2.1.1 Distributor(分发器)
- 职责:接收来自 Agent(如 Promtail)的日志流,进行数据校验(如时间戳合法性、标签格式)和哈希分片。
- 关键机制:使用一致性哈希环将日志流分发到对应的 Ingester 实例,确保同一日志流的日志被发送到同一个 Ingester。
- 高可用:Distributor 是无状态的,可以水平扩展,通常部署多个副本。
2.1.2 Ingester(写入器)
- 职责:负责将日志数据写入内存并打包成 Chunk,最终将 Chunk 刷写到对象存储(如 S3、GCS、MinIO)。
- 状态管理:Ingester 是有状态的,它维护着当前正在写入的“热 Chunk”。
- 数据可靠性:在 Chunk 刷写到对象存储之前,数据仅存在于 Ingester 内存中。如果 Ingester 崩溃,未刷写的数据会丢失。因此,生产环境通常启用 WAL(Write Ahead Log) 或部署多个副本。
2.1.3 Querier(查询器)
- 职责:处理来自 Grafana 或 LogCLI 的 LogQL 查询请求。
- 查询路径:Querier 会同时查询所有 Ingester 的内存数据(热数据)和对象存储中的历史 Chunk(冷数据),合并结果后返回。
- 无状态:Querier 也是无状态的,可以轻松扩展以应对高并发查询。
架构要点: 写入路径是“推”模式,查询路径是“拉”模式。这种读写分离设计使得 Loki 能够独立扩展写入和查询能力,非常适合日志这种写入量远大于查询量的场景。
2.2 日志存储模型:Chunk 与 Index
Loki 的存储模型是其与 Elasticsearch 等传统日志系统最大的区别。它不建立全文索引,而是将日志数据组织成 Chunk,并仅对日志的标签(Labels)建立索引。
2.2.1 Chunk(数据块)
- 定义:Chunk 是 Loki 中日志数据的最小存储单元。它包含一组连续的、属于同一标签集的日志行。
- 生命周期:Ingester 在内存中构建 Chunk,当 Chunk 达到一定大小(默认 1.5MB)或时间阈值(默认 1 小时)时,将其刷写到对象存储。
- 压缩:Chunk 在刷写前会进行压缩,常用压缩算法包括 gzip、snappy 和 lz4,以降低存储成本。
2.2.2 Index(索引)
- 定义:Index 存储的是标签(Labels)到 Chunk 的映射关系。它告诉 Loki:“对于标签 {job="nginx"},在时间范围 [T1, T2] 内,有哪些 Chunk 包含了相关日志”。
- 存储后端:早期版本使用 BoltDB,新版本(2.8+)默认使用 TSDB(时间序列数据库)作为索引后端,性能更高。
- 查询效率:由于只索引标签,Loki 的索引体积远小于 Elasticsearch 的全文索引,这是 Loki 存储成本低的关键原因。
重要提示: 不要将日志内容作为标签(如 request_id、user_email)。这会导致索引爆炸,使 Loki 性能急剧下降。标签应保持低基数(Low Cardinality),例如
job、instance、level。
2.3 Loki 与 Prometheus 的异同
Loki 的设计哲学与 Prometheus 高度一致,但针对日志数据的特点做了关键调整。理解它们的异同,有助于你更好地掌握 Loki 的使用方式。
| 对比维度 | Prometheus | Loki |
|---|---|---|
| 数据模型 | 时间序列(指标名 + 标签 + 时间戳 + 数值) | 日志流(标签 + 时间戳 + 日志行文本) |
| 索引策略 | 对所有标签建立倒排索引 | 仅对标签建立索引,不索引日志内容 |
| 存储后端 | 本地磁盘(TSDB) | 对象存储(S3/GCS/MinIO) |
| 数据压缩 | gorilla 压缩算法(针对浮点数优化) | gzip/snappy/lz4(针对文本优化) |
| 查询语言 | PromQL(Prometheus Query Language) | LogQL(Log Query Language,兼容 PromQL 语法) |
| 数据推送 | Pull 模式(主动拉取) | Push 模式(Agent 主动推送) |
| 典型用途 | 指标监控(CPU、内存、请求速率) | 日志聚合与排查(错误日志、审计日志) |
2.3.1 核心差异详解
- 索引策略:Prometheus 对每个时间序列的所有标签建立索引,支持高效的标签过滤。Loki 只索引标签,日志内容不建索引,因此查询日志内容时必须进行全文扫描(类似于
grep)。 - 存储成本:由于不索引日志内容,Loki 的存储成本通常比 Elasticsearch 低 5-10 倍,但查询速度在复杂全文搜索场景下会慢于 ES。
- 数据推送方式:Prometheus 采用 Pull 模式,由 Prometheus Server 主动拉取目标数据。Loki 采用 Push 模式,由 Agent(如 Promtail)主动将日志推送到 Distributor。这使得 Loki 更适合动态环境(如 Kubernetes)。
架构启示: 如果你熟悉 Prometheus,可以将 Loki 理解为“日志版的 Prometheus”。它们共享标签体系,但 Loki 牺牲了全文索引能力,换取了极低的存储成本和运维复杂度。在日志量巨大的场景下,这种取舍是值得的。