feat(deployments,analysis): 保留弹框重设计 + 分析页命名空间筛选 + 过期 chunk 真正删除
=== 一、保留策略弹框重设计(RetentionDialog)===
旧版问题:
1. 主输入是 YAML textarea,用户必须会写 `limits_config.retention_period: 30d`
2. 三个按钮:取消 / 仅保存 / 保存并应用 —— 「仅保存」只改部署记录不重载 Loki,
今天已经造成一次真实故障:DB 记录 7 天、运行时 ConfigMap 是 24h,配置漂移
3. 「查看完整配置」是独立按钮 + 独立状态,多一次点击
4. 硬编码提示 `retention_delete_delay: 2h`,而实际已改成 10m,属于误导
新版:
- 主输入改为**保留天数数字框**(1–3650),YAML 由前端拼,用户不用写
- 快捷预设 1/7/14/30/90,实时显示「= 720 小时」
- >7 天给出存储成本提示
- 完整运行 config 改成 `<details>` 折叠,展开即自动加载(去掉独立按钮)
- **按钮只剩 2 个:取消 / 保存并应用**
- 删掉硬编码的 compactor 提示块
=== 二、日志分析页加命名空间筛选 ===
根因:analysis 页写死 namespace: '',等价 `{namespace=~".+"}` —— 一次分析扫全集群
(实测约 370 GB/天),5 条 count_over_time 跑 30s 都出不来,页面永远转圈。
- 新增命名空间下拉(从 Loki label values 拉取,localStorage 记忆)
- 默认取业务命名空间 astralx(其次列表首个),不再默认全集群
- 「全部命名空间(较慢)」降级为显式选项
- 命名空间未就绪时先显示 loading,避免先发一次全集群查询
- 5 条查询改**分批(每批 2 条)**执行 + 429 自动重试一次
=== 三、后端并发/超时调整 ===
- 重查询并发上限 2 → 4,且拿不到令牌时**排队最多 10s**再拒绝
(原来立即 429,导致分析页 5 条并发里有 3 条直接失败)
- maxLogQueryLimit 1000 → 5000,与前端下拉完全对齐(原来前端选 5000 会被静默截断)
=== 四、过期 chunk 真正删除(关键修复)===
实测发现:索引表已从 COS 删除,但 chunk 对象仍残留 21,354 个 / 24.8 GB,
最老的 45 小时前 —— 因为 Loki 默认 `retention_delete_delay` = **2h**,
标记后要等 2 小时才真正删 chunk。
- 配置显式设置 `compactor.retention_delete_delay: 10m`
- 已用 `loki -verify-config` 校验(config is valid)
- 删除前后对比:
对象数 46,536 → 23,934;占用 53.61 GB → 27.25 GB
超期对象 21,354 个 / 24.8 GB → **0 个**
桶内最老对象 45.2h → 24.4h(= 保留期 1 天)
- deployment_config_test 同步调整:retention_delete_delay 从"禁止出现"移入"必须存在"
镜像:lghost/lokistack:latest + v0.1 = sha256:b4d98e016bca
This commit is contained in:
1 parent
53c7d64b35
commit
a32f93f533
12 files changed
+265
-97
No files matched your search
@@ -67,12 +67,15 @@ func TestGeneratedLokiConfigKeepsRequiredSettingsWithoutDefaults(t *testing.T) {
|
||||
// loki_config.go 里对 s3/oss/minio/cos/obs 刻意把 compactor 的删除请求存到
|
||||
// 本地盘(见该处注释),避免各云 S3 兼容实现差异导致 compactor 启动失败。
|
||||
// loki_object_store_test.go 断言的也是 filesystem —— 两处必须一致。
|
||||
for _, required := range []string{"retention_period: 2160h", "retention_enabled: true", "delete_request_store: filesystem", "bucketnames: logs", "store: tsdb", "schema: v13"} {
|
||||
for _, required := range []string{"retention_period: 2160h", "retention_enabled: true", "delete_request_store: filesystem", "retention_delete_delay: 10m", "bucketnames: logs", "store: tsdb", "schema: v13"} {
|
||||
if !strings.Contains(config, required) {
|
||||
t.Fatalf("required setting %q missing", required)
|
||||
}
|
||||
}
|
||||
for _, defaultSetting := range []string{"http_listen_port:", "grpc_listen_port:", "chunk_idle_period:", "compaction_interval:", "retention_delete_delay:", "max_entries_limit_per_query:", "frontend_worker:", "chunk_store_config:", "autoforget_unhealthy:"} {
|
||||
// 注意:retention_delete_delay 已从"不该出现"清单移除 —— 现在**有意**配置为 10m。
|
||||
// 默认 2h 会让对象存储里多留 2 小时数据(实测桶内超期对象一度占 24.6GB),
|
||||
// 预发环境用 10m 既保留安全窗口又不白付存储费。
|
||||
for _, defaultSetting := range []string{"http_listen_port:", "grpc_listen_port:", "chunk_idle_period:", "compaction_interval:", "max_entries_limit_per_query:", "frontend_worker:", "chunk_store_config:", "autoforget_unhealthy:"} {
|
||||
if strings.Contains(config, defaultSetting) {
|
||||
t.Fatalf("unnecessary setting %q remained", defaultSetting)
|
||||
}
|
||||
|
||||
@@ -23,10 +23,11 @@ import (
|
||||
"lokistack/repository"
|
||||
)
|
||||
|
||||
// maxLogQueryLimit 单页硬上限。依据:实测每条日志约 671B——
|
||||
// 1000 条 ≈ 655KB 响应;而前端结果表**没有虚拟滚动**,1000 行会全部渲染成
|
||||
// DOM 节点导致卡顿。前端下拉只给到 1000,超过会被这里静默收敛。
|
||||
const maxLogQueryLimit = 1000
|
||||
// maxLogQueryLimit 单页硬上限,与前端下拉选项保持**完全一致**,
|
||||
// 避免"前端能选 5000、后端静默截成 1000"这种假选项。
|
||||
// 取值 5000 = Loki limits_config.max_entries_limit_per_query 的默认上限;
|
||||
// 前端结果表已加虚拟滚动(>100 条只渲染可视区),5000 行不会卡。
|
||||
const maxLogQueryLimit = 5000
|
||||
const logQueryTimeout = 120 * time.Second
|
||||
|
||||
var lokiHTTPClients = [2]*http.Client{
|
||||
@@ -417,8 +418,13 @@ const (
|
||||
maxHistogramRange = 7 * 24 * time.Hour
|
||||
// 重查询(histogram/metrics)单独的更短超时,避免长时间占用 CPU
|
||||
heavyQueryTimeout = 30 * time.Second
|
||||
// 全局并发重查询上限:超过直接快速失败,保护单实例 Loki
|
||||
maxConcurrentHeavyQueries = 2
|
||||
// 全局并发重查询上限。取 4 而不是 2:
|
||||
// 日志分析页一次会并发 5 条 count_over_time,限制 2 会让大部分请求拿不到令牌。
|
||||
maxConcurrentHeavyQueries = 4
|
||||
// 拿不到令牌时最多排队 10 秒再拒绝。
|
||||
// 原来"立即 429"会把分析页 5 条并发里的 3 条直接打回,
|
||||
// 用户看到的就是"当前有较多日志量统计在进行"。
|
||||
heavyQueryWait = 10 * time.Second
|
||||
)
|
||||
|
||||
// ErrTooManyQueries 并发重查询超限(handler 映射为 429)
|
||||
@@ -427,11 +433,20 @@ var ErrTooManyQueries = errors.New("当前有较多日志量统计在进行,
|
||||
// heavyQuerySem 全局重查询信号量
|
||||
var heavyQuerySem = make(chan struct{}, maxConcurrentHeavyQueries)
|
||||
|
||||
// acquireHeavyQuery 先尝试立即获取;拿不到则最多排队 heavyQueryWait。
|
||||
// 突发(如分析页并发 5 条)会自然排队消化,只有持续超载才返回 429。
|
||||
func acquireHeavyQuery() bool {
|
||||
select {
|
||||
case heavyQuerySem <- struct{}{}:
|
||||
return true
|
||||
default:
|
||||
}
|
||||
timer := time.NewTimer(heavyQueryWait)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case heavyQuerySem <- struct{}{}:
|
||||
return true
|
||||
case <-timer.C:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ compactor:
|
||||
working_directory: /var/lib/loki/compactor
|
||||
retention_enabled: true
|
||||
delete_request_store: {{ .CompactorStoreType }}
|
||||
# 过期 chunk 的「标记 → 真正删除」间隔。默认 2h 会让对象存储里
|
||||
# 多留 2 小时的数据(实测桶内超过保留期的对象一度占到 24.6 GB)。
|
||||
# 预发环境取 10m:既保留短暂的安全窗口,又不会长期占用存储成本。
|
||||
retention_delete_delay: 10m
|
||||
|
||||
limits_config:
|
||||
retention_period: {{ .RetentionPeriod }}h
|
||||
|
||||
Reference in new issue
Block a user