完全指南:Compactor 机制、分层保留策略与对象存储生命周期)
Grafana Loki 日志保留Log Retention完全指南Compactor 机制、分层保留策略与对象存储生命周期【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/lokiLoki 的日志保留Retention并非在写入时执行而是由Compactor组件在后台对索引文件进行压缩与标记、并异步清扫过期 chunk 来实现。本指南以 retention.md 为骨架完整讲解 Compactor 的两条独立调度、延迟删除与 marker 文件机制、limits_config下的全局/按流/按租户三层保留策略及优先级决策规则并给出 GCS、S3 等对象存储生命周期策略的配套配置与安全边界帮助你为生产环境设计一套可落地、可调优、不破坏索引一致性的日志保留方案。Compactor保留机制的实现者在 Loki 中日志保留是通过 Compactor 实现的。Compactor 承担两类职责索引文件压缩Compaction将分散的索引文件合并为每个租户每天一个索引文件日志保留Retention根据保留策略标记并删除过期 chunk。默认情况下compactor.retention-enabled未设置即false此时发送到 Loki 的日志永久保留Compactor 只会压缩索引表不会执行任何删除动作。注意如果在对象存储上配置了生命周期策略lifecycle policy请确保其过期时间长于保留周期否则会提前删除 Loki 仍需保留的对象损坏存储并导致查询失败详见下文「对象存储生命周期策略」。单例运行要求Compactor 必须作为单例单个实例运行。从源码角度看Compactor 通过 hash ring 选举出唯一实例执行压缩与保留任务见 pkg/compactor/config.go 中compactor_ring的说明“The hash ring configuration used by compactors to elect a single instance for running compactions”。在 Kubernetes 中建议以 StatefulSet 部署并挂载持久化存储以便保留 marker 文件。两条独立调度compaction 与 retentionCompactor 在两条相互独立的调度上运行压缩与保留压缩每compactor.compaction-interval执行一次保留每compactor.apply-retention-interval执行一次。如果apply-retention-interval保持默认值0sLoki 会将其设置为与compactor.compaction-interval相同的值。更精确地说这一推导逻辑写在 pkg/compactor/config.go 的Validate()中if cfg.RetentionEnabled { if cfg.DeleteRequestStore { return fmt.Errorf(compactor.delete-request-store should be configured when retention is enabled) } if cfg.ApplyRetentionInterval 0 { cfg.ApplyRetentionInterval cfg.CompactionInterval } if cfg.ApplyRetentionInterval cfg.CompactionInterval { // add some jitter to avoid running retention and compaction at same time cfg.ApplyRetentionInterval min(10*time.Minute, cfg.ApplyRetentionInterval/2) } ... }可以看出当两个间隔相等时Loki 会给保留间隔加上一个抖动jitter——取10m与该间隔一半两者中的较小值——以避免压缩与保留在同一时刻运行。例如默认compaction_interval为10m时保留实际每15m运行一次。此外还有两点重要性质追赶上进度如果 Compactor 在任一调度上落后它会在下一轮尽快执行对应操作幂等性压缩与保留都是幂等的——同一操作执行多次不会对日志产生额外影响。因此 Compactor 重启后会从上次中断的位置继续不会重复删除或重复压缩。注意保留周期的修改不具追溯性。变更保留周期只会作用于之后写入的日志不会对已摄取已入库的日志生效。保留算法的执行流程Compactor 的保留算法按天/按表推进索引周期为 24h 时一天对应一张表核心步骤与 pkg/compactor/compactor.go 注释所描述的流程一致对每天每张表做压缩将表中多个索引文件压缩为每个租户每天一个索引文件遍历每租户索引依据租户配置识别需要移除的 chunk移除引用并标记从索引中删除这些 chunk 的引用同时把 chunk 引用写入磁盘上的 marker 文件上传新索引将修改后的索引文件重新上传到对象存储。关键点在于执行保留算法时 chunk 并不会立即被删除。它们由独立的sweeper清扫进程异步删除延迟时间可通过-compactor.retention-delete-delay配置marker 文件用于追踪待删除的 chunk。延迟删除为什么 chunk 不能立刻删chunk 不能立即删除主要有两个原因Index Gateway 的索引刷新窗口Index Gateway 会下载索引文件的副本用于服务查询并按固定间隔刷新。延迟删除给了 Index Gateway 拉取新索引的时间——新索引不再包含已标记删除的 chunk 引用。若没有延迟Gateway 上陈旧的索引仍会引用已被删除的 chunk导致查询失败配置纠错窗口延迟提供了一个短暂的时间窗口在配置错误时有机会取消 chunk 的删除。Marker 文件的位置与持久化默认情况下marker 文件写入本地磁盘路径为working_directory/retention/object_store_type_period_from_date/markers/对应源码见 pkg/compactor/compactor.go保留工作目录拼接为working_directory/retention/{objectStoreType}_{periodFrom}/markers/常量MarkersFolder markers。marker 文件应存放在持久化磁盘上确保 Compactor 进程重启后待删除的 chunk 仍能被继续处理。因此 Grafana Labs 建议将 Compactor 部署为有状态服务Kubernetes 中即 StatefulSet并为 marker 文件配备持久化存储。把 marker 文件放到对象存储另一种方案是通过compactor.deletion-marker-object-store-prefixYAML 中为deletion_marker_object_store_prefix将 marker 文件存储到对象存储而非本地磁盘该前缀必须以/结尾否则配置校验会直接报错见 pkg/compactor/config.go设置后Compactor 将 marker 文件写入 chunk 所在的同一按周期per-period对象存储桶中、该前缀之下启动时Compactor 会把本地已有的 marker 文件复制到该前缀然后删除本地副本见 pkg/compactor/compactor.go使用client.NewPrefixedObjectClient包装对象存储客户端随后os.RemoveAll清理本地目录这让你可以在不挂载持久化卷的情况下运行 Compactor留空则继续使用本地磁盘。当 marker 存于对象存储时每个对象存储类型只会初始化一个 sweeper见 pkg/compactor/compactor.go避免重复清扫。Sweeper 的删除逻辑带重试失败时按retention_backoff_config退避重试若 chunk 已不存在IsChunkNotFoundErr则视为成功跳过见 pkg/compactor/retention/retention.go删除耗时与结果会记录到deleteChunkDurationSeconds指标中。开启保留的完整配置示例与参数详解以下 Compactor 配置示例激活了保留功能compactor: working_directory: /data/retention compaction_interval: 10m retention_enabled: true retention_delete_delay: 2h retention_delete_worker_count: 150 delete_request_store: gcs schema_config: configs: - from: 2020-07-31 index: period: 24h prefix: index_ object_store: gcs schema: v13 store: tsdb storage_config: tsdb_shipper: active_index_directory: /data/index cache_location: /data/index_cache gcs: bucket_name: loki注意保留仅在索引周期index period为 24h 时可用。单存储 TSDBSingle Store TSDB本身要求 24h 索引周期。各参数含义默认值均取自 pkg/compactor/config.go 的RegisterFlags参数说明默认值retention_enabled必须设为true否则 Compactor 只压缩表、不执行保留falsedelete_request_store配置存储删除请求的后端如gcs、s3启用保留时必填未配置会校验失败空working_directory保存已标记 chunk 与临时表的本地目录/var/loki/compactorcompaction_interval压缩执行的频率Compactor 落后时会尽快补跑10mapply_retention_interval保留执行的频率独立于compaction_interval默认0s时取compaction_interval值并加抖动0sretention_delete_delay标记后延迟多久真正删除 chunk2hretention_delete_worker_count用于删除 chunk 的 goroutine 工作协程最大数量150调优保留时还有几个相关选项retention_table_timeout限制 Compactor 在单张表上花费的保留/删除处理时间。默认0s表示无超时。若某表处理超时Compactor 会记录告警并提前停止而非失败剩余工作留待下一轮继续retention_backoff_config控制 sweeper 删除已标记 chunk 失败时的重试行为最小/最大退避间隔与重试次数delete_request_store_key_prefix删除请求在delete_request_store中的存储路径前缀默认index/delete_request_store_db_type删除请求存储所用数据库类型默认boltdb。完整的compactor配置项与默认值可参考仓库中的配置解析实现 pkg/compactor/config.go以及 Loki 的配置文档目录 docs/sources/configure。配置保留周期全局、按流与按租户保留周期在limits_config配置段中设置有两种策略retention_period应用于所有日志流的全局保留周期retention_stream仅应用于匹配 selector 的日志流。注意每条retention_stream规则的period必须至少为 24h。Loki 会在配置校验时直接拒绝更短的取值——无论是全局retention_stream列表还是任何租户覆盖pkg/validation/limits.go 中的Validate()会解析 selector 并检查time.Duration(rule.Period) 24*time.Hour。该最小值不适用于retention_period无论全局还是按租户。以下示例配置了应用于所有租户的全局保留除非被按租户覆盖... limits_config: retention_period: 744h retention_stream: - selector: {namespacedev} priority: 1 period: 24h runtime_config: file: /etc/overrides.yaml ...注意retention_stream的selector字段只能使用标签匹配器label matchers不支持任意 LogQL 表达式。这一点同样在Validate()中通过syntax.ParseMatchers(rule.Selector, true)解析校验。按租户的保留可通过配置runtime overrides运行时覆盖实现。例如overrides: 29: retention_period: 168h retention_stream: - selector: {namespaceprod} priority: 2 period: 336h - selector: {containerloki} priority: 1 period: 72h 30: retention_stream: - selector: {containernginx, leveldebug} priority: 1 period: 24h在 pkg/validation/limits.go 中RetentionPeriod model.Duration与StreamRetention []StreamRetention分别对应retention_period与retention_stream字段StreamRetention的文档注释明确指出selector 是 Prometheus 标签匹配器当多个规则同时匹配时取优先级最高者若无规则匹配则使用retention_period。保留周期决策优先级给定日志流的保留周期按列表中第一个匹配项决定判定顺序如下若多条按租户retention_streamselector 都匹配该流取优先级最高的保留周期若多条全局retention_streamselector 匹配该流取优先级最高的保留周期但若设置了按租户retention_stream全局值不会被考虑若指定了按租户retention_period则应用该值若以上均未匹配应用全局retention_period若连全局retention_period都未指定则使用默认值0s——意味着日志无限期保留。注意优先级数值越大优先级越高。若两条匹配的retention_stream规则优先级相同Loki 采用两者中**较短较小**的那个周期。流匹配使用与 Prometheus 标签匹配相同的语法: 选择标签值精确等于给定字符串的标签!: 选择标签值不等于给定字符串的标签~: 选择标签值正则匹配给定字符串的标签!~: 选择标签值不正则匹配给定字符串的标签。以上示例配置将产生如下保留周期租户29带namespaceprod标签的流保留336h两周——即使其container标签为loki因为prod规则优先级2更高带containerloki但不在prodnamespace 的流保留72h该租户其余流应用按租户覆盖值retention_period: 168h。租户30同时带nginx与leveldebug标签的流保留24h其余流无覆盖应用全局retention_period的744h。除29、30外的所有租户带namespacedev标签的流保留24h其余流保留744h。完整示例GCS 搭配 28 天保留以下是在 GCS 上实现 28 天保留的完整配置schema_config: configs: - from: 2018-04-15 store: tsdb object_store: gcs schema: v13 index: prefix: loki_index_ period: 24h storage_config: tsdb_shipper: active_index_directory: /loki/index cache_location: /loki/index_cache gcs: bucket_name: GCS_BUCKET_NAME limits_config: max_query_lookback: 672h # 28 days retention_period: 672h # 28 days compactor: working_directory: /data/retention delete_request_store: gcs retention_enabled: true注意此配置同时设置了max_query_lookback: 672h将查询回溯窗口与保留周期对齐避免查询超出保留范围的数据。对象存储生命周期策略与 Compactor 保留的边界Loki 将 chunk 与索引以对象形式存储在配置的对象存储中S3、GCS、Azure Blob Storage 等。chunk 由 Compactor 按保留配置删除但Compactor 只删除 chunk 对象绝不会删除持有集群状态的对象如索引或集群种子文件。如果同时在桶上配置对象存储生命周期策略务必谨慎限定作用范围。一条“删除所有 N 天前对象”的宽泛规则最终会删掉 Loki 必须保留的对象损坏存储并导致查询失败。前文的提示同样适用任何生命周期过期时间都必须长于保留周期。警告绝不要对**空前缀针对整个桶**应用生命周期规则。始终将生命周期规则限定在下面描述的每租户 chunk 前缀上。桶内对象布局写入配置桶的对象如下确切前缀取决于你的schema_config以及是否启用多租户编写生命周期规则前请对照自己的配置确认对象或前缀内容能否用生命周期策略过期tenant_id/单租户模式下如fake/多租户下为各租户 org IDchunk 对象日志数据可以。这是生命周期规则唯一应该瞄准的对象。索引对象索引路径前缀之下默认index/由schema_config中的index.path_prefix设置TSDB 或 BoltDB 索引文件由 ingester 写入、Compactor 重写。注意index.prefix值如index_、loki_index_是该路径内的表名前缀不是路径本身生命周期规则应匹配路径前缀不可以。Loki 内部管理索引生命周期删除索引对象会移除活 chunk 的引用并破坏查询。loki_cluster_seed.json桶根目录下保存集群种子的小文件用于用量上报只写一次、从不轮转不可以。按年龄的宽泛规则会在其超过 TTL 后将其删除。删除请求存储对象delete_request_store_key_prefix下的delete_requests/表默认index/位于delete_request_store配置的桶中Compactor 通过 日志删除 API 提交的删除请求数据库及其处理状态不可以。移除会导致 Compactor 丢失待处理删除请求的追踪。删除标记对象仅当设置了deletion_marker_object_store_prefix才存在否则 marker 保存在 Compactor 本地磁盘不会出现在桶中Compactor 记录的已标记但尚未清扫的 chunk不可以。移除会导致 Compactor 丢失待删除 chunk 的追踪。Ruler 存储对象若 Ruler 使用同一桶录制规则与告警规则文件不可以。这些是配置而非时序数据。Compactor 保留与对象存储生命周期策略的差异两者是两套独立机制不应配置成互相冲突Compactor 保留是索引感知的只有从索引中移除某 chunk 的全部引用后才会删除它并且 sweeper 会等待compactor.retention-delete-delay默认2h再删除已标记 chunk以便 Index Gateway 先拉取到重写后的索引对象存储生命周期策略是索引无感知的它纯粹按年龄与前缀删除对象不知道某 chunk 是否仍被索引引用。对大多数部署而言启用 Compactor 保留就足够了未必需要对象存储生命周期策略。如果出于成本兜底仍要加生命周期规则请将其过期时间设置为长于“保留周期 compactor.retention-delete-delay”保证 Compactor 总是先删 chunk生命周期规则最多只能捕获 Compactor 已孤儿化的对象。示例Amazon S3 生命周期策略以下策略只过期单个租户前缀下的 chunk 对象。将fake/替换为你的租户前缀多租户时每个租户或 org ID 前缀一条规则。Days值必须大于保留周期与删除延迟之和{ Rules: [ { ID: loki-chunk-expiration, Status: Enabled, Filter: { Prefix: fake/ }, Expiration: { Days: 395 } } ] }使用 AWS CLI 应用aws s3api put-bucket-lifecycle-configuration \ --bucket YOUR_LOKI_BUCKET \ --lifecycle-configuration file://lifecycle.json示例Google Cloud Storage 生命周期规则等价的 GCS 规则按年龄与前缀过期对象。与 S3 一样将matchesPrefix限定到租户 chunk 前缀并保持age大于“保留周期 删除延迟”{ rule: [ { action: {type: Delete}, condition: { age: 395, matchesPrefix: [fake/] } } ] }使用 Google Cloud CLI 应用gcloud storage buckets update gs://YOUR_LOKI_BUCKET --lifecycle-filelifecycle.json对于 Azure Blob Storage使用带prefixMatch的管理策略将其设置为租户 chunk 前缀并遵循同样的“过期时间必须大于保留周期加删除延迟”原则。小结Loki 的日志保留是一条完整的异步链路Compactor 以单例形式在独立调度上压缩索引、按策略标记过期 chunk写入 marker 文件再由 sweeper 在retention_delete_delay之后异步删除。配置上只需记住几个关键点启用retention_enabled并配置delete_request_store、保证索引周期为 24h、通过limits_config组合retention_period与retention_stream优先级高者胜出、同优先级取较短周期并让对象存储生命周期策略只瞄准租户 chunk 前缀且过期时间长于保留周期与删除延迟之和。深入阅读 retention.md、logs-deletion.md 以及 pkg/compactor/config.go 与 pkg/compactor/retention/retention.go 的源码实现可以进一步理解每个参数在底层如何影响保留行为。【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考