跳转到内容

Loki 面试题

30 道题
分类
可观测性
子分类
logs
题目数
30 道
已阅读 0 / 30 题
1 Loki 的核心架构由哪些组件构成?

答案:

Loki 采用微服务架构,核心组件包括 Distributor、Ingester、Querier、Query Frontend 和 Compactor。

组件职责说明
Distributor日志写入入口接收推流,验证数据,分发到 Ingester
Ingester日志数据写入和临时存储对写入数据进行压缩和索引,定期上传到对象存储
Querier日志查询从 Ingester 和对象存储读取日志数据
Query Frontend查询前端查询缓存、分片、重试和并发控制
Compactor数据压缩和保留合并小文件、应用保留策略、数据清理
Index Gateway索引服务(可选)存储和查询日志索引(TSDB 模式)

数据流:

Promtail/其他采集端 → Distributor → Ingester → 对象存储(S3/MinIO/GCS)
                                      ↑            ↓
                                Querier ←→ 对象存储 + Index Gateway
                              Query Frontend ← Grafana
2 Loki 与 Elasticsearch 的核心区别是什么?

答案:

维度LokiElasticsearch
数据模型Label + 日志行(索引元数据)全文索引(所有字段可索引)
存储日志内容存对象存储,元数据存索引所有数据存本地/云磁盘
写入成本低(元数据索引,日志内容不索引)高(全文索引 + 倒排索引)
查询语言LogQLLucene / EQL / SQL
压缩比高(仅索引 Label,日志内容块压缩)中(全文索引 + 源数据)
资源消耗低-中
存储成本极低(对象存储)高(SSD + 多副本)
全文检索不支持(需要额外配置)原生支持
部署复杂度中(微服务架构)高(集群 + 分片管理)
K8s 原生原生支持(Promtail + K8s服务发现)需额外配置

选型建议:

  • K8s 环境、已有 Prometheus 栈:Loki(统一 Grafana 可视化)
  • 需要全文搜索、复杂文本分析:Elasticsearch
  • 日志量大但查询简单:Loki(成本优势明显)
3 LogQL 的核心语法结构是怎样的?

答案:

LogQL 分为日志查询和指标查询两大类,语法与 PromQL 类似。

日志查询(返回日志行):

# 基础查询:Label 匹配 + 关键词过滤
{job="nginx", namespace="production"} |= "error"

# 正则匹配
{job="nginx"} |~ "status=(5\\d{2})"

# 排除匹配
{job="nginx"} != "healthcheck"

# JSON 解析
{job="nginx"} | json | status >= 500

# 日志模式解析
{job="nginx"} | logfmt | level = "error"

# 标签提取(regex)
{namespace="prod"} | regex "(?P<method>\\w+) (?P<path>[\\w/]+)"

指标查询(返回 Prometheus 格式指标):

# 日志速率
rate({job="nginx"} |= "error" [5m])

# 计数
count_over_time({job="nginx"}[5m])

# 标签值聚合
sum by (namespace) (rate({job="nginx"} |= "error" [5m]))

查询操作符链:

日志流选择器 → 行过滤器 → 解析器 → 标签过滤 → 指标函数
{job="nginx"} |= "500" | json | status > 500 | rate[5m]
4 Loki 的数据索引机制是如何演进的?

答案:

Loki 经历三种索引模式,当前主流是 TSDB 模式。

索引演进历史:

模式版本说明现状
BoltDBv1.x本地 boltdb 文件存储索引废弃
BoltDB Shimv2.0+索引存对象存储,本地只做缓存兼容
TSDBv2.8+(默认)自研 TSDB 索引,类似 Prometheus当前推荐

TSDB 模式优势:

1. 查询性能:比 BoltDB 模式快 10-100 倍
2. 存储效率:索引压缩比更高
3. 内存使用:索引查询可流式处理,不要求全量加载
4. 扩展性:支持索引分片和并发查询
5. 简化运维:无需 Index Gateway 组件

配置启用:

# Helm values.yaml
loki:
  schemaConfig:
    configs:
    - from: "2024-01-01"
      store: tsdb
      object_store: s3
      schema: v13
      index:
        prefix: index_
        period: 24h
5 Loki 的 Ingester 组件是如何工作的?

答案:

Ingester 是 Loki 的核心写入组件,负责接收日志并暂时缓存后上传到对象存储。

写入流程:

