news 2026/9/29 9:24:39

Grafana Loki 日志删除实战指南:从最小配置到取消一条发错的删除请求

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Grafana Loki 日志删除实战指南:从最小配置到取消一条发错的删除请求

Grafana Loki 日志删除实战指南:从最小配置到取消一条发错的删除请求

【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki

Grafana Loki 支持按流、时间窗口和可选行过滤器删除日志条目,删除请求由 compactor 承载:提交后先进入可反悔的取消期,之后数据才从对象存储中物理移除。这是一份 Loki 日志删除的落地指南。

什么情况下你真正需要删日志

日志只增不删的假设,在三种场景下会失效:

  • 合规要求:GDPR 等法规要求删除特定主体关联的日志,"不再展示"不够,必须让数据物理消失;
  • 隐私数据误写:密钥、token、个人信息被打进了日志,留在对象存储里就是长期风险;
  • 容量回收:某批测试数据、某个事故期间的爆量日志占据了可观的存储费用。

但"删"在 Loki 里不是像rm那样直接擦掉一块数据。Loki 把日志按流(stream)打包成一个个chunk(压缩后的日志块,是对象存储的最小读写单位),索引只记录"哪个流的数据在哪些 chunk 里"。因此带行过滤器的物理删除,本质上是把受影响的 chunk 读出来、剔除匹配行、重建压缩块、再写回对象存储并更新索引;而不带行过滤器的删除(整条流的时间窗口),则可以直接废弃整块 chunk。

另外先明确一个索引前提:日志条目删除在TSDB 索引下完整支持;旧的 BoltDB-Shipper 索引也能用,但它计划在 Loki 4.0 移除,新集群请直接用 TSDB。

快速上手:三个前置条件加一份最小配置

删除能力不默认开启,三个条件缺一不可:

  1. 打开 retention 开关:compactor.retention_enabled: true。删除 API 端点只有在它开启时才注册,否则所有请求都会得到 400Retention is not enabled(见 pkg/loki/modules.go 中的端点注册逻辑)。
  2. 配置删除请求存储桶:delete_request_store必须非空。开启 retention 却没填它,compactor 启动校验会直接报错退出(pkg/compactor/config.go 中Validate())。
  3. 租户的deletion_mode不是disabled:默认值就是filter-and-delete,所以前两条满足后,删除对所有租户天然生效。

⚠️开 retention 前先给对象存储桶打开版本控制(versioning)。retention 和删除都是不可逆的重操作,桶的版本历史是你唯一的后悔药。如果只想开删除能力、并不打算按保留期清理数据,把limits_config.retention_period设为0s即可。

一份最小可用配置:

limits_config: retention_period: 0s # 不强制执行 retention,仅开启删除能力 deletion_mode: filter-and-delete compactor: working_directory: /var/loki/compactor retention_enabled: true delete_request_store: gcs://your-bucket/delete-requests # 换成你的对象存储 delete_request_store_db_type: boltdb delete_request_cancel_period: 24h delete_max_interval: 24h

与删除直接相关的 compactor 配置项及默认值(源码见 pkg/compactor/config.go):

配置项默认值作用
retention_enabledfalse删除 API 的总开关
delete_request_store空(启用 retention 后必填)存放删除请求的对象存储桶
delete_request_store_key_prefixindex/桶内路径前缀
delete_request_store_db_typeboltdb本地数据库引擎,可选sqlite
backup_delete_request_store_db_type空迁移用的备份库(当前仅支持 boltdb)
delete_request_cancel_period24h取消期:请求超过该时长后数据才会被真正删除
delete_max_interval24h带行过滤器的删除请求分片跨度上限
delete_batch_size70每个周期最多处理的删除请求数
retention_delete_delay2h取消期之后、chunk 真正删除前的额外缓冲
retention_delete_worker_count150删除 chunk 的并发协程数
apply_retention_interval0sretention 生效周期;为 0 时自动对齐 compaction 周期并加最多 10 分钟抖动,避免两者抢同一时刻

模式选型:deletion_mode 三种取值怎么选

deletion_mode定义在limits_config中,支持全局默认值加按租户覆盖(覆盖写在运行时配置文件里,取值来自 pkg/validation/limits.go)。它回答一个问题:匹配删除请求的日志,是"看不见"还是"不存在"?

