loki01

1、Loki架构

  • Loki的逻辑分组,read path、write path、backend

1.1、read path(读取路径)

  • 包括 Query Frontend、Querier组件

1、Query Frontend(查询前端)

  • 作为 Read 路径的 “入口”,负责接收用户的查询请求(如 LogQL 语句),并进行前置处理,包括
    • 解析和校验查询语法,过滤无效请求;
    • 对复杂查询进行拆分(例如按时间范围分片),生成可并行执行的子查询;
    • 将子查询分发到多个 Querier 实例,实现负载均衡和并行处理。
    • 对查询结果缓存(缓存热门结果,减少重复计算);

2、Querier(查询器)

  • 负责实际执行查询逻辑,接收 Query Frontend 分发的子查询,从两个数据源获取数据,分别是近期数据、历史数据
    • 近期数据:直接从 Ingester 的内存中读取(未持久化到 Backend 的新日志);
    • 历史数据:从 Backend(如对象存储)读取已持久化的日志块
  • 对获取的日志数据执行过滤、匹配等 LogQL 逻辑,处理后将结果返回给 Query Frontend;
  • 支持水平扩展(增加实例数)以提升查询吞吐量。

3、Read 路径的完整流程

  • 用户查询(如 Grafana 发起)→ Query Frontend(接收→校验→拆分→分发)→ 多个 Querier(并行执行子查询)→ 从 Ingester(近期数据)和 Backend(历史数据)读取数据 → Querier 处理结果 → 返回给 Query Frontend(聚合结果→缓存→返回用户)。

1.2、 write path(写入路径)

  • 包括 Distributor、Ingester组件

1、 Distributor(分配者)

  • Distributor 是写入流程的第一个组件, Promtail采集日志后,会将日志发送到 Distributor。Distributor 的核心作用是接收请求并完成初步处理,包括验证、分片、路由
    • 验证日志的合法性(如租户信息、格式);
    • 根据日志的标签集(labels)计算哈希值,与ingester数量取模,模相同则日志被路由到相同的 Ingester 实例
    • 若 Ingester 集群有节点故障,Distributor 会自动将日志路由到健康节点,保证写入可用性。

2、 Ingester(摄入器)

  • Ingester接收 Distributor 转发的日志后,承担数据暂存、压缩和持久化触发的关键职责:
    • 将 日志流 暂存于内存(形成 “活跃块”),并实时进行压缩(减少存储成本)
    • 当内存中的数据达到阈值(如时间窗口到期、块大小达标)时,Ingester 会将 “活跃块” 转换为 “只读块”,并异步写入 Backend(如 S3、GCS 等持久化存储);。

3、 总结:Write 链路的完整流程

  • 客户端 → Distributor(接收与路由) → Ingester(暂存、压缩、写入 Backend) → Backend(最终持久化)。

1.3、backend

1、Index Gateway(索引网关)

  • 作用:避免 Query 组件直接访问后端索引存储(减少对存储的压力)。统一管理索引访问,提升查询效率。
  • Query Frontend 组件查询 Index Gateway 获取查询的日志量,以便决定如何对查询进行分片。
  • Querier 组件查询 Index Gateway 以便知道要获取和查询哪些块

2、Compactor(压缩器)

  • Compactor 会定期从对象存储下载文件,将它们合并为一个文件,上传新创建的索引,并清理旧文件。
  • Compactor 还负责日志保留和日志删除,并指定相应的策略

3、Query Scheduler(查询调度器)

  • Query Frontend 主要负责查询的解析、拆分(将大查询拆分为可并行的子查询)和结果聚合,而 Query Scheduler 则专注于子查询任务的队列管理和分发。当启用 Query Scheduler 后,Frontend 不再直接将子查询分配给 Querier,而是将任务推给 Scheduler 的内存队列,由 Scheduler 统一调度。
  • 每个租户独立队列的设计,避免了 “大租户查询挤占小租户资源” 的问题。例如,一个产生大量日志的租户发起复杂查询时,不会占用所有 Querier 进程,其他租户的查询仍能按优先级或顺序得到处理,确保多租户环境下的资源公平性。
  • 没有 Scheduler 时,Querier 通常被动接收 Frontend 的任务;启用 Scheduler 后,Querier 作为 “工作节点” 主动从 Scheduler 拉取任务
  • 适合大规模、多租户、查询量高的场景,能显著提升查询稳定性

4、Ruler(规则器)

  • Ruler 负责执行预定义的 LogQL 告警规则(基于日志内容生成告警)。
  • 它会定期查询 Loki 中的日志数据(包括 近期数据 和 历史数据),当满足告警条件时触发通知。