Distributor 分发日志到 Ingester
  → Ingester 将日志追加到内存中的 Chunk
  → Chunk 达到阈值(大小/时间/行数)后:
    1. 关闭 Chunk(不再接受新数据)
    2. 压缩 Chunk(Snappy 或 gzip)
    3. 上传到对象存储
    4. 元数据写入索引(TSDB)

Chunk 刷盘策略:

参数默认值说明
chunk_idle_period30m空闲 Chunk 自动关闭
chunk_block_size262144Chunk 块大小(256KB)
chunk_target_size1.5MB目标 Chunk 压缩后大小
max_chunk_age2hChunk 最大存活时间

Ingester 故障处理:

Ingester 无状态(依赖 Replication 和 Receiver)
  → 数据写入前先复制到 3 个 Ingester(默认 replication_factor=3)
  → Ingester 宕机 → 其他 Ingester 有副本
  → 启动后从对象存储恢复数据(wal 目录)

Ingester 扩缩容:

水平扩展:增加 Ingester 副本数
缩容前需等待 Chunk 刷盘完成(drain)
Ingester 无状态设计使其适合 K8s 部署
6 Promtail 是什么?如何采集 K8s 日志?

答案:

Promtail 是 Loki 的官方日志采集端,负责从节点上采集容器日志并推送。

采集架构:

