2. Loki 基础回顾:架构、LogQL 与标签流
在深入告警降噪之前,必须统一团队对 Loki 核心概念的认知。本章从架构、查询语言、标签与流三个维度,为后续标签优化策略打下坚实的技术基础。
2.1 Loki 整体架构
Loki 采用微服务架构,核心组件包括 Distributor、Ingester、Querier 和 Compactor。与 Prometheus 不同,Loki 不进行全文索引,而是通过标签索引日志流,大幅降低存储成本。
架构启示:在弹性伸缩场景中,Distributor 的标签哈希分发机制决定了日志流的分布。标签基数过高会导致 Distributor 内存压力增大,这是后续优化的重要切入点。
2.2 LogQL 查询语言
LogQL 是 Loki 的查询语言,语法与 PromQL 高度兼容。它分为两个核心部分:日志流选择器和日志管道。
2.2.1 日志流选择器
流选择器通过标签匹配过滤日志流,语法与 Prometheus 标签匹配器一致:
{app="nginx", environment="production"}
{job=~"api-server.*", namespace="default"}
{cluster="prod-us-east", container!="sidecar"}
| 匹配器类型 | 语法 | 说明 | 示例 |
|---|---|---|---|
| 精确匹配 | = |
标签值完全相等 | app="nginx" |
| 正则匹配 | =~ |
标签值匹配正则表达式 | job=~"api-.*" |
| 反向匹配 | != |
标签值不相等 | container!="sidecar" |
| 反向正则 | !~ |
标签值不匹配正则 | level!~"debug|trace" |
2.2.2 日志管道操作
管道操作符对匹配到的日志行进行二次处理,常用操作包括:
- line_format:重新格式化日志行输出
- label_format:修改或添加标签
- unwrap:从日志内容中提取数值用于指标计算
- rate/count_over_time:计算日志速率或时间窗口内的计数
{app="nginx"} |= "error" | json | line_format "{{.message}}"
{job="api-server"} | logfmt | duration > 1.5 | rate($__interval)
性能陷阱:管道操作中的正则表达式和 JSON 解析会消耗 CPU 资源。在弹性伸缩场景中,应避免在查询时做复杂解析,优先在采集端完成结构化。
2.3 标签(Label)与流(Stream)
标签是 Loki 的核心抽象,决定了日志的存储、索引和查询效率。理解标签与流的关系,是告警降噪的前提。
2.3.1 标签的定义与作用
标签是键值对,用于标识日志的来源和属性。每个日志行在写入时都附带一组标签,Loki 根据标签组合将日志组织成不同的流(Stream)。
| 标签类型 | 示例 | 基数建议 | 说明 |
|---|---|---|---|
| 固定标签 | cluster, namespace |
低(< 100) | 环境、集群等不变属性 |
| 动态标签 | pod, instance |
中(< 10,000) | Pod 名称、实例 ID 等 |
| 高基数标签 | request_id, user_id |
极高(> 100,000) | ⚠️ 应避免作为标签 |
2.3.2 流(Stream)的概念
流是具有相同标签组合的日志序列。例如,标签组合 {app="nginx", pod="nginx-abc123"} 定义了一个唯一的流。Loki 将流作为存储和索引的基本单位。
2.3.3 标签基数与性能的关系
标签基数是影响 Loki 性能的最关键因素。在弹性伸缩场景中,Pod 频繁创建和销毁会导致标签基数剧烈波动,进而引发以下问题:
- Ingester 内存飙升:每个新流都需要在内存中创建新的 chunk,大量短生命周期流会导致内存碎片。
- Distributor 哈希冲突:高基数标签导致哈希环分布不均,部分节点负载过高。
- 查询性能下降:流选择器需要扫描更多索引条目,查询延迟增加。
- 告警抖动:流频繁创建/删除导致指标出现毛刺,触发误告警。
工程经验:在 Kubernetes 弹性伸缩场景中,建议将 Pod 名称等动态标识从标签中移除,改用
__pod_name__ 内部标签或日志内容字段。目标是将流数量控制在 10,000 以内,这是 Loki 官方推荐的单集群最佳实践上限。