Featured image of post Loki + Promtail 日志架构:像查指标一样查日志

Loki + Promtail 日志架构:像查指标一样查日志

Loki 主服务 + Promtail 代理 + Grafana 展示,从 docker log driver 接入到多行日志识别、retention 清理、nginx 反代的一站式部署

Prometheus 解决的是"指标"问题,但线上排障 80% 的时间还是靠"日志"。传统方案是 ELK(Elasticsearch + Logstash + Kibana),重、占资源、运维复杂。Grafana Labs 推出的 Loki + Promtail 组合走的是另一条路——“像查指标一样查日志”:不建索引、只做标签、查询时按需拉取,配合 Grafana 一站式展示。这篇文章把零散在多个笔记里的部署、配置、踩坑细节,整理成"从零拉起→接入业务容器→清理过期日志"的一条完整路径。

阅读对象:已经在用 Prometheus + Grafana 体系,希望把日志也接进来的开发者、运维同学
覆盖范围:Loki 主服务(v2.9.x 时代)、Promtail / docker log driver 两种采集方式、多行日志识别、retention 清理、Nginx 反向代理

一、为什么是 Loki

在引入 Loki 之前,日志方案无外乎这几类:

  • ELK(Elasticsearch + Logstash + Kibana):经典但重,JVM + 倒排索引吃资源,小机器跑不动
  • EFK(Fluentd 替代 Logstash):稍轻量但仍依赖 ES
  • 自建文件存储 + 简单 grep:能用但没可视化、没聚合
  • 云厂商日志服务:阿里云 SLS、AWS CloudWatch Logs 等,绑定厂商

Loki 的设计哲学是"和 Prometheus 同构":

维度PrometheusLoki
数据类型指标(时序数值)日志(文本行)
索引方式标签(label)标签(label)
存储TSDB 本地/远端对象存储/文件系统
查询语言PromQLLogQL
展示GrafanaGrafana
资源占用低(不做倒排索引)

When to use:如果你已经用 Prometheus + Grafana 做监控,强烈推荐 Loki——同一个 Grafana 实例、同一套标签习惯、几乎零额外学习成本。如果你的日志需要"全文模糊匹配"(Loki 不擅长)或日均日志量 GB 级以上(建议直接上 ES + SLS),那 Loki 就不合适了。

二、架构总览

Loki 体系的角色分工极简:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
业务容器(被采集)
   ├─→ 方式 A:Promtail 进程 → 读容器日志文件 → 推送
   └─→ 方式 B:docker log driver(loki plugin) → 直推
                       Loki 主服务(存储 + 查询)
                       Grafana(展示)
  • Loki:主服务,负责存储日志和处理查询
  • Promtail:代理,负责从各种来源(文件、syslog、journald)采集日志并推给 Loki
  • Grafana:UI 展示 + 数据源对接(和 Prometheus 同一个实例即可)

三、Loki 部署

3.1 直接 docker run(最小可用)

1
docker run -d --name=loki --restart=always -p 3100:3100 grafana/loki:2.9.5

默认数据存到容器内 /loki 目录,容器销毁即数据丢失,生产环境必须挂载出来。

