2. Loki 核心架构:Loki、Promtail、Grafana 组件关系与数据流

在生产环境中构建基于日志的可观测性体系,首先需要理解 Loki 生态中三个核心组件的职责划分与协作方式。Loki 并非一个单体应用,而是由 Promtail(日志采集器)、Loki(日志存储与查询引擎) 和 Grafana(可视化与告警面板) 组成的分布式系统。本节将拆解三者的角色边界、数据流转路径以及关键配置要点。

2.1 组件角色与职责矩阵

下表从数据采集、存储索引、查询展示三个维度对比三个组件的核心职责:

维度 Promtail Loki Grafana
主要职责 日志采集、标签化、推送 日志存储、索引、查询引擎 日志查询 UI、仪表盘、告警规则
部署位置 与被监控目标同节点(DaemonSet/Agent) 独立集群或单机部署 独立部署或与 Loki 同集群
数据协议 读取本地日志文件 / Journald / Syslog 接收 HTTP/gRPC 推送,响应 LogQL 查询 通过 Loki 数据源接口查询
标签管理 自动发现文件、添加静态/动态标签 基于标签建立倒排索引 在 Explore 中按标签过滤
关键配置 scrape_configs、positions.yaml ingester、querier、compactor 数据源 URL、日志查询面板
SRE 视角: Promtail 是“日志搬运工”,Loki 是“日志仓库”,Grafana 是“查询窗口”。三者缺一不可,但 Promtail 的标签设计直接影响 Loki 的查询性能和存储成本。

2.2 核心数据流:从日志行到可视化

一条 CI/CD 流水线日志从产生到呈现在 Grafana 面板上,需要经过以下 6 个关键步骤:

  1. 日志产生: Jenkins / GitLab Runner / 应用容器 输出日志到标准输出或文件。
  2. Promtail 采集: Promtail 通过 scrape_configs 监听日志文件或 Journald,读取新增行。
  3. 标签注入: Promtail 根据配置添加 job、pipeline_id、stage 等标签,并将日志行与标签打包成 Stream。
  4. HTTP/gRPC 推送: Promtail 将 Stream 推送到 Loki 的 distributor 组件。
  5. Loki 存储与索引: Distributor 验证数据后转发给 ingester,ingester 将日志写入对象存储(如 S3/MinIO),同时更新倒排索引。
  6. Grafana 查询: 用户在 Grafana Explore 中输入 LogQL 查询,Loki 的 querier 从存储和索引中检索日志并返回结果。
CI/CD 场景关键点: 在步骤 3 中,Promtail 必须为每条流水线日志注入 pipeline_id 和 stage 标签,否则后续无法按流水线维度聚合 SLI 指标。

2.3 组件关系架构图

下图展示了 Promtail、Loki 内部组件与 Grafana 之间的完整数据流关系:

Loki 核心架构:组件关系与数据流 Promtail 日志采集 + 标签注入 日志源 文件 / Stdout / Journald Loki 集群 Distributor 数据接收与验证 Ingester 内存缓存 + 写入存储 Querier 查询执行与聚合 对象存储 S3 / MinIO / GCS HTTP/gRPC 推送 写入 读取 Grafana 可视化 / 告警 / 仪表盘 LogQL 查询结果 查询请求 标签设计决定查询性能 高基数标签 → 索引膨胀
常见误区: 很多初学者认为 Promtail 直接写入对象存储,实际上 Promtail 只与 Distributor 通信,由 Ingester 负责写入存储。这种架构设计使得 Loki 可以在内存中缓冲最近日志,提供低延迟查询。

2.4 标签机制与数据分片

Loki 的索引完全基于标签(Label),这与 Prometheus 的指标标签模型一致。每个日志流(Stream)由一组唯一的标签组合标识。在 CI/CD 场景中,典型的标签设计如下:

# Promtail 配置片段:为流水线日志注入标签
scrape_configs:
  - job_name: ci-pipeline-logs
    static_configs:
      - targets: [localhost]
        labels:
          job: jenkins-pipeline
          __path__: /var/log/jenkins/pipeline-*.log
    pipeline_stages:
      - regex:
          expression: '^(?P<pipeline_id>\d+)\s+(?P<stage>\S+)'
      - labels:
          pipeline_id: ''
          stage: ''

标签设计需要遵循以下原则:

  • 低基数标签(如 job、namespace)用于索引,查询速度快。
  • 高基数标签(如 pipeline_id、trace_id)应谨慎使用,过多高基数标签会导致索引膨胀。
  • 动态标签(如 stage)通过 Promtail 的 pipeline_stages 从日志内容中提取。

2.5 数据流中的关键路径与瓶颈

在 CI/CD 流水线高并发场景下,数据流的每个环节都可能成为瓶颈:

环节 潜在瓶颈 SRE 优化建议
Promtail 采集 日志文件轮转过快导致漏采 增大 positions.yaml 同步频率,使用 tail 模式
Distributor 接收 瞬时高并发推送导致 OOM 启用速率限制(limits_config),水平扩展 Distributor
Ingester 写入 内存中日志积压未刷盘 调整 chunk_target_size 和 max_chunk_age
Querier 查询 跨多个 ingester 查询延迟高 启用 query_parallelism,使用缓存
实战建议: 在 CI/CD 流水线场景中,建议将 Promtail 以 DaemonSet 部署在每个构建节点上,并为每个流水线任务生成独立的日志文件,避免多个任务写入同一文件导致标签混乱。

2.6 组件间通信协议与端口

理解组件间的通信方式有助于排查网络连通性问题:

  • Promtail → Loki Distributor: HTTP/gRPC,默认端口 3100(HTTP)或 9095(gRPC)。
  • Loki 内部组件间: gRPC,端口 9095(Distributor ↔ Ingester ↔ Querier)。
  • Grafana → Loki Querier: HTTP,端口 3100,通过 Loki 数据源配置。
  • Ingester → 对象存储: HTTPS/S3 协议,端口 443 或自定义。