一、为啥你的分布式系统需要 Loki?
分布式系统一多,日志就变成了一个让人头疼的东西。每个服务都在疯狂往外吐日志,你要是用传统的日志收集方案,比如 Elasticsearch + Filebeat,很快就会发现磁盘满了、内存爆了、查询慢得像蜗牛。这时候,Loki 就登场了。
Loki 是 Grafana 团队开源的一个日志聚合系统,它的设计理念和 Prometheus 很像:只索引日志的标签,不索引日志内容。这听起来有点反直觉,但正是因为这个策略,Loki 才能在存储成本和查询速度上达到一个不错的平衡。简单说,Loki 不关心你日志里具体写了什么,只关心这条日志是哪个服务、哪个实例、哪个 Pod 发出来的。这样,你只需要为 tag 建索引,而不是为全文建索引,存储量能省下 80% 以上。
二、Loki 的架构一点也不神秘
2.1 整体组件
Loki 的架构可以拆成三个核心部分:
- Promtail:日志采集端,部署在每个节点上,负责从本地文件读取日志,加上标签后推送到 Loki。
- Loki:日志存储和查询服务,内部又分为 ingester(写入缓存)、querier(查询)、distributor(分发)等模块。
- Grafana:可视化前端,通过 Loki 的数据源查询并展示日志。
这三个东西配合工作,就像流水线:Promtail 负责把原材料(日志)运到工厂(Loki),Grafana 负责把成品展示给你看。
2.2 数据流示例
假设你有一个微服务叫 user-service,运行在 Kubernetes 上,Pod 标签是 app=user-service。当你配置 Promtail 去抓取这个 Pod 的日志时,它会自动给每条日志加上 {app="user-service"} 这个标签。然后这个标签会被 Loki 建立索引,而日志内容本身只会被压缩存储,不建索引。
查询的时候,你只要告诉 Loki {app="user-service"},它就能迅速定位到这个标签对应的所有日志块,然后从上往下扫一遍内容,找到你想要的。因为不用全文索引,所以写入速度极快,存储也很省。
三、从零开始搭建一个最简单的 Loki 系统
这里我们用 Docker Compose 来演示,技术栈就是 Loki 及其配套组件。
3.1 docker-compose.yml 配置
version: "3"
services:
loki:
image: grafana/loki:2.9.2
ports:
- "3100:3100"
volumes:
- ./loki-config.yaml:/etc/loki/config.yaml
- ./data/loki:/loki
command: -config.file=/etc/loki/config.yaml
promtail:
image: grafana/promtail:2.9.2
volumes:
- ./var/log:/var/log
- ./promtail-config.yaml:/etc/promtail/config.yaml
command: -config.file=/etc/promtail/config.yaml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
3.2 Loki 配置文件 loki-config.yaml
# Loki 服务本身配置
auth_enabled: false # 不启用认证,方便测试
server:
http_listen_port: 3100
ingester:
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 15m # 如果一个chunk 15分钟没更新,就刷到存储
chunk_target_size: 1536000 # 单个chunk目标大小1.5MB
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/index
shared_store: filesystem
cache_location: /loki/cache
filesystem:
directory: /loki/chunks
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
这个配置文件告诉 Loki:使用文件系统存储,索引和块分开存放,块大小控制在 1.5MB 左右,超过 15 分钟没新数据就刷到磁盘。
3.3 Promtail 配置文件 promtail-config.yaml
# Promtail 采集端配置
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml # 记录已读位置,防止重复采集
clients:
- url: http://loki:3100/loki/api/v1/push # 推送到 Loki
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
app: my-application # 给所有日志加一个标签
__path__: /var/log/*.log # 采集 /var/log 下所有 .log 文件
这个配置采集宿主机上的日志文件,打上 app=my-application 标签,然后推送给 Loki。
3.4 启动和验证
# 在 docker-compose.yml 所在目录执行
docker-compose up -d
等待几秒后,访问 http://localhost:3000 进入 Grafana,添加 Loki 数据源,地址填 http://loki:3100。然后你就可以用 LogQL 查询日志了,比如:
{app="my-application"}
这会返回所有带该标签的日志行。
四、实际应用场景:微服务日志排错
4.1 场景描述
假设你有一个电商系统,包含 order-service、payment-service、notification-service。某个用户下单失败了,报错信息是“支付超时”,但到底是订单服务调用支付服务超时,还是支付服务本身超时?传统做法是登录每台机器 tail -f /var/log/xxx 或者用 Kibana 全文搜“timeout”。但全文搜索慢,而且字段不统一。
Loki 的做法是:每个服务在日志中加上一个 trace_id 字段(比如 trace_id=abc123),然后在 Promtail 中不索引这个字段,但你可以用 LogQL 的管道过滤:
{app=~"order|payment|notification"} |= "trace_id=abc123"
这样就能把同一次请求三个服务的日志全部拉出来,按时间排序,一眼看出哪个环节慢。因为 Loki 只根据标签快速定位到三个服务的日志块,然后只扫描包含 trace_id=abc123 的行,速度非常快,即使日志量很大。
4.2 标签设计注意事项
标签是 Loki 的命根子,标签设计不好,查询效率会直线下降。
- 不要用高基数的标签:比如
user_id=123456,这种值会非常多,导致索引膨胀,性能极差。正确的做法是把高基数信息放在日志内容里,用 LogQL 的|=或|~过滤。 - 常用标签建议:
app(服务名)、env(环境)、instance(实例 ID)、job(任务名)。对于 Kubernetes 环境,Promtail 可以自动获取 Pod 标签,比如pod、namespace、container。
五、Loki 的技术优缺点
5.1 优点
- 存储成本极低:不索引日志内容,只索引标签,存储量是 ELK 的 1/5 甚至更低。
- 查询速度尚可:对于已知标签范围的查询,速度很快;对于模糊搜日志内容,需要扫描,但比 Elasticsearch 的全文索引要慢一点,不过对于大部分排错场景够用。
- 与 Prometheus 和 Grafana 无缝集成:同一个 Grafana 面板里可以同时看指标和日志,无需切换工具。
- 水平扩展容易:Loki 是分布式设计的,可以通过增加 ingester 或 querier 来扩容。
5.2 缺点
- 全文搜索能力弱:如果你需要像 Elasticsearch 那样做复杂的全文检索、聚合、Lucene 语法查询,Loki 做不到。它只能做简单的字符串包含、正则匹配。
- 不支持日志结构化字段索引:你不能像 ES 那样给某个 JSON 字段建立独立索引,只能用 LogQL 的
| json解析后做过滤,但性能不如提前索引。 - 社区生态相对较小:插件、文档、第三方集成没有 ES 那么丰富。
六、使用 Loki 时的注意事项
6.1 采样策略
如果你的服务日志量非常大(比如每秒上千条),可以考虑使用 Promtail 的 scraping 采样功能,只保留部分日志,减小存储压力。但要注意,采样可能会丢失关键信息,建议对错误日志(level=error)采用全量采集,对 debug 日志采样。
6.2 保留策略
Loki 的配置里可以设置 retention_period,比如保留 7 天。过度保留会占用大量磁盘,而且会影响查询速度。建议根据业务需求设置合理的保留期,比如近 7 天的日志用来排错,更早的归档到冷存储。
6.3 多租户隔离
如果你的平台有多个团队共用同一个 Loki 集群,可以用 org_id 实现租户隔离。每个租户只能看到自己的数据。Loki 支持 HTTP Header X-Scope-OrgID 来区分租户。
七、文章总结
Loki 在分布式系统中的应用,核心思路就是用标签代替全文索引,用低成本的存储换快速定位。它特别适合那些“我知道是哪个服务出问题了,只想看那个服务的日志”的场景。对于那些需要复杂全文搜索、字段聚合、即时分析的场景,Loki 不是最佳选择,你可能还需要搭配 Elasticsearch 来互补。但是,对于 90% 的日志排错和监控需求,Loki 加 Grafana 的组合已经足够强大,而且运维成本远低于 ELK。
在实际落地时,一定要花心思设计标签,控制基数,同时善用 LogQL 的管道操作来过滤日志内容。如果你正在从 EFK/ELK 迁移,可以先从非核心业务开始试水,逐步扩大范围。记住,没有银弹,选对工具才能事半功倍。
Comments