2、loki采集日志的流程

  • Loki 采集日志并展示的工作流程可分为 “日志采集→处理转发→存储→查询展示” 四个核心环节,配合 Grafana 实现完整的日志可视化链路。以下是详细流程

2.1 日志采集(由 Promtail 或 Grafana Alloy 完成)

  • 发现日志:通过配置 promtail-config.yaml 文件,scrape_configs 字段下的 _ _ path _ _来指定日志采集路径
  • 添加标签:为日志添加元数据标签,用于后续快速筛选
  • 处理日志:对原始日志进行简单清洗(如过滤空行、截断过长日志),并按 “时间戳 + 日志内容” 格式封装。

2.2、 日志转发(发送到 Loki)

  • 采集工具(Promtail/Alloy)将处理后的日志发送到 Loki 的 Distributor 组件(Loki 入口)。默认使用 HTTP/JSON 协议,支持压缩传输以减少网络带宽消耗。

2.3、 日志存储(Loki 内部处理)

  • Loki 接收日志后,Distributor 验证日志格式和标签合法性,按 “一致性哈希” 将日志分片转发到多个 Ingester 节点。
  • Ingester 将日志按 “时间窗口” 和 “标签” 聚合为 “块(Chunk)”(默认每 2 小时一个块)。并对块进行压缩(使用 ZSTD 算法,压缩率通常达 10:1 以上)。先写入本地 WAL(Write-Ahead Log)确保不丢失,再异步刷写到后端存储。
  • 存储的内容有元数据和日志块,元数据:存储标签索引、块位置信息(依赖 Etcd/Consul 等 KV 存储)。日志块:存储压缩后的原始日志(依赖对象存储如 S3/MinIO/ 本地文件)。
  • Compactor:后台定期合并小日志块、删除过期日志(按配置的 retention_period),优化存储效率。

2.4、 日志查询与展示(Grafana + Loki)

  • 用户在 Grafana 中选择 Loki 数据源,编写 LogQL 查询语句来获取日志(先选标签,再输入要查找的内容)
  • Grafana 将查询请求发送到 Loki 的 Querier 组件。
  • Querier 根据标签索引定位相关日志块,从后端存储加载并解压。对日志内容执行过滤、排序等操作,返回结果给 Grafana。

promitail-config.yaml 示例

server:
  http_listen_port: 9080  # Promtail 自身 HTTP 监控端口
  grpc_listen_port: 0     # 关闭 gRPC 端口(默认不启用)

positions:
  filename: /var/lib/promtail/positions.yaml  # 记录日志读取位置(避免重启后重复采集)
  sync_period: 10s  # 位置信息同步到文件的间隔

clients:
  - url: http://loki-distributor:3100/loki/api/v1/push  # 发送日志到 Loki 的 Distributor 地址
    batchsize: 102400  # 单批日志大小上限(字节),默认 100KB
    batchwait: 1s      # 批处理等待时间,超时后即使未达 batchsize 也发送
    timeout: 10s       # 发送请求超时时间
    backoff_config:    # 失败重试策略
      min_period: 1s   # 初始重试间隔
      max_period: 5s   # 最大重试间隔
      max_retries: 10  # 最大重试次数

