Loki 使用介绍

发布时间: 更新时间: 总字数:2961 阅读时间:6m 作者: IP上海 分享 网址

Loki 是一个可水平扩展、高可用性、多租户的开源日志聚合系统,其设计灵感来自 Prometheus,由 Grafana Labs 开发,支持与 Grafana 生态组件紧密集成。

介绍

PS:2024 过年期间整理,后参考马哥教育的Loki专题再次整理

  • Loki Like Prometheus, but for logs.
  • 灵感来自 Prometheus 指标系统
    • 以时序数据格式存储日志流,每个日志流由一组时序 labels(标签) 进行标识
    • 提供类似与 PromQL 的专用日志检索语言 LogQL, Loki’s query language
  • 与其他日志系统不同,Loki 的设计理念是:
    • 只索引日志的元数据:labels(标签)(就像 Prometheus 标签一样)
      • 标签通常是主机名、容器名、日志文件名、程序名、日志级别、HTTP 请求方法、HTTP 响应码
    • 日志流(log stream):是由同一组时序标签标识的日志信息,Loki 的日志数据存储为:
      • index(索引)
      • 对日志数据本身进行压缩,并以 chunks(块) 为单位存储
  • 特点
    • 可扩展性,微服务化设计,各个组件可独立进行水平扩展
    • 多租户,能有效进行日志数据隔离和实施权限控制
    • 可插拔、高效存储:支持多种存储后端
      • AWS S3 或谷歌云存储(GCS)等对象存储中,甚至存储在本地文件系统中
      • 支持根据需求分别为索引和日志选择不同的存储方案
    • Prometheus Alertmanager
    • Grafana integration,Grafana v6.0 中开始支持 Loki
    • 第三方集成
    • Loki 不对日志进行全文索引。通过存储压缩的非结构化日志并只对元数据进行索引,Loki 的操作更简单,运行成本更低
    • Loki 使用与 Prometheus 相同的标签对日志流进行索引和分组,使您能够使用与 Prometheus 相同的标签在指标和日志之间无缝切换
    • Kubernetes Pod 是存储 Kubernetes Pod 日志的最佳选择。Pod 标签等元数据会被自动抓取并编入索引。

与指标数据对比

  • 日志内容更丰富
  • 体量更大
  • debug 日志信息可以详细显示什么时候发生了什么事件

与其他日志系统对比

Loki ElasticSearch
存储 空间需求量小
索引 尽可能少的索引日志信息 索引整行日志
查询语言 LogQL Kibana 查询语言(KQL),或 Lucene 查询语言
日志收集器 Promtail Logstash/Beats
用户接口 Grafan Kibana
成本 开销较低(日志数据压缩存储在S3等对象存储中)、但索引(仅索引标签元数据)能力较差 开销大、提供全文索引

PS:对大规模系统应用场景,ES通常是更好的选择,但另一种兼具二者优势的是使用 ClickHouse 存储的 SigNoz

简单架构

loki arch
  • 参考
  • Loki-based logging stack(PLG(Promtail/Loki/Grafana) Stack) 组成:
    • 客户端,Loki 支持很多客户端,例如与 Loki 一起发布的 Promtail、Grafana Agent,Docker Driver、Fluentd/Fluent-bit、Logstash 等
      • Promtail 负责收集日志并将其发送(Push)给 Loki Server 的 agent,支持发现采集目标(服务发现机制)及添加对应Label、格式转换和过滤功能
      • Promtail 是专为 Loki 设计的日志搜集器,请求 HTTP API 以 Push 的方式上传到 Loki Distributor
    • Loki Server 主服务器,负责接收和存储日志以及处理查询
      • 不索引日志全文,仅完成标签索引,从而显著降低系统开销及接近实时的查询性能
      • LogQL 是 Loki 提供的日志查询语言
        • Grafana 可对查询结果进行 WEB 可视化
        • HTTP API
          • one-shot query
          • live streaming
        • 专用的命令客户端:logcli
    • Grafana 用于查询和显示日志数据
    • AlertManager 用户可编写告警规则,并进行周期性评估,超出阈值,由 AlertManager 通知给用户

核心组件

