2. Loki 基础回顾:架构、存储模型与 LogQL

在深入 eBPF 网络观测与节点池巡检回退演练之前,必须牢固掌握 Loki 的核心设计理念。Loki 并非传统日志搜索引擎(如 ELK),而是受 Prometheus 启发、专为日志而生的“日志聚合系统”。其核心优势在于:不进行全文索引,仅对日志元数据(标签)建立索引,从而大幅降低存储成本与资源消耗。

2.1 Loki 整体架构

Loki 采用微服务架构,主要组件包括 Distributor、Ingester、Querier 和 Compactor。下图展示了日志从采集到查询的完整链路:

Loki 核心架构与日志流 Loki 核心架构与日志流 Promtail / Agent Distributor Ingester 对象存储 Querier Compactor Grafana / API 说明: • Distributor 负责验证和分发日志流,基于 hash 环将日志路由到正确的 Ingester。 • Ingester 将日志数据在内存中批量打包后刷入对象存储,同时支持即时查询。 • Compactor 定期合并和去重存储中的索引与日志块,提升查询效率。
架构要点:Loki 将写路径(Distributor → Ingester → 对象存储)与读路径(Querier → 对象存储 / Ingester)分离,支持水平扩展。Compactor 在后台异步优化存储布局,对查询透明。

2.2 日志存储模型

Loki 的存储模型围绕 Stream(日志流)和 Chunk(数据块)两个核心概念构建:

概念 说明 关键属性
Stream 一组具有相同标签集合(Label Set)的日志条目。例如:{job="nginx", instance="10.0.0.1"} 定义了一个唯一的日志流。 由 Label Set 唯一标识;每个 Stream 内部日志按时间戳严格排序。
Chunk 属于同一 Stream 的一段连续日志数据,经过压缩后存储。Chunk 是 Loki 的最小存储单元。 包含起始/结束时间戳、压缩后的日志内容、元数据(如 CRC 校验)。
Index 仅对 Stream 的标签建立倒排索引,不索引日志内容。支持基于标签的高效过滤。 存储于 BoltDB 或 TSDB(自 Loki 2.8+),支持按标签名和值快速定位 Stream。
Object Store Chunk 和 Index 的持久化后端,支持 S3、GCS、Azure Blob、MinIO 等。 按时间分桶(Bucket),每个 Bucket 内包含多个 Chunk 文件和索引文件。
设计哲学:Loki 不解析日志内容,因此标签设计至关重要。合理的标签(如 job、namespace、pod)能大幅提升查询性能;避免使用高基数标签(如 user_id、request_id),否则会导致索引膨胀。

2.3 LogQL 查询语言基础

LogQL 是 Loki 的查询语言,语法受 PromQL 和 SQL 启发。它分为两个核心部分:日志流选择器和日志管道。

2.3.1 日志流选择器

选择器用于过滤出特定的日志流,语法与 Prometheus 标签匹配器一致:

# 选择 job 为 nginx 的所有日志流
{job="nginx"}

# 选择 namespace 为 production,且 pod 名以 web- 开头的日志流
{namespace="production", pod=~"web-.*"}

# 选择 instance 不等于 10.0.0.1 的日志流
{job="api-server", instance!="10.0.0.1"}

2.3.2 日志管道操作

管道操作符对选中的日志行进行进一步处理、过滤或提取:

操作符 作用 示例
|= 行包含(子串匹配) {job="nginx"} |= "error"
!= 行不包含 {job="nginx"} != "healthcheck"
|~ 行匹配正则表达式 {job="api"} |~ "5[0-9]{2}"
!~ 行不匹配正则 {job="api"} !~ "DEBUG|TRACE"
\| json 解析 JSON 格式日志行,提取字段 {job="api"} | json | status_code > 500
\| logfmt 解析 logfmt 格式日志行 {job="app"} | logfmt | level="error"
\| regexp 使用命名正则提取字段 {job="web"} | regexp "(?P<method>\w+) (?P<path>/\S+)"
\| unwrap 从日志行中提取数值用于聚合 {job="api"} | json | unwrap duration_ms | avg()

2.3.3 聚合与指标查询

LogQL 支持将日志转换为指标(Metric Query),语法与 PromQL 高度兼容:

# 统计过去 5 分钟每个 job 的错误日志数量
sum by (job) (count_over_time({job=~".+"} |= "error" [5m]))

# 计算 P99 响应延迟(假设日志中包含 duration_ms 字段)
quantile_over_time(0.99, {job="api"} | json | unwrap duration_ms [1h]) by (instance)

# 计算错误率(错误日志 / 总日志)
sum(rate({job="nginx"} |= "5[0-9]{2}" [5m])) / sum(rate({job="nginx"}[5m]))
性能注意事项:
  • 优先使用 |= 和 != 进行简单过滤,避免在管道中使用高开销的正则表达式。
  • 聚合查询(如 rate、count_over_time)的步长(Step)不宜过小,建议 ≥ 15s。
  • 使用 unwrap 提取数值时,确保字段类型一致,避免类型转换错误导致查询失败。

2.4 本章小结