说明
与其他日志记录系统不同,Loki 的构建思想是仅为日志标签建立索引,而不为原始日志建立索引。这意味着 Loki 的操作更简单,并且效率更高。
选择使用 Lok i做日志存储和查询工具的原因
- 不对日志进行全文索引。通过压缩并仅索引元数据,Loki 更加易于操作且运行成本更低,相比传统的 ELK 要轻便很多
- 能够跟 Grafana 监控平台无缝集成,grafana 最终的目标可能是实现 log,metric,tracing 的大统一
- 社区非常活跃、发展速度非常快
- 使用与 Prometheus 相同的标签对日志流进行索引和分组,从而使你能够使用与 Prometheus 相同的标签在指标和日志之间无缝切换
- 特别适合存储 Kubernetes Pod 日志。诸如 Pod 标签之类的元数据会自动被抓取并建立索引
metrics 相关
因为 Loki 中代码很多直接是从 Cortex 中移植过来的,所以它的 Metrics 也分成了好几个部分:
- 以 loki_* 开头的主要是 Loki 组件和 LogQL 查询相关的性能指标
- 以 cortex_* 开头的主要是 Loki 组件状态,集群状态相关的指标
- 以 prometheus-* 开头的主要是 Alert 事件通知相关指标
基本概念
Loki 需要存储两种不同类型的数据:chunks(块)和 indexes(索引)
注意:index 允许进行范围查询以获取多个 chunk ID,chunk 存储是一个简单的 chunk id => bytes/blob
Loki 在单独的 streams(流)中接收日志,其中每个流都由其租户 ID 和标签集唯一标识。
当流中的日志 entries(条目)到达时,它们被压缩为 chunks 并保存在 chunks 存储中
与 Loki 的其他核心组件不同,Chunk store 不是独立的服务,作业或流程,而是嵌入在需要访问 Loki 数据的两个服务中的库:ingester、querier
index 是由以下项构成键的条目的集合:
注意:对于 Cassandra,index 条目被建模为单独的列值。hash key 变为行键,range key 变为列键。
- A hash key 这是所有读取和写入所必需的。
- A range key 这对于写入是必需的,而对于读取则可以省略,可以通过前缀或范围来查询。
Chunk 格式
注意:mint 和 maxt 分别描述了最小和最大的 Unix 纳秒时间戳
-------------------------------------------------------------------
| | |
| MagicNumber(4b) | version(1b) |
| | |
-------------------------------------------------------------------
| block-1 bytes | checksum (4b) |
-------------------------------------------------------------------
| block-2 bytes | checksum (4b) |
-------------------------------------------------------------------
| block-n bytes | checksum (4b) |
-------------------------------------------------------------------
| #blocks (uvarint) |
-------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
-------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
-------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
-------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
-------------------------------------------------------------------
| checksum(from #blocks) |
-------------------------------------------------------------------
| #blocks section byte offset |
-------------------------------------------------------------------
Block 格式
一个块由一系列条目组成,每个条目都是一个单独的日志行。
注意:块的字节被使用 Gzip 压缩并存储。以下是未压缩时的形式:
注意:ts 是日志的 Unix 纳秒时间戳,而 len 是日志条目的字节长度
-------------------------------------------------------------------
| ts (varint) | len (uvarint) | log-1 bytes |
-------------------------------------------------------------------
| ts (varint) | len (uvarint) | log-2 bytes |
-------------------------------------------------------------------
| ts (varint) | len (uvarint) | log-3 bytes |
-------------------------------------------------------------------
| ts (varint) | len (uvarint) | log-n bytes |
-------------------------------------------------------------------
读取路径:
- querier 接收 HTTP/1 数据请求
- querier 将查询传递给 ingesters 查询内存中的数据
- ingesters 接收到读取请求并返回与查询匹配的数据(如果有)
- ingesters 如果返回数据,则 querier 会从后备存储延迟加载数据并对其执行查询
- querier 将遍历所有接收到的数据并进行重复数据删除,从而通过 HTTP/1 连接返回最终数据集
写路径:
- distributor 收到 HTTP/1 请求以存储流数据
- 使用 hash ring 对每个流进行哈希处理
- distributor 将每个流发送到适当的 inester 和它们的副本(基于配置的复制因子)
- 每个 ingester 都会为该流的数据创建一个块或将其附加到现有块中。每个租户和每个标签集的块都是唯一的
- distributor 通过 HTTP/1 连接以成功代码作为响应
网友评论