Grafana Loki 是 Grafana Labs 推出的日志聚合系统,设计理念与 Prometheus 类似:只对标签建立索引,而不对日志全文做倒排索引。这种取舍让它在大规模日志场景下的存储成本和资源消耗远低于 ELK 这类方案。对于使用 Docker 部署服务的团队来说,把 Loki 接入现有环境,可以在几乎不增加运维负担的前提下,获得统一的日志查询、筛选和可视化能力。本文将从架构原理讲起,一步步完成 Loki、Promtail 与 Grafana 的 Docker 集成。

一、Loki 的架构与核心概念
Loki 之所以轻量,关键在于它的索引策略。传统日志系统如 Elasticsearch 会对日志的每一行内容建立倒排索引,日志量越大索引膨胀越严重。而 Loki 只对少量的标签(label)建索引,日志正文以压缩块的形式存放在对象存储或本地文件系统中。查询时先通过标签定位到具体的日志流,再对日志内容做实时的全文过滤,这就是典型的“先缩小范围、再线性扫描”的思路。
一个完整的 Loki 体系包含三个角色。首先是采集端 Promtail,它负责从各种数据源读取日志,打上标签后推送给 Loki;其次是 Loki 服务端,包含 distributor、ingester、querier 等组件,负责接收、存储和查询日志;最后是 Grafana,作为可视化层提供查询界面。如果你使用 Docker Desktop 或安装了 Loki 插件,也可以直接用 docker logs 的驱动将日志发送到 Loki,省去独立部署 Promtail 的步骤。
理解标签的设计非常重要。Loki 要求每个日志流必须至少有一个标签,标签数量不宜过多。常见的做法是用 container_name、compose_service、image 这类维度作为标签,而不要把 request_id 这种高基数值放进标签,否则会产生大量微小日志流,导致索引膨胀和查询性能劣化。
二、使用 Docker Compose 部署 Loki 全套组件
用 Docker Compose 部署是最省心的方式,一条命令即可拉起日志平台。下面是一份可直接使用的编排文件,包含 Loki、Promtail 和 Grafana 三个服务。注意 Promtail 需要挂载 Docker 的 socket 文件和容器日志目录,才能实现服务发现和日志读取。
version: "3.8"
services:
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- loki-data:/loki
promtail:
image: grafana/promtail:2.9.0
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
volumes:
loki-data:
grafana-data:部署前还需要准备 Promtail 的配置文件。这里推荐使用 docker_sd_configs 服务发现机制,Promtail 会自动侦测 Docker 中的容器,从 Docker 元数据中提取标签,无需为每个容器手写静态配置。使用这个方案时,需要在 Loki 侧适当放宽标签的校验规则,允许 __meta_docker_container_name 等带双下划线的标签通过。
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 15s
relabel_configs:
# 提取容器名作为标签
- source_labels: [__meta_docker_container_name]
regex: /(.*)
target_label: container
# 过滤掉 Grafana 自身,避免日志循环
- source_labels: [__meta_docker_container_name]
regex: grafana
action: drop准备好两个文件后,执行 docker compose up -d 即可启动全部服务。打开浏览器访问 3000 端口进入 Grafana,默认账号密码均为 admin。在 Connections 中添加 Loki 数据源,地址填写 http://loki:3100,保存后即可在 Explore 页面查询日志。
三、另一种方案:Loki Docker 日志驱动
除了 Promtail 采集,Loki 还提供了官方的 Docker 日志驱动插件 loki。它把日志发送逻辑内置到 Docker 引擎层面,容器启动时直接指定 --log-driver=loki,日志就会实时推送到 Loki,不需要在宿主机上额外部署任何采集进程。对于不想维护 Promtail 配置的小规模场景,这个方案更加简单直接。
安装插件的命令是 docker plugin install loki/docker-driver:latest --alias loki --grant-all-permissions。之后启动容器时指定日志选项即可,例如下面的写法会把日志发送到本机的 Loki 实例,并自动附上容器名和标签。
# 安装插件 docker plugin install loki/docker-driver:latest \ --alias loki --grant-all-permissions # 以 loki 驱动启动容器 docker run --log-driver=loki \ --log-opt loki-url="http://127.0.0.1:3100/loki/api/v1/push" \ --log-opt loki-batch-size=400 \ --log-opt labels="com.docker.compose.service" \ nginx:latest
两种方案各有取舍。日志驱动方案的优点是链路短、延迟低、配置少,缺点是每个容器都要指定选项(虽然可以在 daemon.json 中设为全局默认),而且 Docker 引擎重启期间日志可能丢失。Promtail 方案则更灵活,支持多行日志合并、结构化解析和更复杂的标签重写规则,是生产环境更常见的选择。也可以在 daemon.json 中把 loki 设为默认驱动,这样所有新容器自动接入,配置文件位于 /etc/docker/daemon.json。
四、用 LogQL 查询容器日志
日志接入完成后,查询是日常使用的高频操作。Loki 使用 LogQL 查询语言,语法分为日志流选择器和过滤表达式两部分。日志流选择器用大括号包裹标签条件,例如 {container="nginx"} 表示查询 nginx 容器的全部日志;再加上管道过滤就能做内容检索,比如 {container="nginx"} |= "error" 表示只保留包含 error 关键字的行。
# 查询所有容器的日志量排行
sum by (container) (count_over_time({container=~".+"}[5m]))
# 查询包含异常关键字并统计每分钟出现次数
rate({container=~".+"} |= "exception" [1m])
# 提取 JSON 日志中的 level 字段并过滤
{container="app"} | json | level="error"实际排障时,常用的组合技巧包括:用正则匹配 |~ "timeout|refused" 同时追踪多种错误;用 logfmt 管道解析键值对格式的应用日志;在 Grafana 面板中把 LogQL 查询做成图表,直观展示错误趋势。建议在 Explore 中先调试好查询语句,确认结果符合预期后再固化到仪表盘中。
最后提几个实践建议。第一,为 Loki 配置合理的日志保留期,默认配置的保留时间较短,生产环境需要根据存储空间调整 retention_period;第二,控制标签基数,标签总数建议控制在个位数;第三,为日志平台本身设置资源上限,避免 Loki 与业务容器争抢资源;第四,如果容器规模持续扩大,可以考虑把 Loki 的存储后端切换到 S3 兼容的对象存储,实现水平扩展。通过这套轻量级方案,Docker 环境下的日志管理可以从分散的命令行查看升级为集中式、可查询、可视化的完整体系,而整体资源开销只有传统 ELK 方案的一小部分。
Grafana LokiDocker日志收集Promtail修改时间:2026-08-31 01:06:49