3.2 docker-compose(推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
version: "3"
services:
  loki:
    image: grafana/loki:2.9.5
    container_name: loki
    restart: always
    network_mode: host
    volumes:
      - /home/docker/loki/config:/etc/loki
    command: -config.file=/etc/loki/local-config.yaml

默认配置 /etc/loki/local-config.yaml(Loki 2.9.5 自带):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

schema 版本:v11 是 Loki 2.9 时代的默认 schema。如果以后升级 Loki 主版本,要查 CHANGELOG 看 schema 是否需要迁移。生产环境建议把 chunks 和 rules 目录挂到大磁盘上,不要放系统盘。

四、日志采集:两种方式

4.1 方式 A:Promtail(推荐,灵活)

Promtail 是 Loki 官方的采集器,配置和 Promtail/Loki 同源:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# promtail 配置片段
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://{{LOKI_HOST}}:3100/loki/api/v1/push

scrape_configs:
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        target_label: 'container'
      - source_labels: ['__meta_docker_container_log_stream']
        target_label: 'stream'
    pipeline_stages:
      - docker: {}

多行日志识别:Java 应用的异常堆栈、Nginx 多行 access log 等,默认会被拆成多行记录。配置 loki-pipeline-stagesmultiline 段:

1
2
3
4
5
6
7
options:
  loki-url: "http://{{LOKI_HOST}}:3100/loki/api/v1/push"
  max-size: "50m"
  max-file: "10"
  loki-pipeline-stages: |
    - multiline:
        firstline: '^\[\d{2}:\d{2}:\d{2} \w{4}\]'

上面这条规则表示:以 [HH:MM:SS DAY] 开头的行是新日志的起点,其余行归属同一日志。

4.2 方式 B:docker log driver(最简)

Loki 官方提供了 docker plugin,装上后业务容器启动时加 --log-driver=loki 即可:

1
2
# 安装插件
docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions

国内直接拉可能 403(gcr.io 同款问题),离线场景需要:

1
2
3
4
5
6
# 离线节点
docker plugin disable loki -f
docker plugin rm loki -f
tar -xzf plugins.tar.gz -C /var/lib/docker
service docker restart
docker plugin enable loki

业务容器接入(示例):

1
2
3
4
5
6
7
8
docker run -it -d \
  --name my-app \
  --restart=always \
  --log-driver=loki \
  --log-opt loki-url="http://{{LOKI_HOST}}:3100/loki/api/v1/push" \
  --log-opt max-size=50m \
  --log-opt max-file=10 \
  my-app:v1

小坑loki-pipeline-stages 这种含特殊字符的 option 在 daemon.json 里配置时,JSON 字符串需要转义 \n,写错直接被 docker daemon 拒绝。

4.3 全局模式(所有容器默认走 Loki)

修改 /etc/docker/daemon.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "log-driver": "loki",
  "log-opts": {
    "loki-url": "http://{{LOKI_HOST}}:3100/loki/api/v1/push",
    "max-size": "50m",
    "max-file": "10"
  },
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com",
    "https://docker.nju.edu.cn"
  ]
}

重启 docker 后所有容器都会切换日志驱动,已经在跑的应用会因为日志驱动不匹配启动失败。生产环境切换前先停业务,或者用"按容器 logging 字段覆盖"的折中方案。

4.4 docker-compose 模板

最常用的"logging anchor"写法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
version: "3"
x-logging: &loki-logging
  driver: loki
  options:
    loki-url: "http://{{LOKI_HOST}}:3100/loki/api/v1/push"
    max-size: "50m"
    max-file: "10"
    loki-pipeline-stages: |
      - multiline:
          firstline: '^\[\d{2}:\d{2}:\d{2} \w{4}\]'

services:
  my-service:
    image: my-image:tag
    restart: always
    logging: *loki-logging
    # ...

五、Grafana 对接

Loki + Grafana 是天生一对:

  1. Grafana → ConfigurationData SourcesAdd data sourceLoki
  2. URL 填 http://{{LOKI_HOST}}:3100
  3. 保存

查询语法(LogQL)示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 查名为 my-app 容器的所有日志
{container="my-app"}

# 包含 "ERROR" 的日志
{container="my-app"} |= "ERROR"

# 不包含 "health-check"
{container="my-app"} != "health-check"

# 正则匹配
{container="my-app"} |~ "Exception.*at .*\\(.+\\.java"

# 过去 5 分钟
{container="my-app"}[5m]

六、Retention 清理(关键!)

Loki 默认永不过期,磁盘会被慢慢吃光。

旧方式(已废弃,2.4 之前)

1
2
3
table_manager:
  retention_deletes_enabled: true
  retention_period: 24h

新方式(2.4+)

1
2
3
4
5
6
7
8
compactor:
  retention_enabled: true
  compaction_interval: 10m          # Compactor 检查过期表的间隔
  retention_delete_delay: 5m        # 过期后多久真正删除
  retention_delete_worker_count: 150

