Grafana Loki 日志删除实操:配置、API 与避坑全解
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
合规要求把敏感信息从日志里抹掉,可分布式日志系统不是删个文件就行。Grafana Loki 的日志删除功能让你通过 compactor 提交请求,保护窗口过后才真正抹除数据。几段 YAML 加几条 curl 命令就能跑通。
什么能删什么不能删:场景与边界
先说清楚什么时候需要删日志:
- 合规擦除:用户要求删除其个人数据,对应的时间段必须真的消失
- 测试数据清理:压测流量混进了生产流,要按时间窗口清掉
- 敏感信息误入库:密码、token 被打进日志,需要尽快消除
Grafana Loki 能删的粒度是日志条目(一行一行)。一个删除请求由三个要素构成:流选择器(如{cluster="prod"})、起止时间窗口、可选的 LogQL 行过滤器(如|= "ERROR")。它做不到的是:不能删掉流本身,不能改动索引的既有结构,更不会实时生效——提交之后有一段保护窗口,窗口内还能反悔。
📌 索引类型有硬约束:
- TSDB 索引:完整支持,新部署的标准选择
- BoltDB Shipper:也支持删除,但已废弃并将在 Loki 4.0 移除,别在新环境上选它
删除由 compactor 组件承载,它在整体架构里的位置如下图(单体模式下所有组件跑在同一个 Loki 二进制里):
三步开启删除功能
Loki 日志保留与删除共用同一套底座:删除能力构建在 compactor 的 retention(日志保留,到期自动清数据)工作流之上。开启它只需三步:
- 打开 retention 开关
compactor: retention_enabled: true只有开关打开时,删除 API 端点才会被注册。关闭状态下,无论deletion_mode取什么值,任何租户都访问不了删除接口。命令行等价写法是-compactor.retention-enabled;YAML 键与命令行 flag 一一对应,前缀都是-compactor.,后文不再重复。
配置删除请求桶
delete_request_store:指定一个对象存储桶,用来存放删除请求的数据文件。retention 一旦启用,这一项就是必填——留空时 compactor 启动校验直接失败,进程起不来。确认
deletion_mode不是disabled:它是limits_config里的全局与按租户设置,默认值filter-and-delete意味着开箱即删。想限制某个租户时,在运行时配置文件中做按租户覆盖即可。
⚠️安全警告:开 retention 要谨慎。强烈建议同时在对象存储上开启版本控制——retention 配置写错时,还能从版本历史把数据捞回来。如果只想要删除能力、不想强制执行日志保留,把retention_period设为0s。
与删除相关的核心配置项如下(定义见 pkg/compactor/config.go):
| 配置项 | 默认值 | 一句话说明 |
|---|---|---|
retention_enabled | false | retention 与删除功能的总开关 |
delete_request_store | 空 | 存放删除请求的桶,启用后必填 |
delete_request_store_key_prefix | index/ | 请求数据在桶内的路径前缀 |
delete_request_cancel_period | 24h | 删除请求的保护窗口(可取消时长) |
delete_max_interval | 24h | 带行过滤器请求的最大跨度,超出自动拆分 |
delete_batch_size | 70 | 每个周期最多处理的删除请求数 |
retention_delete_delay | 2h | chunk 真正删除前的额外缓冲 |
retention_delete_worker_count | 150 | 删除 chunk 的并发工作协程数 |
一个最小可用配置示例:
limits_config: retention_period: 744h # 不想强制保留就改成 0s deletion_mode: filter-and-delete compactor: working_directory: /var/loki/compactor retention_enabled: true delete_request_store: gcs://bucket_for_delete_requests delete_request_cancel_period: 24h delete_max_interval: 24h三种删除模式(deletion_mode)怎么选
deletion_mode决定删除请求对查询层和存储层各产生什么效果:
| 模式 | 查询层行为 | 存储层行为 | 典型场景 |
|---|---|---|---|
disabled | 照常返回 | 不删除 | 未授权租户;删除 API 直接返回403 |
filter-only | 过滤掉匹配行 | 不删除,数据仍在存储里 | 先观察删除范围,或只做逻辑擦除 |
filter-and-delete | 过滤掉匹配行 | 从存储中物理移除(默认) | 合规删除,数据必须真正消失 |
决策建议:✅ 先用filter-only跑一轮,在查询里确认命中范围符合预期,再切到filter-and-delete落地物理删除;不需要删除能力的租户用disabled锁死。配置拼写错误(比如filteranddelete)会导致请求解析失败,注意连字符。
动手:Loki compactor 删除 API 实操
提交、跟踪、取消三个端点都挂在 compactor 上,且仅在 retention 启用时存在。多租户环境用X-Scope-OrgID请求头指定租户,完整参数见官方 HTTP API 文档。
提交:删除请求的 curl 写法
通过POST /loki/api/v1/delete创建删除请求。参数要点:
query(必填):LogQL 流选择器,可带行过滤器,需做 URL 编码start/end(必填):Unix 秒或 RFC3339 两种格式都行;不允许删除未来时间,且start必须小于endmax_interval(可选):单个请求允许的最大跨度,不能超过delete_max_interval,合法单位s、m、h
下面这条命令提交一个删除请求,-g参数用于关掉 curl 对花括号的大括号展开:
curl -g -X POST \ 'http://loki-compactor:3100/loki/api/v1/delete?query={cluster="prod"}&start=1704067200&end=1704153600' \ -H 'X-Scope-OrgID: tenant1'成功返回204 No Content,响应头X-Delete-Request-ID里带着请求 ID,取消时要用。表达式非法(比如正则写错)会在提交时立刻拿到 400,不会拖到执行期才暴露问题。
跟踪:查询删除请求状态
GET /loki/api/v1/delete按创建时间排序返回该租户的全部删除请求,支持两个可选过滤参数:
for_querytime_filtering=true:只返回与查询时过滤相关的那部分start+end:只返回与给定时间范围有交集的请求
这条命令列出指定时间范围内的所有删除请求:
curl -X GET \ 'http://loki-compactor:3100/loki/api/v1/delete?start=1704067200&end=1704153600' \ -H 'X-Scope-OrgID: tenant1'被拆分过的同一请求会以一条记录合并展示,状态字段取Received、Processed或N% Complete三种之一,数字就是已完成子请求的占比。
取消:Loki 删除请求取消方法
DELETE /loki/api/v1/delete?request_id=<ID>用于撤回请求:
- 取消期(cancellation period,即提交后到真正删除前的保护窗口,由
delete_request_cancel_period控制,默认24h)内随时可撤 - 请求已被处理或已完成时,默认返回 400 拒绝
force=true的适用场景:请求已经开始处理,或已超过取消期。注意强制撤回后,已删掉的那部分数据不会恢复,请求状态仍会显示为已处理
带强制参数的取消命令:
curl -X DELETE \ 'http://loki-compactor:3100/loki/api/v1/delete?request_id=<REQUEST_ID>&force=true' \ -H 'X-Scope-OrgID: tenant1'成功同样返回204。撤掉之后,请求会从存储中移除,GET结果里不再出现。
缓存失效端点
删除之后,查询结果缓存可能还留着旧数据,Loki 用缓存代数(cache generation number,用来判断查询结果缓存是否过期)解决:
GET /loki/api/v1/cache/generation_numbers:查看租户当前代数POST /loki/api/v1/cache/generation_numbers/increase:手动递增,让该租户的查询缓存全部失效,回放历史数据这类场景会用到;删除完成后代数会自动递增,无需手动干预
请求在背后经历了什么
提交只是记了一笔账,真正的执行是一条五步流水线:
- 周期扫描:compactor 在每个 retention 周期(
apply_retention_interval,默认与压缩周期一致,并额外加上不超过 10 分钟的抖动,避免和压缩撞车)扫一遍所有未处理的删除请求 - 取消期过滤:只有创建时间已超过
delete_request_cancel_period(默认 24h)的请求才进入删除阶段,保护窗口在这里兜底 - 批量执行:每个周期最多处理
delete_batch_size(默认 70)个请求,防止一个大请求拖死整条队列 - chunk 重建写回:
filter-and-delete模式下,把相关 chunk 读出来、剔掉匹配行、重建后再传回对象存储并更新索引;这一步之前还有一层retention_delete_delay(默认 2h)缓冲 - 缓存失效:处理完成后自动递增缓存代数,后续查询不会再命中已删数据的旧缓存
分片机制只作用于带行过滤器的请求:每个分片最多覆盖delete_max_interval(默认 24h),相邻子请求之间刻意保留少量时间重叠,避免毫秒级边界上的日志漏删;不带行过滤器的请求原样执行,不拆分。
存储层与迁移:删除请求存在哪里
删除请求本身也要持久化,它由两个不同维度的配置共同决定:
delete_request_store:对象存储位置,即请求数据文件所在的桶与路径前缀(默认index/)delete_request_store_db_type:本地数据库引擎,默认boltdb,可换成sqlite
想把引擎从 boltdb 迁到 sqlite 时,先给备份库开双写:请求同时落进旧的 boltdb 存储,迁移期间一条都不丢;切换完成后把备份配置去掉。备份库目前只支持 boltdb。迁移期的三行配置:
compactor: delete_request_store_db_type: sqlite backup_delete_request_store_db_type: boltdb两种引擎的实现分别放在 delete_requests_db_boltdb.go 和 delete_requests_db_sqlite.go,配套测试齐全,想深入细节可以直接读源码。
避坑清单
- ⚠️对象存储先开版本控制。retention 或保留期一旦配错,版本历史是你唯一能回头的路。
- ⚠️先 filter-only 试跑再物理删除。在查询层确认命中范围无误,再切
filter-and-delete,避免一上来就删错。 - ⚠️带行过滤器的删除非常吃 CPU 和 IO——读 chunk、剔行、重写、回传,一步不少。批量删大量数据时,按compactor 水平扩展文档把负载摊到多个 compactor 实例上。
- ⚠️用
max_interval控制单请求跨度。别把一个请求跨好几天,执行时间会拖到失控。 - ⚠️盯住
loki_compactor_deletion_*指标。请求积压、删除行数、失败数都在这组指标里,发现异常及时扩容或拆请求。
把retention_enabled、delete_request_store、deletion_mode三件套配齐之后,Grafana Loki 的日志删除就是一个完全可预期的流程:提交请求、享受保护窗口、由 compactor 在后台分片并批量执行、最后在查询层验证干净。真正不可逆的只有物理删除这一步——开版本控制、先 filter-only 试跑、盯紧指标,这三件事做到位,这项能力就可以放心用了。
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考