按你的诉求对号入座:

  • 只要"看不见",物理数据还要留着(例如法务要求隐藏,但你自己想保留取证副本)→ 选filter-only。查询路径会按需过滤掉匹配行,对象存储里的 chunk 原封不动。
  • 必须物理消失(GDPR 类合规)→ 选filter-and-delete(默认值)。查询时过滤,同时 compactor 会把数据从存储中移除。
  • 共享集群里不想让某个租户碰删除→ 对该租户覆盖为disabled。此时它的删除 API 请求会被拒绝(validDeletionLimit检查不通过,见 pkg/compactor/deletion/util.go)。

两个容易踩的点:

  • 拼写错误会被拒绝。ParseMode只认disabled、filter-only、filter-and-delete三个值,写错一个字母,配置校验直接失败(pkg/compactor/deletionmode/mode.go)。
  • 端点注册与 retention 强绑定,与 deletion_mode 无关。retention_enabled为 false 时,端点压根不存在,handler 为 nil,任何租户访问都得到 400。先开 retention,再用deletion_mode做租户级收放,这是正确的控制顺序。

实操:提交、查询、取消一条删除请求

删除端点挂在 compactor 上,路径统一为/loki/api/v1/delete,POST 提交、GET 查询、DELETE 取消(注意取消用的是 HTTP DELETE 方法)。所有操作都需要租户头X-Scope-OrgID。

提交删除请求。参数规则来自 pkg/compactor/deletion/request_handler.go:

  • query(必填):LogQL 流选择器,可带行过滤器,如{cluster="prod"} |= "ERROR"。提交时就会完整解析并构建 pipeline,非法的正则、写错的ip()模式会当场返回 400,而不是留到执行期爆炸;
  • start/end(必填):Unix 秒(必须恰好 10 位数字)或 RFC3339 格式。两条硬规则:不允许删除未来时间(deletes in the future are not allowed),且 start 必须严格小于 end;
  • max_interval(可选):请求更细的分片,最小1s,单位只认s/m/h,不能大于delete_max_interval,也不能大于待删时间窗口本身。
curl -X POST -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?query=%7Bcluster%3D%22prod%22%7D&start=1704067200&end=1704153600"

成功返回204 No Content,响应头X-Delete-Request-ID就是这条删除请求的 ID,取消时要用到。

查询删除请求。返回该租户全部请求的 JSON 数组,按创建时间排序,内部字段UserID、SequenceNum会被抹掉。

curl -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete" # 只看在查询时参与过滤的请求: curl -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?for_querytime_filtering=true" # 按时间重叠过滤(start/end 同样支持 Unix 秒或 RFC3339): curl -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?start=1704067200&end=1704153600"

被分片的大请求在列表里会合并回一条展示,状态按已完成子请求比例渲染为Received、N% Complete或Processed(合并逻辑mergeDeletes)。

取消一条发错的删除请求。常规取消只在取消期内、且请求还没开始处理时有效:

curl -X DELETE -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?request_id=<REQUEST_ID>"

三种会被 400 挡回的情况:请求 ID 不存在(404)、状态已是Processed、请求已部分完成或创建时间超过delete_request_cancel_period(默认 24h)。后两种可以加force=true强制取消:

curl -X DELETE -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?request_id=<REQUEST_ID>&force=true"

另有两个缓存辅助端点,与删除联动:GET /loki/api/v1/cache/generation_numbers查租户的缓存代数;POST /loki/api/v1/cache/generation_numbers/increase手动递增代数、令该租户的查询结果缓存失效(例如回放历史数据后)。删除处理完成时代数会自动递增,保证查询不会命中已删数据的旧缓存。

幕后机制:一条删除请求提交后发生了什么

时间线比直觉中慢很多,这是刻意的:

  1. 提交后先躺平。请求进入存储,状态Received。只有当请求年龄超过delete_request_cancel_period(默认 24h),它才会进入实际删除阶段——这 24 小时就是给你留的取消窗口。
  2. compactor 周期扫描。每个 retention 周期(apply_retention_interval,默认与 compaction 周期一致并带抖动)检查未处理请求,每个周期最多处理delete_batch_size(默认 70)条。
  3. 分片只针对带行过滤器的请求。流选择器本身不带过滤器时,整个时间窗口就是一条请求,不拆分。带行过滤器时,buildRequests会把它切成每段不超过delete_max_interval的子请求,且相邻子请求刻意保留少量时间重叠而不是严丝合缝——因为端点是闭区间语义,精确衔接会在边界留 1ms 缝隙漏删日志。
  4. 重建与写回。filter-and-delete模式下,涉及删除的 chunk 会被读出、剔除匹配行、重写压缩块、上传回对象存储并更新索引;带行过滤器的删除由删除清单(deletion manifest,deletion_manifest_builder.go)驱动。整个动作还要再等retention_delete_delay(默认 2h)的缓冲。
  5. 横向扩展。horizontal_scaling_mode支持 main/worker 分工,删除作业经 jobqueue 分发给 worker(pkg/compactor/deletion/job_runner.go)。

