Grafana Loki 日志删除实操:从提交请求到数据真正被删掉
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
想象一下这样的场景:某租户把包含支付卡号的日志写进了 Loki,合规要求必须在 72 小时内把这些行从存储里清掉。Grafana Loki 的日志条目删除(Log Entry Deletion)就是为此设计的——你提交一个带流选择器、时间窗口、可选行过滤器的删除请求,等取消期过后,数据才真正从对象存储里消失。下面按"先跑通、再会查、最后敢删"的顺序讲清楚。
🧭 30 秒心智模型:删除是"先记账、再执行"的两段式操作
- 删除是异步任务,不是同步动作。提交请求只产生一条持久化的"删除记录",真正的数据移除由 compactor 周期性执行,中间隔着两段缓冲时间(见后文)。
- 取消期就是你提交后的"后悔窗口"。在
delete_request_cancel_period(默认24h)内,请求随时可以撤销;超过窗口就基本不可逆了。 - 删除记录本身会被持久化,所以即使是"只过滤不删"的模式,查询路径也会按它实时过滤结果——提交即对查询生效。
- 三种模式决定最终行为:
disabled(禁止删除,API 拒绝)、filter-only(只过滤不删)、filter-and-delete(过滤并从存储移除,默认值)。 - 执行者只有一个角色:compactor。微服务模式里它是独立组件,负责 retention 与删除的整套工作:
一句话总结:API 负责登记,compactor 负责执行,取消期是你的反悔时间。
🚀 上手路径:三处配置 + 一次成功调用
这节解决"最小要改哪些东西才能跑起来"的问题。删除能力同时依赖三个开关,缺一不可:
- compactor 侧
retention_enabled: true——删除 API 端点只在这个开关为true时才注册,否则所有请求直接返回400 Retention is not enabled; - compactor 侧
delete_request_store必填——否则启动校验直接失败,报错compactor.delete-request-store should be configured when retention is enabled(见 pkg/compactor/config.go 的Validate()); - 租户的
deletion_mode不是disabled——它是limits_config中的全局加按租户设置,默认filter-and-delete,所以满足前两条后所有租户开箱可用,也可在运行时配置文件中按租户覆盖。
最小可用配置(注释说明每行"为什么"):
limits_config: retention_period: 0s # 只要删除能力、不强制按保留期清理,就设为 0s deletion_mode: filter-and-delete # 全局默认;想先观察命中范围可先改 filter-only compactor: working_directory: /var/loki/compactor retention_enabled: true # 不开这个,删除端点根本不存在 delete_request_store: gcs://bucket_for_delete_requests # 删除记录的存放位置,按你的对象存储类型改 delete_request_store_db_type: boltdb # 本地请求数据库引擎,默认 boltdb,可换 sqlite delete_request_cancel_period: 24h # 后悔窗口,源码注释建议至少 24h delete_max_interval: 24h # 带行过滤器的单个分片最大跨度配置生效后,提交一条删除请求做冒烟验证:
curl -i -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"参数逐个说:
query:LogQL 流选择器,如{cluster="prod"},可追加行过滤器(|= "ERROR")。表达式会在提交时就解析校验,regex 写错、ip()模式非法等直接400,不会拖到执行期才炸。start/end:起始与结束时间,Unix 秒(必须是 10 位)或 RFC3339 格式。两个硬校验:end不能指向未来(deletes in the future are not allowed),start必须严格小于end(start time can't be greater than or equal to end time)。- 多租户用
X-Scope-OrgID头标识,请求按租户隔离。
成功的样子:204 No Content,响应头X-Delete-Request-ID里就是删除请求 ID,请收好,后续查询和取消都靠它。失败的样子:各种400加上明确的错误文案(query not set、start time not set、invalid start time: require unix seconds or RFC3339 format等),全部在提交阶段拦截,不会产生半成品请求。
📋 操作手册:提交 → 查询 → 取消
这节把日常会用到的端点按动作流串起来,每条都给出命令、关键参数和成败判据。
提交:带行过滤器时记得控制分片大小
curl -i -X POST -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?query=%7Bcluster%3D%22prod%22%7D%20%7C%3D%20%22ERROR%22&start=1704067200&end=1704681600&max_interval=12h"max_interval是唯一的可选参数,语义是"单个子请求最多覆盖多长":
| 约束 | 触发的错误 |
|---|---|
最小1s,单位仅s、m、h | invalid max_interval: valid time units are 's', 'm', 'h' |
不能超过delete_max_interval | max_interval can't be greater than 24h0m0s |
| 不能超过本次要删的窗口本身 | max_interval can't be greater than the interval to be deleted (...) |
查询:看状态、看进度
curl -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete"返回该租户的删除请求 JSON 数组,按创建时间升序。两个可选参数:
for_querytime_filtering=true:只返回与查询时过滤相关的请求;start+end:时间重叠过滤,只返回与给定区间有交集的请求。
被拆分过的同一请求的多个子请求会被合并成一条展示,状态按完成比例呈现:Received(未开始)、N% Complete(部分完成)、Processed(全部完成)。验证生效最直接的方式就是轮询这个接口,等状态从Received走到Processed;若是filter-only模式,还可以直接对查询做回归,确认匹配行已不可见。
取消:窗口内随便撤,窗口外要 force
curl -i -X PUT -H "X-Scope-OrgID: tenant1" \ "http://loki-compactor:3100/loki/api/v1/delete?request_id=<REQUEST_ID>"成败判据一览:
| 场景 | 响应 |
|---|---|
| 取消成功 | 204 No Content |
| ID 不存在 | 404 could not find delete request with given id |
请求已在处理中或已完成(Processed) | 400 deletion of request which is in process or already processed is not allowed |
| 已超过取消期 | 400 Cancellation of partially completed delete request or delete request past the deadline of 24h0m0s since its creation is not allowed. To force, use the ?force query parameter |
最后这种想强行撤销,追加&force=true即可:
curl -X PUT -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_number查看,POST同一端点递增(成功返回204),该租户的查询结果缓存随即失效。
⚙️ 原理与边界:只讲影响你决策的部分
这节不展开源码,只回答"什么情况下这些机制会咬到你"。
什么时候数据才真正消失
提交请求 ≠ 删除发生。时序是:取消期(delete_request_cancel_period,默认24h)→ compactor 在每个 retention 周期扫描未处理且已过取消期的请求 → 每周期最多处理delete_batch_size(默认70)个 → chunk 重建并写回对象存储。apply_retention_interval设为0时与 compaction 周期(默认10m)一致,并自动加上最多10m(不超过其一半)的抖动避免撞车。filter-and-delete模式下,涉及删除的 chunk 需要读出来、剔除匹配行、重写再写回,这是 CPU 与 IO 最重的操作;需要大批量、多租户删带过滤器的数据时,考虑横向扩展 compactor,把删除工作分到多个实例。
什么时候分片机制会影响你
只有带行过滤器的请求会被拆分:超过delete_max_interval(默认24h)的窗口会被切成多个不超过该跨度的子请求;不带行过滤器的请求永远不拆。分片之间刻意保留少量时间重叠而非精确衔接,避免边界 1ms 的缝隙漏删。你要关心的只是:删除窗口很长且带行过滤器时,用max_interval把单片压小,避免单个请求执行时间过长。
什么时候会碰到请求存储迁移
删除记录存两份维度:delete_request_store是对象存储位置(放数据文件),delete_request_store_db_type是本地数据库引擎(boltdb默认,或sqlite)。从一种引擎迁到另一种时,设backup_delete_request_store_db_type: boltdb让请求双写备份库,迁移期间不丢请求——当前备份库仅支持 boltdb。索引存储侧的约束:TSDB 索引完整支持删除;BoltDB Shipper 索引虽也支持,但将在 Loki 4.0 移除,新部署直接用 TSDB。
🚧 避坑清单
⚠️启用 retention 前,先在对象存储上开启版本控制。retention 配错会连带删数据,没有版本控制就没有回滚。对策:开启 versioning 是启用retention_enabled: true的前置动作,不是可选项;只想用删除能力、不想强制保留期,把retention_period设0s。
⚠️filter-only是免费的试错通道。先切到filter-only提交同样的请求,用查询验证实际命中范围,确认无误再改回filter-and-delete落地物理删除。对策:同一请求在两种模式下都会持久化,观察期不丢数据。
⚠️取消窗口过后就没有第二次机会了。默认24h,且已进入处理中的请求默认不可取消(force=true也只能用于部分完成的请求)。对策:把delete_request_cancel_period按合规节奏调长;提交后立刻用 GET 接口核对流、时间窗口、行过滤器三要素再等待。
⚠️deletion_mode拼错不会静默降级,而是直接报错。未知值返回unknown deletion mode: must be one of disabled|filter-only|filter-and-delete,请求校验阶段就失败。对策:上线前用一次400请求冒烟,或检查运行时配置里租户覆盖值的拼写。
⚠️带行过滤器的删除是 compactor 上最重的活之一。每个相关 chunk 都要读出、剔除、重写、回传,积压会拖慢整个 retention 周期。对策:监控loki_compactor_deletion_*指标(选中 chunk 数、已删行数、已处理请求数、失败数),观察积压与失败;需要时按官方横向扩展方案把 compactor 扩成多实例分摊删除负载。
🔍 快速参考
| 配置项 / 端点 | 默认值 | 作用 |
|---|---|---|
compactor.retention_enabled | false | 总开关;true才注册删除端点 |
compactor.delete_request_store | 空(启用 retention 时必填) | 删除记录的对象存储位置 |
compactor.delete_request_store_key_prefix | index/ | 记录在桶中的路径前缀 |
compactor.delete_request_store_db_type | boltdb | 本地请求数据库引擎(boltdb/sqlite) |
compactor.delete_request_cancel_period | 24h | 后悔窗口,过后才执行删除 |
compactor.delete_max_interval | 24h | 带行过滤器请求的单分片最大跨度 |
compactor.delete_batch_size | 70 | 每 retention 周期最多处理的请求数 |
compactor.retention_delete_delay | 2h | chunk 真正被删除前的额外缓冲 |
compactor.retention_delete_worker_count | 150 | 删除 chunk 的工作协程数 |
limits_config.deletion_mode | filter-and-delete | 全局/按租户模式,disabled时端点返回403语义的拒绝 |
POST /loki/api/v1/delete | — | 提交删除请求,204+X-Delete-Request-ID |
GET /loki/api/v1/delete | — | 列出请求,支持for_querytime_filtering、start/end |
PUT /loki/api/v1/delete?request_id=... | — | 取消请求,超期需force=true,204成功 |
删除能力 =retention_enabled+delete_request_store+ 非disabled的deletion_mode,三要素齐备即对租户生效。记住两段缓冲——取消期与retention_delete_delay——就是"提交"与"真删"之间的距离。把filter-only当演练、把取消期当刹车、把 compactor 指标当仪表盘,这套强操作能力就能安全地用在生产上。
<输出文章>
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考