limits_config:
  retention_period: 7d              # 保留 7 天

修改后重启 Loki 生效。

七、Nginx 反向代理

Loki 服务端(带 WebSocket 实时推送和 HTTP 查询)需要 Nginx 转发:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
server {
    listen  8080;
    server_name loki.example.com;

    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    location / {
        proxy_pass http://{{LOKI_HOST}}:3100;
        proxy_http_version 1.1;
        proxy_set_header Host loki.example.com;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 60;
        proxy_read_timeout 600;
        proxy_send_timeout 600;
    }
}

必须带 Upgrade/Connection 头:Grafana 查询 Loki 的实时流接口要 WebSocket,没有 Upgrade 头会被 nginx 拒绝。

八、踩坑清单

  1. 磁盘吃光——Loki 默认永久保留日志,必须配置 compactor.retention_enabled
  2. 查询慢——Loki 不索引日志内容,模糊匹配走"全量扫描"。查询时尽量用 |=!= 做精确过滤,避免 |~ 正则匹配超大日志流
  3. 多行日志被拆碎——Java 异常、Nginx 多行 log 必须配 multiline,否则一条错误变成几十条记录
  4. gcr.io 拉镜像失败——grafana/loki-docker-driver 插件托管在 gcr.io,国内走离线打包或者换用 Promtail 方案
  5. schema 升级——Loki 主版本升级时,schema 不兼容会启动失败。务必先查 CHANGELOG 看是否需要 loki-canary 验证、是否需要执行 loki 内置的 schema 迁移工具
  6. 时区问题——Loki 时间戳按 UTC 存储,Grafana 里看日志时间默认也是 UTC。要么在 Grafana 里设 Dashboard timezone 为 Asia/Shanghai,要么在查询时显式带时区

九、2024+ 视角补充

本文写于 2024-05,2024-2026 期间 Loki 关键演进:

  • Loki 3.x(2024-Q3 起):全面 BoltDB-shipper 弃用,统一走 TSDB 存储(与 Prometheus 一样的 TSDB),启动速度 / 写入吞吐 / 内存占用全面优化
  • Loki 3.3+单二进制模式(Loki / Querier / Ingester / Distributor 合并)——小规模部署 1 容器跑 1 个 Loki 即可,无需拆微服务
  • Alloy 替代 Promtail:Grafana 2024 推出 Grafana Alloy(基于 Grafana Agent 演进),统一 Telemetry 采集(Logs / Metrics / Traces / Profiles),Promtail 进入 maintenance 模式
  • Loki 3.4+结构化元数据(Structured Metadata)——日志标签化效率比 2.9 时代提升 10x
  • Loki + Promtail 已成过去:2025+ 推荐 Loki + Alloy 组合
  • 对象存储成熟:S3 兼容存储(GCS、MinIO、Azure Blob、华为 OBS)全支持——本地盘生产已不再推荐

实战建议(2025-2026 视角)

  • Loki 3.3+ 单二进制 + MinIO / S3 存储后端 + Alloy 采集 是 2025+ 主流
  • 大集群(> 50 节点):Loki 微服务模式(querier / ingester / distributor 拆分)+ Kafka 缓冲——高吞吐稳定
  • 向量检索:2025+ Loki 实验支持 向量相似度查询(与 Grafana 11 配合),但生产慎用——性能不如专用向量库
  • 告警:Loki 3.x 内置 ruler(不再依赖 alertmanager)——一站式告警

十、参考资料

  • Loki 官方文档:https://grafana.com/docs/loki/latest/
  • Promtail 配置:https://grafana.com/docs/loki/latest/clients/promtail/
  • LogQL 查询语法:https://grafana.com/docs/loki/latest/logql/
  • 离线插件打包:参考 docker plugin install 文档
  • Grafana Alloy 替代 Promtail:https://grafana.com/docs/alloy/

下一步

使用 Hugo 构建
主题 StackJimmy 设计