跳转至

600_Loki日志聚合

一句话概述:Grafana Loki 是轻量级日志聚合系统,被称为"日志界的 Prometheus"——只索引标签不索引日志内容,存储成本极低,通过 LogQL 查询语言搜索日志,与 Grafana 深度集成,是云原生时代 ELK 的轻量替代方案。

核心知识点表

概念白话解释
Loki日志存储和查询引擎,只索引标签(省存储)
Grafana Alloy日志采集器(替代已废弃的 Promtail),收集并发送日志到 Loki
LogQLLoki 的查询语言,类似 PromQL 但用于日志
Label标签,日志的元数据(如 job、namespace),Loki 用标签索引
Stream日志流,同一组标签的日志集合
Chunk数据块,日志的压缩存储单元
Tenant租户,多租户隔离(不同团队的日志互不可见)

安装配置

Docker Compose 部署(推荐)

# docker-compose.yml — Loki + Grafana Alloy + Grafana
services:
  loki:
    image: grafana/loki:3.7.1            # Loki v3.7
    container_name: loki
    ports:
      - "3100:3100"                      # HTTP API 端口
    volumes:
      - ./loki-config.yaml:/etc/loki/local-config.yaml  # 配置文件
      - loki-data:/loki                  # 数据持久化
    command: -config.file=/etc/loki/local-config.yaml

  alloy:
    image: grafana/alloy:latest          # Grafana Alloy(日志采集)
    container_name: alloy
    volumes:
      - ./alloy-config.alloy:/etc/alloy/config.alloy  # 配置文件
      - /var/log:/var/log:ro             # 挂载系统日志(只读)
      - /var/lib/docker/containers:/var/lib/docker/containers:ro  # Docker 日志
    command: run /etc/alloy/config.alloy
    depends_on:
      - loki

  grafana:
    image: grafana/grafana:latest        # Grafana 可视化
    container_name: grafana
    ports:
      - "3000:3000"                      # Web UI
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin  # 管理员密码
    volumes:
      - grafana-data:/var/lib/grafana

volumes:
  loki-data:
  grafana-data:

Loki 配置

# loki-config.yaml — Loki 基本配置
auth_enabled: false                      # 单租户模式(测试用)

server:
  http_listen_port: 3100                 # HTTP 端口

common:
  path_prefix: /loki                     # 数据路径前缀
  storage:
    filesystem:
      chunks_directory: /loki/chunks     # 数据块目录
      rules_directory: /loki/rules       # 规则目录
  replication_factor: 1                  # 复制因子(单节点 =1)
  ring:
    kvstore:
      store: inmemory                    # 内存存储(单节点)

schema_config:
  configs:
    - from: 2024-01-01                   # 生效日期
      store: tsdb                        # 使用 TSDB 存储
      object_store: filesystem           # 对象存储:本地文件系统
      schema: v13                        # Schema 版本
      index:
        prefix: index_                   # 索引前缀
        period: 24h                      # 索引周期

limits_config:
  retention_period: 720h                 # 日志保留 30 天

Grafana Alloy 配置

// alloy-config.alloy — 日志采集配置

// 采集本地日志文件
local.file_match "logs" {
  path_targets = [{
    __path__ = "/var/log/*.log",         // 日志文件路径
    job      = "system-logs",            // 标签:job
  }]
}

loki.source.file "local_files" {
  targets    = local.file_match.logs.targets  // 引用上面的文件匹配
  forward_to = [loki.write.default.receiver]  // 转发到 Loki
}

// 采集 Docker 容器日志
discovery.docker "containers" {
  host = "unix:///var/run/docker.sock"   // Docker socket
}

loki.source.docker "docker_logs" {
  host       = "unix:///var/run/docker.sock"
  targets    = discovery.docker.containers.targets
  forward_to = [loki.write.default.receiver]
}

// 发送到 Loki
loki.write "default" {
  endpoint {
    url = "http://loki:3100/loki/api/v1/push"  // Loki 地址
  }
}
docker compose up -d                     # 启动所有服务
# 在 Grafana 中添加 Loki 数据源:
# Configuration → Data Sources → Add → Loki
# URL: http://loki:3100

基本使用

LogQL 查询

# === 日志流选择器(选择哪些日志) ===
{job="system-logs"}                      # 选择 job=system-logs 的日志
{container="nginx"}                      # 选择 nginx 容器的日志
{namespace="production", app="web"}      # 多标签组合

# === 过滤表达式(筛选日志内容) ===
{job="system-logs"} |= "error"           # 包含 "error" 的日志
{job="system-logs"} != "debug"           # 不包含 "debug" 的日志
{job="system-logs"} |~ "ERROR|WARN"      # 正则匹配 ERROR 或 WARN
{job="system-logs"} !~ "health|ping"     # 正则排除 health 或 ping

# === 管道操作(处理日志) ===
{container="nginx"} 
  | json                                 # 解析 JSON 格式日志
  | status >= 400                        # 过滤 HTTP 状态码 >= 400
  | line_format "{{.method}} {{.path}} {{.status}}"  # 格式化输出