每个 Kubernetes 节点运行一个 Promtail DaemonSet
  → 读取 /var/log/pods/*.log(容器标准输出)
  → 为日志添加 K8s Label(pod_name, namespace, container 等)
  → 通过 HTTP/gRPC 推送到 Loki Distributor

配置文件:

scrape_configs:
- job_name: kubernetes-pods
  kubernetes_sd_configs:
  - role: pod
  pipeline_stages:
  # 添加 K8s 元数据标签
  - labels:
      pod: ""
      namespace: ""
      container: ""

  # 解析 Docker 日志格式
  - docker: {}

  # 匹配多行日志(如 Java 异常栈)
  - multiline:
      firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'

  # 丢弃不需要的日志
  - drop:
      expression: "healthcheck"

- job_name: kubernetes-events
  pipeline_stages:
  - json: {}
  relabel_configs:
  - source_labels: [__meta_kubernetes_namespace]
    target_label: namespace

配置说明:

阶段功能说明
dockerDocker 日志格式解析将 json 行拆分为多字段
criCRI 日志格式解析containerd 日志格式
regex正则提取字段从日志内容提取自定义字段
jsonJSON 解析将 JSON 日志解析为字段
logfmtlogfmt 格式解析key=value 格式解析
multiline多行合并Java 异常栈、SQL 语句
drop丢弃匹配的日志减少存储和传输
labels添加 Label为日志添加元数据
7 LogQL 如何实现日志指标化和告警?

答案:

LogQL 提供多种指标函数,可以将日志内容转换为 Prometheus 格式指标,并集成到告警规则。

日志指标函数:

函数作用示例
rate()日志产生速率rate({job="nginx"}[5m])
count_over_time()日志行计数count_over_time({job="nginx"}[5m])
bytes_rate()日志字节速率bytes_rate({job="nginx"}[5m])
bytes_over_time()日志字节总量bytes_over_time({job="nginx"}[5m])
absent_over_time()日志是否不存在absent_over_time({job="nginx"}[5m])

提取字段后指标化:

# 按状态码统计请求数
sum by (status) (count_over_time({job="nginx"} | json [5m]))

# 计算 P99 响应时间
quantile_over_time(0.99,
  {job="nginx"} | json | unwrap duration [5m]
)

告警规则配置:

groups:
- name: loki-alerts
  rules:
  - alert: HighErrorRate
    expr: |
      sum(rate({job="nginx"} | json | status >= 500 [5m])) > 10      
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Nginx 5xx error rate > 10/s"

  - alert: NoLogsForApp
    expr: |
      absent_over_time({job="myapp", namespace="production"}[10m])      
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "No logs from myapp for 10 minutes"

Grafana Alerting 集成:

Grafana → Loki 数据源 → LogQL 查询 → 基于日志的告警规则
8 Loki 的对象存储配置方式及推荐后端?

答案:

Loki 原生支持多种对象存储后端,日志数据和索引均可存储。

# 通用对象存储配置
storage_config:
  # S3 / MinIO 兼容
  aws:
    s3: s3://region/bucket
    endpoint: s3.amazonaws.com
    access_key_id: AKIA...
    secret_access_key: ...
    insecure: false
    s3forcepathstyle: true      # MinIO 需要

  # GCS
  gcs:
    bucket_name: loki-logs
    service_account: /etc/gcs/sa.json

  # Azure Blob
  azure:
    storage_account_name: lokilogs
    storage_account_key: ...
    container_name: loki

  # 本地文件系统(单节点测试用)
  filesystem:
    directory: /tmp/loki/chunks

Schema 配置(多周期保留策略):

schema_config:
  configs:
  - from: "2020-10-01"
    store: tsdb
    object_store: s3
    schema: v13
    index:
      prefix: index_
      period: 24h

生产推荐:

规模推荐后端说明
单节点测试本地文件系统简单但不可持久
小规模(<1TB/天)MinIO本地部署、S3 兼容
中大规模S3 / GCS稳定、低成本、无需运维
极大规模S3 + 生命周期策略自动归档到 Glacier
9 Loki 如何配置数据保留策略?

答案:

Loki 通过 Compactor 组件和 Table Manager 实现数据保留和清理。

# Compactor 保留策略
compactor:
  working_directory: /data/loki/compactor
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 10

  # 按天配置保留期
  retention_rules:
  # 命名空间粒度
  - selector:
      namespace: "production"
    priority: 1
    period: 30d

  - selector:
      namespace: "staging"
    priority: 2
    period: 14d

  # 所有日志默认保留
  - selector: {}
    priority: 10
    period: 7d

通过 Schema 配置保留:

# Table Manager 方式(旧版)
table_manager:
  retention_deletes_enabled: true
  retention_period: 720h   # 30 天

Ingester 数据保留(Chunk 缓存):

ingester:
  # 内存中 Chunk 的最长保留
  max_chunk_age: 2h
  # Chunk 上传对象存储后保留本地
  # 本地保留用于快速查询
  # 长期保留依赖对象存储

保留删除机制:

Compactor 定时扫描所有 Index/Chunk
  → 检查 retention_rules
  → 超期的数据标记删除
  → 清理对象存储中的已删除数据
10 Loki 的 Query Frontend 如何加速查询?

答案:

Query Frontend 作为查询入口,通过缓存、分片和并发优化加速查询。

query_frontend:
  # 查询分片
  max_outstanding_per_tenant: 2048
  log_queries_longer_than: 5s

  # 结果缓存
  cache_results: true
  results_cache:
    cache:
      # Redis 缓存
      redis:
        endpoint: redis:6379
        expiration: 1h

  # 查询分片配置
  parallelize_shardable_queries: true
  querier:
    max_concurrent: 10

加速机制说明:

机制说明效果
查询分片将大查询按时间或标签拆分为多个子查询并行执行大规模日志查询秒级响应
结果缓存相同查询的缓存命中直接返回重复查询 10ms 级响应
查询重试Querier 失败自动重试故障容错
主动取消客户端取消时终止后端查询节省资源

典型查询优化:

# ❌ 慢查询:大范围无过滤
{namespace="prod"} |= "error"

# ✅ 快查询:缩小时间范围 + 精确匹配
{namespace="prod", app="nginx"} |= "error"

# 也可以指定时间范围
{namespace="prod", app="nginx"} |= "500"
# 查询 Grafana 侧限定时间如 1h
11 Loki 如何处理多租户隔离?

答案:

Loki 通过租户 ID 实现数据和查询的隔离,多租户为可选项。

# 启用多租户模式
loki:
  auth_enabled: true   # 开启后每个请求必须携带 X-Scope-OrgID Header

写入时指定租户:

# Promtail 配置(每个租户独立实例)
clients:
- url: http://loki:3100/loki/api/v1/push
  tenant_id: team-a

# 或 single tenant 模式
# 如果 Promtail 不能携带租户 ID,可通过代理添加 Header

查询时指定租户:

# Grafana 数据源中设置 Tenant ID
# 每个租户只能查自己的数据

# API 直接查询
curl -H "X-Scope-OrgID: team-a" http://loki:3100/loki/api/v1/query_range

多租户部署场景:

租户 A(生产团队):tenant_id = "prod-team"
租户 B(开发团队):tenant_id = "dev-team"

同一 Loki 集群,数据和查询完全隔离
全局管理员通过跳过租户检查查看所有数据
12 Loki 与 Grafana 的集成方式是什么?

答案:

Grafana 原生支持 Loki 数据源,配置简单并支持日志面板和变量联动。

数据源配置:

apiVersion: 1
datasources:
- name: Loki
  type: loki
  url: http://loki:3100
  access: proxy
  jsonData:
    maxLines: 1000
    derivedFields:
    # 从日志提取字段跳转到 Tempo(链路追踪)
    - name: traceID
      type: string
      matcherType: regex
      matcherRegex: "trace_id=(\\w+)"
      url: "$${__value.raw}"
      datasourceName: Tempo

Grafana 日志面板特性:

特性说明
日志卷显示日志速率的时间序列图
实时流查看实时日志流
高亮匹配关键词高亮
显示上下文查看选中日志行前后的上下文
日志详情解析后的结构化字段展示
跳转到 Trace从 traceID 跳转到追踪系统

标签联动:

# Grafana 变量
- name: namespace
  type: query
  query: label_values(namespace)

- name: pod
  type: query
  query: label_values({namespace="$namespace"}, pod)
13 Loki 的 Ruler 组件作用是什么?

答案:

Loki Ruler 组件负责定期评估 LogQL 规则并触发告警,与 Alertmanager 集成。

ruler:
  storage:
    type: s3
    s3:
      bucket: loki-rules
  rule_path: /tmp/loki/rules
  alertmanager_url: http://alertmanager:9093
  ring:
    kvstore:
      store: memberlist

  # 规则存储
  # 支持本地磁盘 / S3 / GCS
  storage_config:
    s3:
      bucket: loki-rules

告警规则示例:

groups:
- name: loki-rules
  rules:
  - alert: HighErrorRate
    expr: |
      sum(rate({app="nginx"} | json | status >= 500 [5m])) > 10      
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "High error rate for nginx"

Ruler 高可用:

多副本 Ruler 通过 Ring 协调
只有 Leader 执行规则评估
Leader 故障后自动切换
14 Loki 如何管理 Chunk 的生命周期?

答案:

Chunk 是 Loki 中日志存储的基本单位,经历从 Ingester 内存到对象存储的完整生命周期。

生命周期阶段:

1. 创建(Ingester 内存)
  → 接收 Distributor 分发的日志行
  → 写入内存 Chunk(包含压缩)

2. 刷盘(Flush)
  → Chunk 达到阈值(大小/时间/空闲)
  → 关闭 Chunk(不可变)
  → 压缩后上传到对象存储
  → 索引信息写入 TSDB

3. 查询
  → Querier 从 Ingester 读热数据(内存 Chunk)
  → Querier 从对象存储读历史数据(已刷盘的 Chunk)

4. 合并(Compactor)
  → 小 Chunk 合并为大 Chunk
  → 覆盖对象的元数据

5. 删除(Retention)
  → Compactor 根据保留策略删除过期 Chunk

Chunk 大小配置:

ingester:
  chunk_target_size: 1572864     # 1.5MB(压缩后)
  chunk_block_size: 262144       # 256KB
  chunk_idle_period: 30m
  chunk_retain_period: 5m
  max_chunk_age: 2h

性能影响:

  • Chunk 太小:对象存储请求多、Compactor 压力大
  • Chunk 太大:内存占用高、刷盘延迟、查询需要扫描更多无用数据
15 Loki 的 Distributor 组件如何验证和处理写入请求?

答案:

Distributor 是 Log 写入的第一站,负责数据验证、去重和分发。

distributor:
  ring:
    kvstore:
      store: memberlist

  # 写入限制
  ingester_heartbeat_timeout: 5s
  rate_store:
    max_parallel: 10

  # 接收限制
  receiever:
    max_recv_msg_size: 4194304          # 4MB 最大消息
    max_connection_age_grace: 0s

处理流程:

Promtail 推送日志流 → Distributor
  → 1. 验证 Label 格式(不能为空、不能超过标签数限制)
  → 2. 验证时间戳(不能太旧或太新)
  → 3. 验证日志行大小(单行不能超过 max_line_size)
  → 4. 将日志流散列到指定 Ingester(一致性哈希)
  → 5. 复制到 replication_factor 个 Ingester
  → 6. 至少半数 Ingester 写入成功即返回成功

限制配置:

distributor:
  # 验证
  extend_wal: true
  max_line_size: 1048576           # 单行最大 1MB
  max_label_name_length: 1024      # Label 名最大长度
  max_label_value_length: 2048     # Label 值最大长度
  max_label_names_per_series: 20   # 单日志流最多标​​签数
16 Loki 如何与 Prometheus 共享基础设施?

答案:

Loki 与 Prometheus 设计理念一致,可以共用 Grafana、Alertmanager 等组件。

统一 Grafana 数据源:

grafana:
  datasources:
  - name: Prometheus
    type: prometheus
    url: http://prometheus:9090
  - name: Loki
    type: loki
    url: http://loki:3100
    jsonData:
      derivedFields:              # 日志 → 指标 联动
      - datasourceName: Prometheus
        matcherRegex: "namespace=(\\w+)"
        name: namespace
        url: '$${__value.raw}'

统一告警:

# 同一 Alertmanager 同时处理 Prometheus 和 Loki 的告警
# Prometheus 告警: rate(node_cpu_seconds_total[5m])
# Loki 告警: rate({app="nginx"}[5m])
alertmanager:
  receivers:
  - name: team-alerts
    slack_configs:
    - channel: '#alerts'

统一变量系统:

# Grafana 变量可以混合 Prometheus 和 Loki 数据源
- name: namespace
  type: query
  query: label_values(namespace)      # Prometheus
  # 或
  query: label_values(namespace)      # Loki

推广实践:

统一告警平台:Prometheus(指标) + Loki(日志) → Alertmanager
统一可视化:Prometheus(趋势图) + Loki(日志面板) → Grafana
统一服务发现:Prometheus 和 Promtail 使用相同的 K8s 元数据
17 Loki 的高可用部署方案是怎样的?

答案:

Loki 通过多副本 + 数据复制 + 微服务隔离实现高可用。

# 微服务模式(推荐生产)
loki:
  structuredConfig:
    ingester:
      replication_factor: 3        # 每个日志流写入 3 个 Ingester
    distributor:
      ring:
        kvstore:
          store: memberlist
    querier:
      max_concurrent: 20

# Helm 多副本
ingester:
  replicas: 3
  resources:
    requests:
      memory: 4Gi
      cpu: 2

distributor:
  replicas: 2

querier:
  replicas: 2

queryFrontend:
  replicas: 2

compactor:
  replicas: 1

单进程模式(测试/小规模):

# Single binary 模式(所有组件同一进程)
loki -config.file=loki.yaml -target=all

# 读写分离模式
loki -config.file=loki.yaml -target=read     # Querier + QueryFrontend
loki -config.file=loki.yaml -target=write    # Distributor + Ingester

故障场景:

故障影响恢复
1 个 Ingester 宕机无影响(replication_factor=3)自动分配新 Ingester
多数 Ingester 宕机写入部分失败需手动恢复
Distributor 宕机部分写入失败LB 直切健康节点
Querier 宕机部分查询失败自动重试其他节点
18 Loki 如何通过 Helm Chart 部署到 K8s?

答案:

Loki 官方提供 Helm Chart,支持单进程和微服务模式。

# 添加仓库
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

# 安装单进程模式(测试)
helm upgrade --install loki grafana/loki \
  --namespace=loki --create-namespace \
  --values single-binary-values.yaml

# 安装微服务模式(生产)
helm upgrade --install loki grafana/loki \
  --namespace=loki --create-namespace \
  --values microservices-values.yaml

生产 values.yaml 示例:

loki:
  auth_enabled: false
  commonConfig:
    replication_factor: 3
  storage:
    type: s3
    bucketNames:
      chunks: loki-chunks
      ruler: loki-ruler
      admin: loki-admin
    s3:
      endpoint: minio.minio:9000
      region: us-east-1
      secretAccessKey: minio123
      accessKeyId: minio
      s3ForcePathStyle: true
      insecure: true

ingester:
  replicas: 3
  persistence:
    enabled: true
    size: 10Gi
  zoneAwareReplication:
    enabled: false

distributor:
  replicas: 2

querier:
  replicas: 2

queryFrontend:
  replicas: 2

compactor:
  enabled: true
  replicas: 1

monitoring:
  dashboards:
    enabled: true
  alerts:
    enabled: true
  serviceMonitor:
    enabled: true
19 Loki 的 boltdb-shipper 模式的工作原理是什么?

答案:

BoltDB Shipper 是 Loki v2.0 引入的索引模式,将索引也存储在对象存储中,简化架构。

Ingester 写入 Chunk → 上传到对象存储
  → 同时将 BoltDB 索引文件上传到对象存储
  → Querier 从对象存储下载索引文件
  → 基于本地缓存快速查询

不需要独立的 Index Gateway
每个 Querier 直接读取共享索引

工作原理:

# BoltDB Shipper 配置
storage_config:
  boltdb_shipper:
    active_index_directory: /data/loki/index
    shared_store: s3
    cache_location: /data/loki/boltdb-cache
    cache_ttl: 24h     # 索引缓存时间

工作流:

1. Ingester 每 15 分钟将 BoltDB 索引文件上传到 S3
2. Querier 定期从 S3 同步索引文件到本地缓存
3. 查询时优先查本地缓存,缓存未命中再从 S3 获取
4. 索引文件按时间分割(通常每 24h 一个索引)

BoltDB vs TSDB 模式:

维度BoltDB ShipperTSDB
索引格式BoltDB(key-value)自研 TSDB
查询性能中等(需加载全部索引)快(流式加载)
内存使用高(全量索引加载到内存)低(按需流式加载)
支持版本v2.0+v2.8+
推荐兼容过渡期新部署首选
20 Loki 如何实现日志行多行合并(如 Java 异常栈)?

答案:

Promtail 的 multiline 阶段可以将多行日志合并为单条,解决异常栈等多行问题。

scrape_configs:
- job_name: java-app
  pipeline_stages:
  # 多行合并
  - multiline:
      # 第一行匹配:以日期开头(Java 异常栈起始标志)
      firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
      # 最大等待时间
      max_wait_time: 3s
      # 最大行数
      max_lines: 128

  # 解析后添加 Label
  - labels:
      app: "java-app"

常见多行模式:

# Java 异常栈
multiline:
  firstline: '^\d{4}-\d{2}-\d{2}'

# Go panic
multiline:
  firstline: '^panic:'

# Python 回溯
multiline:
  firstline: '^Traceback'

# SQL 多行
multiline:
  firstline: '^(SELECT|INSERT|UPDATE|DELETE)'
  max_lines: 50

说明:

  • 只有 Promtail 端支持 multiline,Loki 服务端不支持
  • max_wait_time 控制等待后续行的最长时间
  • max_lines 控制一次合并的最大行数(防止单条日志过大)
21 Loki 的标​​签设计最佳实践是什么?

答案:

Loki 的标签设计直接影响查询性能和存储成本,需遵循与 Prometheus 类似的基数规则。

推荐标签:

标签基数说明
namespace低(<100)必选,K8s 命名空间
pod中(<10000)可选,Pod 级过滤
container低(<100)必选,容器名称
app低(<500)必选,应用名称
job低(<100)Promtail scrape job
level极低(<10)日志级别

禁止的标签:

# ❌ 高基数标签(禁止)
# request_id: "req_abc123"       → 基数百万级
# user_id: "usr_12345"           → 基数百万级
# session_id: "sess_abc"         → 基数百万级
# ip: "10.0.0.1"                → 基数万级

# ✅ 正确做法:用 LogQL 解析器提取
{app="nginx"} | json
# 提取 user_id 等字段但不作为索引标签

标签数量控制:

Loki 建议每个日志流 5-10 个标签
单条日志流标签数上限:20(可配置)
标签总数建议:< 100 万活跃 Label 组合

标签选择器优化:

# ❌ 高基数标签过滤→扫描大量数据
{pod="myapp-abc123"}  # → 需要扫索引找该 Pod

# ✅ 先缩小范围再过滤
{namespace="prod", app="nginx"} |= "500"
22 Loki 如何通过 LogQL 实现日志采样分析?

答案:

LogQL 提供 ratecount_over_timetopk 等函数实现日志统计和采样。

# 1. 日志速率(每秒行数)
rate({namespace="prod", app="nginx"}[5m])

# 2. 按状态码统计
sum by (status) (
  count_over_time({namespace="prod", app="nginx"} | json [5m])
)

# 3. Top 5 最慢请求
topk(5,
  avg by (path) (
    quantile_over_time(0.95,
      {namespace="prod", app="nginx"}
      | json
      | unwrap response_time [5m]
    )
  )
)

# 4. 错误比例
sum(rate({namespace="prod", app="nginx"} | json | status >= 500 [5m]))
/ sum(rate({namespace="prod", app="nginx"} | json [5m]))

# 5. 日志采样(取前 100 条)
topk(100, {namespace="prod", app="nginx"} |= "error" | json | status)

# 6. 异常日志模式识别(pattern 解析)
{namespace="prod"} | pattern "<ip> - - <method> <path> <status> <size>" | status >= 500

聚合分析模式:

# 每 5 分钟错误趋势
sum by (namespace) (
  count_over_time({namespace="prod", app="nginx"} | json | status >= 500 [5m])
)

# 请求路径分布
topk(10,
  sum by (path) (
    count_over_time({app="nginx"} | json [1h])
  )
)
23 Loki 的 Compactor 组件如何处理索引压缩?

答案:

Compactor 是 Loki 的后台维护组件,负责索引合并和保留策略执行。

compactor:
  working_directory: /data/loki/compactor
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 10

  # TSDB 模式
  tsdb:
    # 索引压缩
    compact_interval: 10m
    # 索引过期
    retention:
      minimum_interval: 1h

  # 对象存储
  shared_store: s3

Compactor 工作流程:

1. 定期扫描对象存储中的索引文件
2. 合并小索引文件成大索引文件
3. 删除重复或过期的索引记录
4. 标记已删除的 Chunk(retention)
5. 清理对象存储中已标记删除的文件
6. 更新元数据

Compactor 的读写分离:

Compactor 是写入集中型组件
推荐独立部署,不与其他组件混部
需要足够的 CPU 和内存(索引合并是密集操作)
Compactor 的单副本设计(Leader 选举)
24 Loki 如何通过速率限制保护自身稳定性?

答案:

Loki 提供多层速率限制,防止过载写入或查询导致服务不稳定。

# 全局速率限制
loki:
  limits_config:
    # 每租户限制
    ingestion_rate_mb: 10           # 写入速率限制(MB/s)
    ingestion_burst_size_mb: 25     # 突发写入限制
    max_streams_per_user: 10000     # 每租户日志流上限
    max_global_streams_per_user: 0  # 全局日志流上限

    # 查询限制
    max_query_length: 721h          # 最大查询时间范围(30天)
    max_query_series: 500           # 单查询最大系列数
    max_query_range: 0              # 最大范围查询跨度

    # 行限制
    max_line_size: 1048576          # 单行最大长度(1MB)
    max_line_size_truncate: true    # 超长截断而非拒绝

    # 重试限制
    max_entries_limit_per_query: 5000  # 单查询最大条数

拒绝行为:

写入超过 ingestion_rate_mb:
  → 返回 429 Too Many Requests
  → Promtail 自动重试(指数退避)

查询超过 max_query_length:
  → 返回 400 Bad Request
  → 提示缩小查询时间范围
25 Loki 的 LogQL 模式解析(Pattern)语法是什么?

答案:

Pattern 解析器将日志行中的变量字段提取出来,适用于结构固定但不完全是 JSON 的日志。

# 模式语法:<name> 表示提取字段
# 示例日志: "10.0.0.1 - GET /api/v1/users 200 1234"

# Pattern 解析
{job="nginx"}
  | pattern "<ip> - - <method> <path> <status> <size>"

# 解析后可以用提取的字段过滤
| status >= 500
| ip != "127.0.0.1"

复杂模式示例:

# 原始日志: '[2024-01-01 12:00:00] [INFO] [request_id=abc123] user=admin action=login'

# Pattern 提取
| pattern "[<timestamp>] [<level>] [request_id=<request_id>] user=<user> action=<action>"

# 按用户统计动作
sum by (user, action) (
  count_over_time({app="myapp"} | pattern "[<timestamp>] [<level>] [request_id=<request_id>] user=<user> action=<action>" [5m])
)

Pattern vs Regex:

# Pattern(更简单、性能更好)
| pattern "<ip> - - <method> <path> <status> <size>"

# Regex(更灵活、性能稍差)
| regex "(?P<ip>\\S+) - - (?P<method>\\S+) (?P<path>\\S+) (?P<status>\\d+) (?P<size>\\d+)"

Pattern 优势:

  • 语法简洁,无需正则专业知识
  • 执行性能优于 regex 解析器
  • 适合 Nginx、Apache 等格式固定的日志
26 Loki 如何通过 `unwrap` 实现数值提取?

答案:

unwrap 是 LogQL 的数值提取操作,从日志行中提取数值字段用于指标计算。

# 1. 提取响应时间计算 P99
quantile_over_time(0.99,
  {app="nginx"} | json | unwrap response_time [5m]
)

# 2. 按路径统计平均响应时间
avg by (path) (
  avg_over_time(
    {app="nginx"} | json | unwrap response_time [5m]
  )
)

# 3. 字节量统计
sum by (method) (
  sum_over_time(
    {app="nginx"} | json | unwrap body_bytes [5m]
  )
)

# 4. 最大值
max_over_time(
  {app="nginx"} | json | unwrap upstream_time [5m]
)

# 5. 排序
sort(
  avg_over_time(
    {app="nginx"} | json | unwrap response_time [5m]
  )
)

unwrap 要求:

  • 提取的值必须是数值类型(int/float)
  • 标签提取后的字段才能 unwrap
  • 可配合除错处理 __error__ 标签
27 Loki 如何处理日志采集端(Promtail/Docker/OTel)的差异?

答案:

Loki 支持多种采集端接入,各采集端的定位和功能差异如下。

采集端适用场景优势劣势
PromtailK8s 环境原生 K8s 服务发现、Label 注入仅支持 Loki
Docker PluginDocker 单机部署简单功能有限
Fluent Bit轻量采集资源消耗极低需额外配置
Fluentd复杂管道插件丰富资源消耗偏高
LogstashELK 迁移大量过滤器
OpenTelemetry CollectorOTel 标准统一采集管道较新

Promtail 特有功能:

# K8s 自动服务发现
scrape_configs:
- job_name: kubernetes-pods
  kubernetes_sd_configs:
  - role: pod
  pipeline_stages:
  - cri: {}                    # containerd 日志格式
  - docker: {}                 # Docker 日志格式
  - labels:
      pod: ""
      container: ""
      namespace: ""

# 文件日志
- job_name: system-logs
  static_configs:
  - targets: [localhost]
    labels:
      job: varlogs
      __path__: /var/log/*.log

Fluent Bit 接入:

output:
  name: loki
  match: *
  host: loki
  port: 3100
  labels: job=fluentbit, app=$TAG
  label_keys: $TAG
  auto_kubernetes_labels: on
28 Loki 查询时遇到 429 错误是什么原因?如何解决?

答案:

429 错误表示请求速率超过 Loki 配置的速率限制。

原因分析:

原因说明表现
写入速率超限超过 ingestion_rate_mbDistributor 返回 429
查询并发超限超过 max_concurrentQuery Frontend 返回 429
日志流超限超过 max_streams_per_userDistributor 拒绝新流

解决方法:

# 1. 增大速率限制
limits_config:
  ingestion_rate_mb: 20
  ingestion_burst_size_mb: 50
  max_streams_per_user: 20000
  max_concurrent: 30

# 2. 客户端调整(Promtail)
clients:
  # 启用写缓冲
  backoff_config:
    min_period: 100ms
    max_period: 10s
    max_retries: 10
  # 本地缓冲(网络故障时)
  # 默认开启,缓冲目录 /tmp/promtail

监控速率限制:

# 被拒绝的写入请求
rate(loki_discarded_samples_total[5m])

# 速率限制器状态
loki_ingester_rate_limit_logs_per_second
29 Loki 与 ELK 在 K8s 环境下的选型对比?

答案:

维度LokiELK(Elasticsearch + Logstash + Kibana)
部署复杂度简单(Helm Chart)复杂(需要配置 ES 集群、分片)
资源消耗低-中高(ES 多副本、内存密集型)
存储成本极低(对象存储)高(SSD 本地存储)
查询语言LogQL(简洁)Lucene(功能完整但复杂)
全文搜索有限强大
K8s 原生原生(Promtail)需额外采集端(Filebeat)
扩容简单(微服务、水平扩展)需要管理分片和 rebalance
可视化依赖 Grafana内置 Kibana
社区CNCF 项目Elastic 公司主导
典型场景K8s 日志、DevOps企业搜索、安全分析

选型建议:

K8s 环境、已有 Grafana、日志量大但查询简单 → Loki
需要全文搜索、复杂分析、企业合规要求 → ELK
混合部署:Loki 做运维日志快速查询,ELK 做审计/全文检索
30 Loki 的典型告警规则有哪些?

答案:

groups:
- name: loki-alerts
  rules:
  - alert: LokiLogHighErrorRate
    expr: |
      sum(rate({app="nginx"} | json | status >= 500 [5m])) > 10      
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High error rate in nginx logs"
      description: "Error rate is XQOPEN $value XQCLOSE/s"

  - alert: LokiLogPanicDetected
    expr: |
      count_over_time({namespace="production"} |= "panic" [5m]) > 0      
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Panic detected in production"
      description: "Application panic found in logs"

  - alert: LokiLogNoOutput
    expr: |
      absent_over_time({app="myapp", namespace="production"}[15m])      
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "No logs from myapp for 15 minutes"

  - alert: LokiLogSlowRequests
    expr: |
      max_over_time(
        {app="nginx"} | json | unwrap request_time [5m]
      ) > 5      
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Slow requests detected"

Loki 自身告警:

- alert: LokiRequestErrors
  expr: |
    rate(loki_request_duration_seconds_count{status_code=~"5.."}[5m]) > 0.01    
  for: 5m
  labels:
    severity: warning

- alert: LokiDiskFull
  expr: |
    (loki_ingester_disk_usage_bytes / loki_ingester_disk_limit_bytes) > 0.9    
  for: 5m
  labels:
    severity: critical