当服务从一台机器扩展到十几台容器,再想靠 ssh 登录挨个翻 /var/log 已经不现实。日志收集的本质,是把散落在各处的文本流,汇聚成一条可被统一检索的时间线。本文对比 Loki 与 ELK 两条主流路线,并给出一套用 Docker Compose 十分钟搭起的轻量落地方案,帮你补齐可观测性里”日志”这一环。
一、为什么单台服务器的日志不够用了
日志的价值在于”出问题能回溯、平时能度量”。当系统还是单体时,一台机器一个文件尚可应付;一旦进入容器化或微服务,日志面临三个结构性难题:位置分散(Pod 随时漂移到任意节点)、生命周期短暂(容器删除即消失)、格式混乱(Nginx、应用、K8s 各有各的写法)。不解决收集,事故复盘就只能靠猜。
更关键的是日志与监控的协同。我们在Prometheus + Grafana 监控面板实战中解决了”指标”问题,但指标只能告诉你”QPS 掉了”,日志才能告诉你”因为哪个异常栈”。指标加日志,才是完整的可观测性底座。
二、Loki 与 ELK:两条主流路线的取舍
市面上的日志方案大致归为两派:以 Elasticsearch 为核心的 ELK(Elasticsearch + Logstash + Kibana),和以 Grafana Loki 为代表的”轻量标签派”。它们的根本差异在存储模型——ES 对全文字段建倒排索引,强大但吃资源;Loki 只对标签建索引,把日志原文压缩进对象存储,查询时按标签定位再全文扫描。
| 维度 | Loki | ELK(Elasticsearch) |
|---|---|---|
| 索引方式 | 仅标签索引,原文压缩存储 | 全文倒排索引 |
| 资源占用 | 低,适合中小团队 | 高,堆内存消耗大 |
| 查询语言 | LogQL,类 PromQL | KQL / Lucene 语法 |
| 生态协同 | 与 Prometheus 天然一体 | Kibana 独立可视化 |
| 适用场景 | 容器日志、运维排障 | 全文检索、合规审计 |
一句话结论:如果你的诉求是”按服务/环境快速翻日志、和 Prometheus 共用一套面板”,Loki 性价比最高;如果需要对任意字段做复杂全文检索、做长期合规留存,ELK 更稳。下文以 Loki 为主讲实操。
三、Loki 实战:用 Docker Compose 十分钟搭起
Loki 的最小可用组合是三个组件:Loki(存储与查询入口)、Promtail(采集端,装在每台机器)、Grafana(可视化)。下面这份编排文件即可在本机或单节点服务器跑起来。
version: "3.8"
services:
loki:
image: grafana/loki:2.9.2
command: -config.file=/etc/loki/local-config.yaml
ports:
- "3100:3100"
volumes:
- loki_data:/loki
promtail:
image: grafana/promtail:2.9.2
volumes:
- /var/log:/var/log:ro
- ./promtail-config.yaml:/etc/promtail/config.yml:ro
command: -config.file=/etc/promtail/config.yml
depends_on:
- loki
grafana:
image: grafana/grafana:10.2.2
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=change_me
volumes:
- grafana_data:/var/lib/grafana
depends_on:
- loki
volumes:
loki_data:
grafana_data:
启动后,在 Grafana 里添加 Loki 数据源(地址填 http://loki:3100),即可在 Explore 页写查询。如果是 K8s 环境,Promtail 用 DaemonSet 部署即可,每台节点自动采集,相关容器编排基础可参考Kubernetes 入门:Pod、Deployment 与 Service。
四、Promtail 采集配置:标签决定查询效率
Loki 的设计哲学是”标签即索引”。标签加得合理,查询飞快;标签加得随意(比如把时间戳或用户 ID 当标签),会触发”标签爆炸”把内存撑爆。下面的配置把 job、service、env 作为固定标签,再用 pipeline 从日志行里抽出结构化字段。
server:
http_listen_port: 9080
grpc_listen_port: 0
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: nginx
static_configs:
- targets: [localhost]
labels:
job: nginx
service: web-gateway
env: prod
__path__: /var/log/nginx/*.log
pipeline_stages:
- match:
selector: '{job="nginx"}'
stages:
- regex:
expression: '^(?P<ip>\S+) .* "(?P<method>[A-Z]+) (?P<path>\S+)" (?P<status>\d+)'
- labels:
status:
method:
- job_name: app
static_configs:
- targets: [localhost]
labels:
job: app
service: order-svc
env: prod
__path__: /opt/app/logs/*.log
注意 pipeline_stages 里的 labels 只会把匹配到的有限枚举(如 HTTP 状态 200/404/500)提升为标签——这些是低基数维度,安全。而像 ip 这种高基数字段,只抽成日志内的结构化字段供全文检索,绝不提升为标签。
五、LogQL:像写 PromQL 一样查日志
LogQL 的语法刻意贴近 PromQL,会写 Prometheus 查询的人几乎零成本上手。基本形态是”标签选择器 + 管道操作符”。下面几个例子覆盖了 80% 的排障场景。
# 1. 筛选某服务全部日志
{service="order-svc", env="prod"}
# 2. 文本包含(不区分大小写)与排除
{service="web-gateway"} |= "timeout"
{service="web-gateway"} != "healthcheck"
# 3. 把 JSON 日志解析成字段,再按字段过滤
{job="app"} | json | level="ERROR" | err_code="DB_504"
# 4. 统计最近 5 分钟 5xx 速率(和 PromQL 同构)
sum by (status) (rate({job="nginx"} | json | status=~"5.." [5m]))
# 5. 错误日志条数折线
count_over_time({service="order-svc", level="ERROR"}[1h])
把第 4 条查询做成 Grafana 面板,就能和 Prometheus 的 QPS、延迟曲线叠在一起看——指标异常时,旁边就是同一时间窗的错误日志,排障闭环就此打通。Nginx 访问日志本身的格式治理,可配合Nginx 限流防刷实战里的访问日志分析一起做。
六、ELK 路线:什么时候它更合适
当需求越过”排障翻日志”,进入”对任意业务字段做全文检索”或”安全合规审计留存数年”时,Elasticsearch 的全文索引优势就显现了。ELK 的采集端用 Filebeat,比 Promtail 更轻,且对各类中间件(MySQL、Redis、Kafka)有现成模块。
# filebeat.yml 关键片段
filebeat.inputs:
- type: filestream
id: nginx-access
paths:
- /var/log/nginx/access.log
fields:
service: web-gateway
env: prod
output.elasticsearch:
hosts: ["https://es.internal:9200"]
username: ${ES_USER}
password: ${ES_PWD}
indices:
- index: "nginx-log-%{+yyyy.MM.dd}"
setup.template.name: "nginx-log"
setup.template.pattern: "nginx-log-*"
Filebeat 用 filestream 输入自动记录读取位点,重启不丢不重;输出直接进 ES 并按天建索引,配合 ILM 生命周期策略滚动清理。如果你的技术栈里已经有 Kafka,还可以让 Filebeat 先发到 Kafka 做削峰,再经 Logstash 落库,这一层消息缓冲的思路在Kafka 入门实战里有更系统的说明。
七、生产避坑清单
日志系统上线后最容易踩的坑,往往不在搭建而在治理。下面几条是反复验证过的经验。
- 防标签爆炸:只把低基数的环境/服务/状态提升为标签,高基数字段(用户 ID、IP、URL)只做日志内字段,绝不当标签。
- 设保留期:Loki 配
compactor保留 30 天,ES 配 ILM 滚动删除,否则磁盘会被历史日志缓慢吃满。 - 采集端资源上限:Promtail / Filebeat 给足内存上限但别贪大,避免和宿主机业务抢资源。
- 日志分级:DEBUG 只开发环境开,生产默认 INFO 起,ERROR 单独建索引或告警,减少噪声。
- 权限与脱敏:日志里常带手机号、Token,采集管道要加脱敏 stage,访问 Grafana 走账号鉴权,安全审计可结合服务器安全加固里的账号与防火墙策略。
- 告警闭环:用 Grafana 或 ElastAlert 对 ERROR 速率设阈值告警,别让日志系统只用于事后翻查。
日志收集没有银弹,但选型有章法:中小团队、容器环境、要和 Prometheus 协同,Loki 是最省心的起点;全文检索与合规留存需求重,ELK 更稳妥。无论哪条路,标签治理、保留期、脱敏与告警这四件事做到位,日志才会从”出事后才看的文件”变成”平时就在帮你盯系统”的资产。把日志面板接进日常 oncall,排障时间通常能砍掉一大半。