loki arch
  • 参考
  • Loki 的系统组件大体可以分为两类
    • Read Path 处理读取日志样本请求的组件(参考
      • Querier 基于 HTTP 接收到查询请求
      • 转发请求到 Ingester 来检查缓存在内存中的日志数据
      • Ingester 查询到了相关数据,则返回给 Querier
      • Ingester 为查询到相关数据,则 Querier 会向存储系统发起请求
      • Querier 基于 HTTP 协议响应查询请求
    • Write Path 处理存储日志样本请求的组件(参考
      • Distributor 基于 HTTP 协议接收到写请求,并将其附加到特定的 stream
      • Distributor 将每个 stream 发往 Ingester
      • Ingester 创建一个新 Chunk 或附加到现有 Chunk
      • Distributor 将结果响应给客户端
  • 核心组件
    • Distributor(write path) 收到的日志流基于标签进行 consistent hashing 计算后,分发至不同的 Ingester,复制因子用于指定要存储的副本数以实现容错
      • 从 Loki 客户端(如 Promtail)接收数据、检查数据,随后将其分成 数据库(chunks) 并发往 Ingester
      • 无状态应用,可接受 LB 进行数据分发
      • shard 数据分片
        • Distributor 收到日志流基于标签进行 consistent hashing 计算后,分发至不同的 Ingester,以实现数据分片,从而将写操作负载均衡至多个 Ingester 节点
    • Ingester(write, read path)
      • 负责将数据存入外部存储系统(如s3)以长期存储数据,以及接受客户端的查询请求并响应相关的数据
      • 为避免数据丢失,通常应该提供复制式多实例
    • Compactor 将那些由多个 Ingester 生成的多个索引,以天为单位基于 tenant 合并成单个索引文件,实现高效索引查询
    • Querier(read path)
      • 处理 LogQL 请求,并从 Ingester 或长期存储系统中加载数据
    • Querier Scheduler 调度查询请求,为 Querier Frontend 提供更为高级的队列机制,可选组件
    • Querier Frontend(read path)
      • 处理查询请求的前端组件,如 Grafana
    • Ruler
      • 告警组件,基于告警规则评估日志数据,并在必要时发送告警通知给 AlertManager
    • Object Store

loki architecture

loki arch

数据格式

chunks_diagram
  • 参考
  • Loki 日志服务器的时序数据遵循类似 Prometheus 指标的定义规范,所有日志数据都按照时序类型存储
    • 时序数据:是在一段时间内通过 重复测量(measurement) 获得的观测值的集合
  • Loki 中的日志存储由两部分组成:
    • 索引数据(index) 时序标识
    • 日志数据(Chunk) 数据样本,时序标识的数据组成的时间线,loki 称为 stream
      • 每天时间线都有一个唯一的时序标识
      • labels 则由一至多组键值对组成

Index format

Loki 会对 chunks 基于 标签集时间范围 进行索引以加快检索操作

  • Loki 仅索引日志流的元数据(标签集),不会索引日志内容
  • 索引中主要存储标签集日志流标识符(stream indentifiers) 和时间范围(内部日志流的时间边界)
  • 索引信息通常存储与 key-value 存储系统中,已提供较快的读取和查询性能,支持的存储包括:
    • TSDB (recommended)
    • BoltDB (deprecated)
  • Loki 对标签命名的限制与 Prometheus 相同
    • 支持使用字母、数字、下划线和冒号,且能够匹配 RE2 规范的正则表达式
    • 标签不支持使用的字符,需要转化为下划线,例如:标签 app.kubernetes.io/name 应写为 app_kubernetes_io_name
    • 注意:
      • Loki 有内置动态创建一些标签
      • 标签不建议过多,否则导致 Loki 构建一个巨大的索引
  • 与 Prometheus 不同,Loki 仅使用标签集(label set)作为时序标识,没有 指标名称

Chunk format

  • Loki 会将每个日志流中的日志信息存储为 chunk
    • 每个 chunk 存储由特定的时间窗口的日志条目,这些日志条目会被压缩后再进行存储
    • 每个 chunk 的所有日志条目会按照 timestamp 进行排序
  • chunk 中的内容包括:
    • 压缩的日志条目(compressed log entries)
    • 时序信息(time series information)
    • 块标识符(chunk identifiers)
  • chunks 由 Ingester 缓存在内存中,并周期性刷写至磁盘上
    • 触发刷写操作的条件:达到单个 chunk 的存储上限,或达到窗口时长
  • Loki 通常将 chunk 存储在对象存储中,支持的存储包括:
    • aws s3 或兼容 s3 协议的存储系统,如 minio、openstack swift、aliyun oss 等
    • dynamoDB
    • GCS
    • BigTable
    • Filesystem(single node)
  • 支持存储纯文本信息,也支持存储 Json 或 logfmt 格式
  • 基于 Log streams(共享同一组标签的日志信息) 存储日志数据
    • 同一 stream 中的数据将会存储在一起,以降低 标签匹配(label matching) 可能涉及到的 stream 数量
    • 日志信息的匹配将采用类似 grep 式的粗暴方式
  • 注意:应尽量小心地避免时间线膨胀的问题
    • 时序数据的典型难题之一 cardinality explosion(基数爆炸),即标签上的不同可用值过多
----------------------------------------------------------------------------
|                        |                       |                         |
|     MagicNumber(4b)    |     version(1b)       |      encoding (1b)      |
|                        |                       |                         |
----------------------------------------------------------------------------
|                      #structuredMetadata (uvarint)                       |
----------------------------------------------------------------------------
|      len(label-1) (uvarint)      |          label-1 (bytes)              |
----------------------------------------------------------------------------
|      len(label-2) (uvarint)      |          label-2 (bytes)              |
----------------------------------------------------------------------------
|      len(label-n) (uvarint)      |          label-n (bytes)              |
----------------------------------------------------------------------------
|                      checksum(from #structuredMetadata)                  |
----------------------------------------------------------------------------
|           block-1 bytes          |           checksum (4b)               |
----------------------------------------------------------------------------
|           block-2 bytes          |           checksum (4b)               |
----------------------------------------------------------------------------
|           block-n bytes          |           checksum (4b)               |
----------------------------------------------------------------------------
|                           #blocks (uvarint)                              |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
| #entries(uvarint) | mint, maxt (varint)  | offset, len (uvarint)         |
----------------------------------------------------------------------------
|                          checksum(from #blocks)                          |
----------------------------------------------------------------------------
| #structuredMetadata len (uvarint) | #structuredMetadata offset (uvarint) |
----------------------------------------------------------------------------
|     #blocks len (uvarint)         |       #blocks offset (uvarint)       |
----------------------------------------------------------------------------

Block format

-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-1 bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-2 bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-3 bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------
|  ts (varint)  |  len (uvarint)  |  log-n bytes  |  len(from #symbols)  |  #symbols (uvarint)  |  symbol-1 (uvarint)  | symbol-n*2 (uvarint) |
-----------------------------------------------------------------------------------------------------------------------------------------------

参考

  1. https://github.com/grafana/loki
  2. https://github.com/iKubernetes/learning-k8s/tree/master/Loki-Stack
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数