自建日志聚合实战:Loki + Promtail + Grafana,把散落各处的日志收进一个可搜索的仪表盘
上一期我们搭好了 Prometheus + …
上一期我们搭好了 Prometheus + Grafana 的监控大盘,CPU、内存、磁盘这些指标看得清清楚楚。但指标只能告诉你”哪里不对劲”,真要定位到具体原因,还得看日志。可自建环境里日志散得到处都是:每个容器的 stdout、每台机器的 /var/log、每个应用自己写的那份独立文件,出问题时你得一台一台 SSH 上去 tail、grep,特别磨人。
今天 IT 大叔就带你把日志也收进来,用 Loki + Promtail + Grafana 搭一套轻量级日志聚合系统。和重量级的 ELK(Elasticsearch + Logstash + Kibana)相比,Loki 只索引标签、不索引正文,存储成本低一个数量级,几 GB 内存就能跑得动,特别适合自建、单机或小规模集群的场景。
三个角色的分工
先把角色说清楚,免得后面绕晕:Promtail 是采集端,部署在每台要收日志的机器上,负责 tail 日志文件、给日志打标签、再推给 Loki;Loki 是存储与查询端,负责接收日志、按标签建索引、响应查询;Grafana 是可视化端,用 LogQL 语言像查 PromQL 一样查日志。
这套架构最巧的一点是:Loki 不索引日志正文,只索引你打上去的标签(比如 host、job、app),正文按时间切块压缩存储。查询时先靠标签快速定位到块,再在块内扫描关键词。所以它的铁律是——标签要少而精,高基数标签(user_id、request_id 这种取值成千上万的)千万别当标签用,否则索引会爆炸;而查询关键词则可以随便写。
第一步:Docker Compose 一把梭
三个服务用一个 docker-compose.yml 全搞定。存好之后 docker compose up -d 启动即可。
services:
loki:
image: grafana/loki:3.4.2
container_name: loki
restart: unless-stopped
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki/loki-config.yaml:/etc/loki/local-config.yaml:ro
- loki-data:/loki
ports:
- "3100:3100"
promtail:
image: grafana/promtail:3.4.2
container_name: promtail
restart: unless-stopped
command: -config.file=/etc/promtail/promtail-config.yaml
volumes:
- ./promtail/promtail-config.yaml:/etc/promtail/promtail-config.yaml:ro
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- promtail-positions:/tmp
grafana:
image: grafana/grafana:11.4.0
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
volumes:
loki-data:
grafana-data:
promtail-positions:
这里注意两点:一是版本号我写的是示例,实际部署前请去 Docker Hub 确认最新稳定版再固定,别图省事用 latest,生产环境固定版本是底线;二是 Promtail 的 positions 文件一定要持久化——它记录”每个日志文件读到第几行”,丢了就会重复收或漏收。
第二步:Promtail 配置,两个 job 各司其职
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
host: node01
__path__: /var/log/*log
- job_name: docker
static_configs:
- targets:
- localhost
labels:
job: docker
__path__: /var/lib/docker/containers/*/*-json.log
pipeline_stages:
- json:
expressions:
output: log
stream: stream
attrs: attrs
- json:
expressions:
tag: attrs.tag
source: attrs
- labels:
tag:
stream:
- output:
source: output
两个 job 分工明确:system 负责收系统日志(/var/log 下的各种 *.log),docker 负责收容器的 json 日志。docker 那个 job 里的 pipeline_stages 是关键——Docker 的日志是 JSON 格式,外层套了 log、stream、time 这些字段,得先用 json 阶段把它剥开、取出真正的日志正文,再把镜像名 tag 提出来当标签。不写这段的话,你查到的日志会是一坨转义过的 JSON,看着头疼。
第三步:Loki 配置
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
retention_period: 168h
Loki 的配置在 3.x 里精简了不少,common 块把单机和集群共用的存储、ring 配置集中到了一起。单机部署就抓三个要点:filesystem 存储(日志块写本地磁盘)、inmemory 的 ring(单机不需要 etcd/consul 这类协调组件)、replication_factor: 1(不复制,只存一份)。retention_period: 168h 表示日志保留一周自动清理,防止磁盘被悄悄撑爆。
第四步:Grafana 接数据源,用 LogQL 查日志
Grafana 起好后,进 Connections → Data sources → Add data source,搜 Loki,URL 填 http://loki:3100(三个容器在同一个 compose 网络里,直接用服务名互访),保存。之后就能在 Explore 里用 LogQL 查了,几个常用姿势:
# 圈定某台机器的系统日志,再过滤出含 error 的行
{job="varlogs", host="node01"} |= "error"
# 正则匹配,(?i) 表示不区分大小写
{job="docker"} |~ "(?i)error|failed|panic"
# 结构化日志:先 json 解析,再按字段过滤
{job="docker", tag="nginx"} | json | status_code >= 500
# 范围聚合:最近5分钟 error 出现次数
count_over_time({job="varlogs"} |= "error" [5m])
# 按主机分组统计,画成时序图
sum by (host) (count_over_time({job="varlogs"}[5m]))
这些查询的逻辑很直白:{job="docker"} 是标签选择器,先圈定范围;|= 是包含匹配,|~ 是正则匹配;count_over_time 是范围聚合,配合 sum by (host) 就能画出一张”各节点错误数”的时序图。有了时序图,日志就不再是只能翻的一堆文本,而是能当指标来看趋势了。
几个避坑要点
- 标签少而精:只放
host、job、app这类低基数维度,request_id、trace_id请用 structured metadata,别塞进标签。 - 查询先收窄:正文不建索引,直接全文扫关键词会慢,先加标签缩小时间范围和日志源,再上关键词。
- 多节点部署:每台机器各装一个 Promtail,标签里的
host改成对应主机名,日志统一推到一台 Loki 即可。 - 磁盘别裸奔:
retention_period一定要配,再顺手挂个监控看 Loki 的磁盘占用。 - Promtail 的接班人:官方现在主推 Grafana Alloy 作为统一采集器,Promtail 仍在维护、够用,等 Alloy 生态成熟了再迁移不迟。
小结
搭好这套之后,出问题时的排查路径就清爽了:先看 Grafana 大盘定位是哪台机器、哪个服务异常,再切到 Explore 用 LogQL 按 host + 关键词把相关日志捞出来,前后几分钟搞定。以前要 SSH 十几台机器翻日志的事,现在一个页面就办了。
如果你已经在用 Prometheus + Grafana,那 Loki 几乎是零学习成本的顺延——同一种查询思路、同一套可视化、同样的告警体系(Loki 也支持用 LogQL 写告警规则)。日志收进来之后,下一步就可以接 Alerting,比如”5 分钟内某服务 error 超过 100 条就通知我”,这个咱们下回接着聊。