执行进度由 delete_requests_manager.go 中的DeleteRequestsManager持续推进,指标定义在 pkg/compactor/deletion/metrics.go,包括deleteRequestsReceivedTotal、deleteRequestsProcessedTotal、deletedLinesTotal、deletionFailures等,前缀loki_compactor_,是观察积压和失败的第一现场。

⚠️带行过滤器的删除是 compactor 最耗资源的操作之一:每个相关 chunk 都要读出来、剔除匹配行、再重写回对象存储,CPU 和 IO 双密集。多租户、大批量的场景请把删除工作分散到多个 compactor 实例(横向扩展),别指望单机扛。

请求存储与迁移:boltdb 与 sqlite 双写思路

delete_request_store和delete_request_store_db_type是两个不同维度:前者是删除请求数据文件所在的对象存储位置,后者是 compactor 本地使用的数据库引擎(默认boltdb,可切sqlite)。两套实现分别在 delete_requests_db_boltdb.go 和 delete_requests_db_sqlite.go。

想从 boltdb 迁到 sqlite 又不丢请求,用备份库双写:

compactor: delete_request_store_db_type: sqlite # 目标引擎 backup_delete_request_store_db_type: boltdb # 迁移期间同时写老引擎

backup_delete_request_store_db_type当前只支持boltdb(见 flag 说明),语义就是"迁移期间新请求同时写进备份库,老数据仍可被读取"。等迁移确认无遗漏后去掉备份配置即可。这也是为什么保留一个已开版本控制的delete_request_store桶格外重要:本地引擎迁移出问题,桶里的请求数据才是兜底。

避坑清单与运维检查项

上线前逐项过一遍:

  1. ⚠️对象存储桶版本控制已开启——包括日志数据桶和delete_request_store桶。误删之后这是唯一能恢复的路径。
  2. 先用filter-only观察,再切filter-and-delete。同一批流、同一时间窗口,先让删除请求以纯过滤模式跑几天,用查询结果核对命中范围是否符合预期,再物理落地。
  3. 控制分片粒度。大批量带过滤器的删除,用max_interval把单次分片压小(如2h),避免一条请求横跨数天导致执行时间失控;注意它只能小于等于delete_max_interval。
  4. 取消期别调太短。flag 注释建议delete_request_cancel_period至少 24h——调成 1h 看似"删得快",实则是把操作错误的缓冲时间压缩到几乎没有。
  5. 盯指标。loki_compactor_deletion_*家族重点看请求积压(received 与 processed 的差值走势)和deletionFailures;持续积压就横向扩 compactor。
  6. 缺 chunk 时的逃生门要慎用。deletion_ignore_missing_chunks(默认false)可在"索引指向的 chunk 在对象存储里丢失"时跳过该 chunk、不让整条删除请求卡死,被跳过的数量计入loki_compactor_deletion_missing_chunks_total。但源码注释警告得很直白:只有在确认真正丢失后才应临时开启——若 chunk 其实还在却被跳过,它承载的数据将永远不会被删掉,而请求却会显示已完成。

Loki 的日志删除把"强操作"拆成了可反悔的两段:提交到执行之间隔着取消期,执行之前还隔着 retention 与删除延迟的缓冲。把deletion_mode、分片粒度和监控接好,这套能力就能安全地服务于合规与容量需求。

【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 9:24:37

芯片烧录详解:ISP、ICP、IAP三种方式原理与实操区别

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 9:23:03

YOLOv5目标检测全解析:从网络结构到TensorRT部署实战

我记起来做目标检测那阵子&#xff0c;最焦头烂额的阶段不是调参&#xff0c;而是数据标到凌晨三点发现标错了一百多张图。后来项目上了 YOLOv5&#xff0c;流程图和结构图在我脑子里过了无数遍&#xff0c;踩过的坑也一个没落下。这篇就跟大家从原理拆到实战&#xff0c;把 YO…

作者头像 李华
网站建设 2026/9/29 9:23:00

PyTorch模型端侧优化四层漏斗工作流实战

1. 项目概述&#xff1a;这不是一个“一键压缩”的玩具&#xff0c;而是一套面向真实推理场景的模型瘦身工作流“Model-Optimizer”这个名字听起来像某个商业软件的宣传页标题&#xff0c;但在我过去三年深度参与十几个边缘AI部署项目的实操经验里&#xff0c;它从来不是点几下…

作者头像 李华