{container="app"} 
  | logfmt                              # 解析 logfmt 格式
  | level="error"                       # 过滤 error 级别
  | duration > 1s                       # 过滤耗时 > 1秒

# === 聚合查询(统计分析) ===
# 每 5 分钟的错误日志数量
rate({container="nginx"} |= "error" [5m])

# 每个容器的日志速率
sum by (container) (rate({namespace="production"}[5m]))

# 最近 1 小时的错误总数
count_over_time({job="app"} |= "ERROR" [1h])

# P99 响应时间(需要 JSON 解析)
quantile_over_time(0.99, {container="api"} | json | unwrap duration [5m]) by (path)

在 Grafana 中查看

# 1. 打开 Grafana → Explore
# 2. 选择 Loki 数据源
# 3. 输入 LogQL 查询
# 4. 切换视图:
#    - Logs → 查看原始日志
#    - Table → 表格视图
#    - Graph → 日志量趋势图

# 创建 Dashboard 面板:
# Dashboard → Add Panel → 选择 Loki 数据源
# 使用 rate() 创建日志速率图
# 使用 count_over_time() 创建错误统计图

CLI 查询(logcli)

# 安装 logcli
# 从 Loki releases 下载 logcli 二进制

# 查询日志
export LOKI_ADDR=http://localhost:3100    # Loki 地址

logcli query '{job="system-logs"}'        # 查询日志
logcli query '{job="system-logs"}' --limit=50  # 限制 50 条
logcli query '{job="system-logs"}' --since=1h  # 最近 1 小时
logcli query '{job="system-logs"}' --from="2024-01-01T00:00:00Z" --to="2024-01-02T00:00:00Z"  # 时间范围

# 实时查看(类似 tail -f)
logcli query '{job="system-logs"}' --tail  # 实时日志流

# 列出标签
logcli labels                             # 列出所有标签
logcli labels job                         # 列出 job 标签的所有值

高级用法

Kubernetes 日志采集

# 在 K8s 中用 Helm 部署 Loki Stack
helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack \
  --namespace monitoring \
  --create-namespace \
  --set grafana.enabled=true \           # 同时安装 Grafana
  --set loki.persistence.enabled=true \  # 持久化存储
  --set loki.persistence.size=50Gi       # 存储大小
# 自动采集所有 Pod 日志,按 namespace/pod/container 标签分类

告警规则

# loki-rules.yaml — 基于日志的告警
groups:
  - name: app-alerts                     # 规则组名
    rules:
      - alert: HighErrorRate             # 告警名
        expr: |                          # LogQL 表达式
          rate({container="api"} |= "ERROR" [5m]) > 10
        for: 5m                          # 持续 5 分钟
        labels:
          severity: critical             # 严重级别
        annotations:
          summary: "API 服务错误率过高"
          description: "最近 5 分钟 ERROR 日志超过 10 条/秒"

对象存储后端(生产推荐)

# loki-config.yaml — 使用 S3 存储
common:
  storage:
    s3:
      endpoint: s3.amazonaws.com         # S3 端点
      bucketnames: loki-logs             # S3 桶名
      region: ap-southeast-1             # 区域
      access_key_id: ${AWS_ACCESS_KEY_ID}
      secret_access_key: ${AWS_SECRET_ACCESS_KEY}
# S3 存储成本极低,适合长期保存日志

常见报错

报错信息原因解决方案
entry out of order日志时间戳乱序检查时钟同步或启用 unordered_writes
max streams limit标签组合过多减少高基数标签(如 user_id)
context deadline exceeded查询超时缩小时间范围或优化 LogQL
no logs found没有匹配的日志检查标签名和采集器配置
rate limit exceeded推送速率超限调整 limits_config

速查表

# === LogQL 速查 ===
{label="value"}                  # 标签选择
|= "text"                       # 包含文本
!= "text"                       # 不包含文本
|~ "regex"                      # 正则匹配
| json                          # 解析 JSON
| logfmt                        # 解析 logfmt
| line_format "{{.field}}"      # 格式化输出
rate({...}[5m])                  # 每秒日志速率
count_over_time({...}[1h])       # 时间范围内总数
sum by (label) (rate({...}[5m])) # 按标签聚合

# === 关键端口 ===
# 3100 → Loki HTTP API
# 9096 → Loki gRPC

# === API 端点 ===
# POST /loki/api/v1/push          → 推送日志
# GET  /loki/api/v1/query         → 查询日志
# GET  /loki/api/v1/query_range   → 范围查询
# GET  /loki/api/v1/labels        → 列出标签
# GET  /ready                     → 就绪检查

同类对比

特性LokiElasticsearchSplunkClickHouse
索引方式只索引标签全文索引全文索引列式索引
存储成本极低很高
查询速度中等
学习曲线
Grafana 集成原生插件自带 UI插件
资源需求很高
适合场景云原生日志全文搜索企业级大数据分析

选型建议:云原生环境日志收集首选 Loki(轻量、成本低、Grafana 原生集成);需要全文搜索选 Elasticsearch(ELK Stack);大规模日志分析选 ClickHouse;不差钱选 Splunk。