From c5739d1a06e737932fdeaeb751087e7241002bfe Mon Sep 17 00:00:00 2001 From: opsx Date: Thu, 29 Oct 2026 13:14:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(alerts):=20=E5=86=85=E7=BD=AE=E8=A7=84?= =?UTF-8?q?=E5=88=99=E9=BB=98=E8=AE=A4=E5=90=AF=E7=94=A8=E5=B9=B6=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E9=BB=98=E8=AE=A4=E9=80=9A=E9=81=93,=E5=BC=80?= =?UTF-8?q?=E7=AE=B1=E5=8D=B3=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新种规则 Enabled: true;启动时执行 SeedAlertRules(此前仅部署成功调用,存量环境拿不到升级) - 存量一次性迁移:内置名单内且从未关联通道的规则补绑默认通道并启用;用户已自配通道的不动 - 管理员只需给「默认告警」通道填 Webhook 并启用;通道未就绪时命中只记发送历史(failed+原因),不影响规则评估 --- backend/main.go | 2 ++ backend/seed/alert_rules.go | 33 +++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/backend/main.go b/backend/main.go index 57c00e0..14b732a 100644 --- a/backend/main.go +++ b/backend/main.go @@ -138,6 +138,8 @@ func main() { log.Println("Seeding default data...") seed.Seed() + // 补齐/升级内置告警规则(幂等):启动即执行,存量环境也能拿到「默认启用+绑默认通道」 + seed.SeedAlertRules() // 安全迁移:历史明文 kubeconfig → 加密存储,并清理明文文件 services.MigrateClusterKubeconfigs() diff --git a/backend/seed/alert_rules.go b/backend/seed/alert_rules.go index f8dda6b..02e8d3f 100644 --- a/backend/seed/alert_rules.go +++ b/backend/seed/alert_rules.go @@ -4,7 +4,8 @@ package seed // 致命类(出现即报):FATAL / panic / OutOfMemoryError / StackOverflowError // 突增类(超阈值才报):应用 error 过多 / ingress-nginx 4xx / 5xx // 绑定数据源:部署成功自动注册的 LogSource(managed)。 -// 规则默认不启用(通知通道由管理员后期在设置页配置后再开启),ChannelIDs 留空。 +// 规则默认启用并绑定「默认告警」通道:管理员只需在设置页给默认通道填好 Webhook 并启用即可收告警; +// 通道未就绪时命中只会把失败原因写进发送历史,不影响规则评估。 import ( "slices" "time" @@ -96,17 +97,34 @@ var legacyInfraRules = []string{"Kubernetes 节点 NotReady", "容器 OOMKilled" // SeedAlertRules 补齐内置告警规则(幂等;公开给启动与部署成功两处调用) func SeedAlertRules() { - // 先清掉历史遗留的基础设施类规则(只删名字精确匹配的这两条) + // 先清掉历史遗留的基础设施类规则,并把内置规则升级为「默认启用 + 绑默认通道」 + // (仅处理从未关联过通道的规则;用户已自行配置过通道的规则不动) db := repository.GetDB() + defaultChannelID := ensurePlaceholderChannel(db) + builtinNames := make(map[string]bool, len(builtinRules)) + for _, r := range builtinRules { + builtinNames[r.name] = true + } db.Lock() kept := db.AlertRules[:0] + changed := false for _, rule := range db.AlertRules { - if !slices.Contains(legacyInfraRules, rule.Name) { - kept = append(kept, rule) + if slices.Contains(legacyInfraRules, rule.Name) { + changed = true + continue } + if builtinNames[rule.Name] && len(rule.ChannelIDs) == 0 { + rule.ChannelIDs = channelIDsFor(defaultChannelID) + if !rule.Enabled { + rule.Enabled = true + rule.UpdatedAt = time.Now() + } + changed = true + } + kept = append(kept, rule) } - if len(kept) != len(db.AlertRules) { - db.AlertRules = kept + db.AlertRules = kept + if changed { db.RequestSave() } db.Unlock() @@ -141,7 +159,6 @@ func SeedAlertRules() { // - 管理员填好 Webhook 再启用即可,不必逐条给规则关联通道 // - 之所以不放"已启用 + 空地址":那样每次命中都会投递失败刷屏 // 这里直接操作 db 而不调 services,避免 seed ↔ services 的包循环依赖。 - defaultChannelID := ensurePlaceholderChannel(db) newID := db.NextID("alertRules") db.Lock() @@ -154,7 +171,7 @@ func SeedAlertRules() { db.AlertRules = append(db.AlertRules, models.AlertRule{ ID: newID, Name: r.name, SourceID: target.id, Query: r.query, WindowMin: r.windowMin, Threshold: r.threshold, - Enabled: false, CooldownMin: r.cooldown, + Enabled: true, CooldownMin: r.cooldown, ChannelIDs: channelIDsFor(defaultChannelID), CreatedAt: time.Now(), UpdatedAt: time.Now(), })