scrape_configs:
  # 采集系统日志(如 /var/log/messages)
  - job_name: system-logs
    static_configs:
      - targets:
          - localhost  # 目标节点(本地)
        labels:
          job: system  # 固定标签:任务名
          env: prod    # 固定标签:环境
          __path__: /var/log/*.log  # 日志文件路径(支持通配符)

  # 采集 K8s Pod 日志(需部署在 K8s 节点)
  - job_name: kubernetes-pods
    kubernetes_sd_configs:  # 自动发现 K8s 资源
      - role: pod  # 发现 Pod 资源
    relabel_configs:  # 动态标签处理(从 Pod 元数据提取标签)
      # 保留 Pod 名称作为标签
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: pod
      # 保留 Namespace 作为标签
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: namespace
      # 保留容器名作为标签
      - source_labels: [__meta_kubernetes_pod_container_name]
        action: replace
        target_label: container
      # 构建日志路径(K8s 容器日志默认路径)
      - replacement: /var/log/pods/*$1/*.log
        separator: /
        source_labels: [__meta_kubernetes_pod_uid, __meta_kubernetes_pod_container_name]
        regex: ^(.+)$
        target_label: __path__

k8s中自动暴露的元数据标签

  • 标签前缀为 _ _ meta_kubernetes _ _
  • k8s容器日志的标准化存储路径:/var/log/pods/< namespace >< pod-name >< pod-uid >/< container-name >/< 序号 >.log
# Pod 相关
__meta_kubernetes_pod_name        # Pod 名称
__meta_kubernetes_pod_namespace   # Pod 所在命名空间
__meta_kubernetes_pod_uid         # Pod 唯一 ID
__meta_kubernetes_pod_ip          # Pod IP 地址
__meta_kubernetes_pod_label_<key>          # Pod 自定义标签的键值(如 Pod 有 app=nginx,则自动暴露 __meta_kubernetes_pod_label_app=nginx)
__meta_kubernetes_pod_annotation_<key>     # Pod 注解的键值(如注解 promtail.io/scrape=true,则暴露 __meta_kubernetes_pod_annotation_promtail_io_scrape=true)
# 工作负载相关(Deployment/StatefulSet 等)
__meta_kubernetes_pod_owner_kind     # Pod 所属控制器类型
__meta_kubernetes_pod_owner_name     # Pod 所属控制器名称
# Service/Endpoint 相关
__meta_kubernetes_service_name       # Service 名称
__meta_kubernetes_service_namespace  # Service 所在命名空间
__meta_kubernetes_endpoint_port_name # Endpoint 的端口名称
__meta_kubernetes_endpoint_port_protocol   # Endpoint 端口协议(TCP/UDP)
# 节点相关
__meta_kubernetes_node_name          # 节点名称
__meta_kubernetes_node_label_<key>   # 节点自定义标签的键值(如节点标签 env=prod,则暴露 __meta_kubernetes_node_label_env=prod)

loki配置文件示例

全局设置

# 全局开关
auth_enabled: false  # 是否启用认证(影响所有组件的访问控制)

server:
  http_listen_port: 3100  # HTTP 监听端口
  grpc_listen_port: 9095  # GRPC 监听端口
  log_level: info  # 服务日志级别
  http_server_read_timeout: 5m  # HTTP 读超时
  http_server_write_timeout: 5m  # HTTP 写超时

# 全局限制配置(所有组件共享的资源和行为限制)
limits_config:
  retention_period: 72h  # 日志全局保留时间
  ingestion_rate_mb: 10  # 单租户每秒最大摄入速率
  ingestion_burst_size_mb: 20  # 摄入突发上限
  max_query_length: 72h  # 最大查询时间范围
  max_entries_limit_per_query: 5000  # 单查询返回条目上限
  query_timeout: 2m  # 查询超时时间
  max_label_name_length: 1024  # 标签名最大长度
  max_label_value_length: 2048  # 标签值最大长度

# 存储 schema 配置(定义日志存储格式和周期,全局生效)
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
    cache_location: /loki/cache
  filesystem:
    directory: /loki/chunks

distributor 组件(日志分发)

distributor:
  ring:  # 分布式环配置(用于分片和路由)
    kvstore:
      store: inmemory  # 环存储方式(inmemory/consul/etcd)
    replication_factor: 3  # 日志副本数
  remote_timeout: 5s  # 与 ingester 通信超时

ingester 组件(日志摄入与暂存)

ingester:
  lifecycler:  # 生命周期管理(与分布式环交互)
    ring:
      kvstore:
        store: inmemory
      replication_factor: 3
    final_sleep: 0s  # 关闭前最终等待时间
  chunk_idle_period: 5m  # 日志块空闲超时后写入存储
  max_chunk_age: 1h  # 日志块最大存活时间
  chunk_target_size: 1.5MB  # 日志块目标大小
  wal:  # 预写日志配置(防止数据丢失)
    directory: /loki/wal
    enabled: true

querier 组件(日志查询)

querier:
  query_ingesters_within: 12h  # 查询 ingester 中未持久化数据的时间范围
  engine:
    timeout: 1m  # 查询引擎超时
  max_concurrent: 20  # 最大并发查询数

query_range 组件(范围查询优化)

query_range:
  split_queries_by_interval: 15m  # 按时间间隔拆分查询
  cache_results: true  # 是否缓存查询结果
  results_cache:
    cache:
      type: inmemory  # 结果缓存类型(inmemory/redis)
      max_freshness: 10s  # 缓存最大新鲜度

ruler 组件(告警规则)

ruler:
  storage:  # 告警规则存储
    type: local
    local:
      directory: /loki/rules
  rule_path: /tmp/loki/rules-temp  # 规则临时目录
  alertmanager_url: http://alertmanager:9093  # Alertmanager 地址
  evaluation_interval: 1m  # 规则评估间隔

compactor 组件(日志块压缩)

compactor:
  working_directory: /loki/compactor  # 工作目录
  retention_enabled: true  # 是否启用基于保留期的清理
  compaction_interval: 10m  # 压缩间隔
  max_compaction_objects: 1000000  # 单次压缩最大对象数

table_manager 组件(索引表管理,旧版本用)

table_manager:
  retention_deletes_enabled: true  # 启用索引删除
  retention_period: 72h  # 索引保留时间(与 limits_config 配合)

memberlist

  • 用于 分布式集群成员管理 的核心配置,主要用于组件(如 Ingester、Distributor 等)之间的节点发现、状态同步和集群健康检查
memberlist:
  join_members: ["read", "write", "backend"]  # 初始加入的集群成员地址列表(可填域名或IP:端口)
  dead_node_reclaim_time: 30s  # 标记为"死亡"的节点被彻底移除的时间(回收资源)
  gossip_to_dead_nodes_time: 15s  # 对已标记为"死亡"的节点继续发送gossip消息的时间(确保状态同步)
  left_ingesters_timeout: 30s  # Ingester节点主动离开后,保留其状态的超时时间(避免数据丢失)
  bind_addr: ['0.0.0.0']  # 本地绑定的IP地址(通常为0.0.0.0表示监听所有网卡)
  bind_port: 7946  # 用于memberlist通信的端口(gossip协议端口)
  gossip_interval: 2s  # 节点间发送gossip消息的间隔(控制同步频率,影响集群一致性和网络开销)

common

  • 用于定义全局共享参数的配置块,主要作用是将多个组件(如 Ingester、Distributor、Compactor 等)共用的基础配置集中管理,避免重复定义
  • 通常出现在分布式部署场景中
common:
  path_prefix: /loki  # 所有组件的基础路径前缀(用于存储数据、缓存等)
  replication_factor: 1  # 全局默认的副本数(日志数据或元数据的冗余副本数量)
  compactor_address: http://backend:3100  # Compactor 组件的访问地址(供其他组件调用)
  storage:  # 全局存储后端配置(所有组件共享的存储参数)
    s3:  # 使用 S3 兼容存储(如 MinIO)作为对象存储
      endpoint: minio:9000  # S3 服务地址(这里是 MinIO 地址)
      insecure: true  # 是否允许非 HTTPS 连接(开发环境常用)
      bucketnames: loki-data  # 存储日志数据的桶名称
      access_key_id: loki  # 访问密钥 ID
      secret_access_key: supersecret  # 访问密钥
      s3forcepathstyle: true  # 强制使用路径风格访问(兼容 MinIO 等服务)
  ring:  # 全局分布式环配置(集群成员管理的基础参数)
    kvstore:
      store: memberlist  # 分布式环的存储方式(这里使用 memberlist 协议)

Loki生产环境/大规模场景部署方式建议

  • distributor 和 ingester 通常需要多实例部署(通过负载均衡器Service 或 LoadBalancer 暴露入口),实现负载均衡。
  • compactor 可以单实例部署(但建议主从备份,避免因 compactor 故障导致日志块无法合并),通常与其他组件分开部署(因其计算和 I/O 负载较低)。
  • 所有组件可部署在独立的机器或容器(如 Kubernetes Pod)中,通过网络通信(需配置相同的 ring 信息,确保组件间能发现彼此)。

Loki的查询语言:LogQL

  • LogQL 是 Loki 专用的日志查询语言,语法类似 PromQL,核心能力是,通过标签过滤日志 + 对日志内容进行模式匹配

1. 日志流查询

  • 通过标签筛选出目标日志流,是所有查询的基础,语法与 Prometheus 指标选择器完全一致:
# 筛选出 "prod" 环境下 "backend" 应用的日志
{env="prod", app="backend"}

2. 日志内容过滤

  • 在日志流的基础上,对日志原文进行模糊匹配或正则匹配,支持以下运算符:
  • |=:包含指定字符串(如 |= "error" 筛选含"error"的日志)
  • !=:不包含指定字符串(如 != "debug" 排除含"debug"的日志)
  • !~:不匹配正则表达式
# 筛选 prod 环境 backend 应用的 ERROR 日志
{env="prod", app="backend"} |= "ERROR"

# 筛选 prod 环境 backend 应用中,含 "timeout" 且不含 "test" 的日志
{env="prod", app="backend"} |= "timeout" != "test"

3. 聚合与统计

  • 支持对日志流进行聚合计算(如统计日志条数、按标签分组),常用函数:
  • count_over_time():统计指定时间窗口内的日志条数
  • sum(count_over_time()) by (pod):按 pod 标签分组,统计每个 Pod 的日志总条数
  • topk(5, count_over_time()):取日志量前 5 的标签组
# 统计 5 分钟内 backend 应用的日志量
count_over_time({app="backend"}[5m])

# 统计 prod 环境下,每个 backend Pod 过去 10 分钟的 ERROR 日志数,并取前 3
topk(3, sum(count_over_time({env="prod", app="backend"} |= "ERROR" [10m])) by (pod))
posted @ 2026-03-27 11:23  立勋  阅读(26)  评论(0)    收藏  举报