Files
story/docs/alert-operations.md
T

249 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 告警监测与运营处置开发说明
> 对应数据库迁移:V22、V25
> 对应接口契约:OpenAPI 0.55.0
> 适用范围:客户组织级业务、技术、安全和费用告警的发现、路由、升级、静默及人工闭环
## 1. 建设目标与边界
告警模块将已有Prometheus基础规则之外、需要关联业务资源和人工处置的信号统一落入运营闭环。平台按客户组织隔离信号,把同一问题的重复命中归并为一个事件,冻结当时有效的接收组和运行手册,并记录确认、缓解、关闭、静默、重新打开和升级动作。
当前版本已经覆盖数据库、后台扫描器、运营接口、React工作台、可靠Webhook通知适配器、不会生成正式告警的接收器联通测试和本地端到端验证。`receiver_group`和`escalation_receiver_group`仍是受审计的逻辑接收组,接收端可据此映射短信、电话、PagerDuty或企业协作平台;真实通知网关账号、地址和值班组映射需在部署环境配置。PostgreSQL或Redis自身完全不可连接时,服务无法把故障写入同一主库,因此这类基础设施失联仍由Prometheus/部署平台外部探针告警,不能依赖本模块自举。
阶段8使用`cmd/alert-receiver-conformance`把全部活动路由、联通测试、真实网关回执、主/升级值班组映射和一次受控正式告警演练形成候选一致的结构化报告。详细采集格式、10项校验和证据包门禁见[真实告警接收器一致性证据](alert-receiver-conformance-evidence.md)。
## 2. 功能设计
### 2.1 告警分类与级别
| 维度 | 取值 | 用途 |
|---|---|---|
| 分类 | `BUSINESS` | 隐私删除、录音上传、业务任务等需要业务确认的问题 |
| 分类 | `TECHNICAL` | 死信、语音任务卡滞、固件失败等技术处置问题 |
| 分类 | `SECURITY` | 证书、异常访问、密钥或安全策略问题 |
| 分类 | `COST` | 额度临界、异常费用增长和供应商核价问题 |
| 级别 | `CRITICAL/HIGH/MEDIUM/LOW` | 决定队列排序、响应时限和升级路径 |
### 2.2 当前自动扫描规则
| 规则代码 | 分类/级别 | 判定依据 | 关联资源 |
|---|---|---|---|
| `OUTBOX_DEAD_LETTER` | 技术/高 | 客户组织存在`dead`可靠任务 | Outbox队列 |
| `PRIVACY_DELETE_OVERDUE` | 业务/严重 | 删除请求部分失败,或创建超过1小时仍未完成 | 隐私请求 |
| `MEDIA_UPLOAD_FAILED` | 业务/中 | 双轨录音上传批次进入失败终态 | 上传批次 |
| `FIRMWARE_ROLLOUT_FAILURE` | 技术/高 | 固件灰度存在失败设备 | 固件灰度批次 |
| `COST_DAILY_QUOTA_NEAR` | 费用/中 | 当前账期日费用达到活动硬额度的80% | 额度策略 |
| `VOICE_TASK_STUCK` | 技术/高 | 通话在启动或停止中间态停留超过5分钟 | 通话 |
| `PROVIDER_NOTIFICATION_BACKLOG` | 技术/高 | 供应商通知处于待处理/失败且超过1分钟,按客户组织和供应商归并 | 供应商通知队列 |
| `PROVIDER_UNAVAILABLE` | 技术/严重 | 连续临时失败达到阈值,供应商启动熔断处于不可用状态 | 供应商依赖状态 |
| `CERTIFICATE_EXPIRING` | 安全/高或严重 | 活动证书引用在30天内到期;7天内提升为严重 | 受控证书引用 |
扫描器随Worker进程启动,默认使用`WORKER_POLL_INTERVAL`。每次扫描只读取规范业务表,生成脱敏标题和摘要,不把供应商任务正文、录音、字幕、密钥或完整请求正文复制到告警表。
供应商临时错误由 Worker 按网络错误、超时、HTTP 429 和 HTTP 5xx 分类。连续失败达到`PROVIDER_FAILURE_THRESHOLD`后,`provider_dependency_state`按客户组织和供应商持久化为`UNAVAILABLE`,在`PROVIDER_CIRCUIT_COOLDOWN`窗口内暂停新的语音任务启动;停止和异常回收不经过启动熔断,仍会继续尝试。任一成功的启动或停止会清零连续失败并恢复`AVAILABLE`。告警扫描器对`UNAVAILABLE`状态生成`PROVIDER_UNAVAILABLE` CRITICAL事件,摘要只包含供应商代号、失败次数和归一化错误码。
### 2.3 路由策略
- 路由按`客户组织 + 分类 + 级别`版本化,同一组合只有一个`ACTIVE`版本。
- 新版本由`ops_approver`或`ops_admin`激活,旧版本自动变为`RETIRED`。
- 策略保存首接组、响应时限、升级时限、升级接收组、运行手册引用和变更说明。
- 事件首次发现时保存活动路由内部引用;后续策略升级不会改写历史处置依据。
- 如果事件首次发现时尚无路由,事件仍进入队列;后续重复命中可补挂当时有效的路由。
### 2.4 告警归并与状态机
同一客户组织内使用`fingerprint`归并仍未关闭的重复信号。扫描器通过事务级顾问锁串行化同一指纹,避免多Worker同时插入两个活动事件。重复命中只增加`occurrence_count`、刷新最后发现时间和摘要,不生成重复费用或重复业务资源。
```mermaid
stateDiagram-v2
[*] --> OPEN: 首次命中且无静默
[*] --> SILENCED: 首次命中且匹配静默
OPEN --> ACKNOWLEDGED: 运营确认并接手
OPEN --> MITIGATED: 直接记录缓解
ACKNOWLEDGED --> MITIGATED: 完成缓解
OPEN --> RESOLVED: 验证后关闭
ACKNOWLEDGED --> RESOLVED: 验证后关闭
MITIGATED --> RESOLVED: 验证后关闭
OPEN --> SILENCED: 后续命中有效静默
ACKNOWLEDGED --> SILENCED: 后续命中有效静默
MITIGATED --> SILENCED: 后续命中有效静默
SILENCED --> OPEN: 静默到期或撤销且信号仍存在
RESOLVED --> [*]
```
关闭后的相同问题再次发生时创建新的事件,而不是改写已关闭事件。这样每次故障窗口、首次发现、响应和恢复证据都可独立复盘。
### 2.5 超时升级
扫描器在每轮检测后查找`OPEN`或`ACKNOWLEDGED`且尚未升级的事件。若`first_detected_at + escalation_after_minutes`已到期,则原子写入`escalated_at`并追加`ESCALATED`动作。升级不改变业务状态,避免“已升级”被误认为“已缓解”。
### 2.6 静默
- 静默至少按规则代码、分类或具体资源之一限定,不能创建全平台无条件静默。
- 时间窗口必须有效,最长30天,原因必填。
- 静默不会删除事件或停止命中计数;首次命中仍写`DETECTED`和`SILENCED`动作。
- 对活动事件新增静默后,下次命中转为`SILENCED`;静默到期或撤销且问题仍存在时转回`OPEN`并写`REOPENED`。
- 撤销使用强`If-Match`,防止两个运营人员基于旧页面重复操作。
### 2.7 Webhook通知
- 未静默的新告警在写入事件和`DETECTED`动作的同一事务中生成`alert.webhook_requested`可靠任务;静默中的首次命中不对外通知。
- 超时升级在写入`ESCALATED`动作的同一事务中生成第二条通知,接收组切换为`escalation_receiver_group`。
- 通知正文只使用客户组织、事件和业务资源的公开编号,并携带规则、分类、级别、状态、脱敏标题/摘要、逻辑接收组和运行手册引用,不暴露内部主键、令牌、密钥、字幕或录音正文。
- Worker以稳定`event_id`投递,网络错误、超时、HTTP 408/425/429/5xx进入统一指数退避重试,其他4xx作为永久配置错误进入死信。
- 每次请求携带`X-Voice-Event-ID`、Unix秒`X-Voice-Timestamp`和`X-Voice-Signature`。签名格式为`v1=HMAC-SHA256(secret, timestamp + "." + raw_body)`;接收端必须校验签名、限制时间偏差并按事件编号去重。
- `ALERT_WEBHOOK_URL`为空时适配器安全关闭,任务由Worker正常确认,不访问外部网络,也不形成待处理或死信积压;启用时密钥至少32字符,生产环境强制HTTPS和非占位密钥。
- 运营人员可以针对任一活动路由发起`alert.webhook_test_requested`。测试使用同一Worker、目标地址、HMAC密钥、超时和错误分类,但`aggregate_type=alert_delivery_test`,不会写入`alert_incident`或触发正式告警升级。
- 普通告警在地址未配置时仍保持安全空操作;联通测试在地址未配置时明确进入`FAILED`,避免把“没有发送”误报为“接收器可用”。失败任务使用公开任务编号进入现有诊断受控重试链路。
## 3. 数据库设计
### 3.1 表及职责
| 表 | 主体字段 | 约束与用途 |
|---|---|---|
| `alert_routing_policy` | 分类、级别、版本、接收组、时限、运行手册 | 活动版本部分唯一;历史版本不可覆盖 |
| `alert_silence` | 规则/分类/资源范围、匹配器、原因、起止时间、撤销人 | 至少一个范围;结束晚于开始;服务层限制最长30天 |
| `alert_incident` | 规则、指纹、状态、摘要、资源、路由、负责人、时间和次数 | 未关闭指纹部分唯一;保存响应、缓解、关闭、静默和升级时间 |
| `alert_action` | 动作、前后状态、系统/用户操作人、说明、时间 | 随事件级联删除;构成不可变处置时间线 |
| `ops_resource_reference`(V25扩展) | `resource_type=CERTIFICATE`、受控引用、核验时间、到期时间 | 不保存证书私钥或正文;活动证书必须有未来到期时间 |
| `outbox_event`(V26扩展) | 公开任务编号、事件类型、测试关联号、状态、尝试与人工重试次数 | 联通测试复用可靠任务台账,不新增业务表;测试原因只进审计,不进入外发正文 |
所有表使用内部UUID关联,HTTP只暴露`public_id`。每张租户数据表都保存`tenant_id`,所有服务查询先用OIDC身份和客户组织公开编号解析运营账号,再固定内部客户组织条件;不接受前端传入内部客户组织编号。
### 3.2 关键索引
- `alert_route_active_unique`:保证一个分类/级别只有一个活动路由。
- `alert_silence_active_idx`:加速客户组织当前静默窗口判断。
- `alert_incident_active_fingerprint`:保证同一问题只有一个未关闭事件。
- `alert_incident_queue_idx`:按状态、严重度和最后发现时间查询运营队列。
- `alert_action_incident_time_idx`:按事件顺序读取完整动作时间线。
## 4. 接口设计
所有运营接口均要求OIDC Bearer Token和`X-Tenant-Id`;创建接口要求`Idempotency-Key`,状态变更要求`If-Match: "v{row_version}"`。
| 方法与路径 | 最低角色 | 说明 |
|---|---|---|
| `GET /api/v1/ops/alert-routing-policies` | `ops_viewer` | 查询活动和历史路由 |
| `POST /api/v1/ops/alert-routing-policies` | `ops_approver` | 激活新路由版本,支持幂等重放 |
| `GET /api/v1/ops/alert-delivery-tests` | `ops_viewer` | 查询最近50次联通测试及真实投递状态,不返回地址、密钥或测试原因 |
| `POST /api/v1/ops/alert-routing-policies/{id}/delivery-tests` | `ops_operator` | 针对活动路由创建受审计、幂等的联通测试,不创建正式告警 |
| `GET /api/v1/ops/alerts` | `ops_viewer` | 查询队列,可按状态筛选 |
| `GET /api/v1/ops/alerts/{id}` | `ops_viewer` | 查询事件详情和完整动作时间线 |
| `POST /api/v1/ops/alerts/{id}/acknowledge` | `ops_operator` | 确认并把首次操作人记为当前负责人 |
| `POST /api/v1/ops/alerts/{id}/mitigate` | `ops_operator` | 记录已采取缓解措施 |
| `POST /api/v1/ops/alerts/{id}/resolve` | `ops_operator` | 验证恢复并关闭事件 |
| `GET /api/v1/ops/alert-silences` | `ops_viewer` | 查询当前和历史静默 |
| `POST /api/v1/ops/alert-silences` | `ops_operator` | 创建有范围、有原因、有限时长的静默 |
| `POST /api/v1/ops/alert-silences/{id}/revoke` | `ops_operator` | 提前撤销静默 |
稳定错误包括:`OPS_ACCESS_DENIED`、`ALERT_NOT_FOUND`、`ALERT_STATE_CONFLICT`、`VERSION_CONFLICT`、`IDEMPOTENCY_KEY_CONFLICT`。内部数据库错误只在服务端结构化日志记录,客户端不返回SQL或内部编号。
## 5. 业务流程
### 5.1 自动发现到关闭
```mermaid
sequenceDiagram
participant S as Worker扫描器
participant DB as PostgreSQL
participant W as Outbox Worker
participant R as 通知接收端
participant O as 运营人员
S->>DB: 读取业务表并生成脱敏Signal
S->>DB: 指纹加锁、检查活动路由和静默
S->>DB: 新建事件或增加命中次数
S->>DB: 超时则追加ESCALATED动作
S->>DB: 同事务写可靠通知任务
W->>DB: 领取通知任务
W->>R: HMAC签名Webhook
R-->>W: 2xx确认或临时失败重试
O->>DB: 查询队列和运行手册
O->>DB: If-Match确认并接手
O->>DB: If-Match记录缓解
O->>DB: If-Match验证并关闭
DB-->>O: 返回最新版本及完整动作时间线
```
### 5.2 静默与恢复
1. 运营人员选择明确规则、分类或资源,填写原因和结束时间。
2. 服务端校验角色、范围、时间和幂等键,在同一事务写静默与审计。
3. 扫描器继续检测信号并保留命中次数,但把事件放入`SILENCED`。
4. 静默到期或被带版本撤销后,如果信号仍存在,扫描器将事件重新打开并追加动作。
5. 若信号已经消失,事件保留静默历史,不自动伪造“已恢复”;运营人员根据复核结果关闭。发布候选快照会提供阻断告警公开编号和关联资源`PRESENT/MISSING/AGGREGATE/UNKNOWN`状态,帮助复核,但`MISSING`本身不会绕过人工关闭责任。
### 5.3 接收器联通测试
```mermaid
sequenceDiagram
participant O as 运营人员
participant A as 运营API
participant DB as PostgreSQL/Outbox
participant W as Worker
participant R as 告警接收器
O->>A: 选择活动路由并填写测试原因
A->>DB: 幂等写测试任务和最小审计
A-->>O: 返回PENDING与test_ref
W->>DB: 领取alert.webhook_test_requested
W->>R: 与正式告警相同的HMAC签名信封
R-->>W: 2xx或可分类错误
W->>DB: DELIVERED或FAILED
O->>A: 查询最近测试状态
A-->>O: 尝试次数、错误码和完成时间
```
外发正文固定带`action=TEST`、测试关联号、客户组织公开编号、活动路由公开编号、分类、级别、接收组、运行手册和“非真实故障”说明,不携带操作人填写的测试原因。失败任务只有在配置或网络问题已复核后才能由运营操作角色受控重试。
## 6. 工作台设计
运营工作台包含五块信息:待处置/严重/已升级/活动静默概览;按状态筛选的告警队列;包含路由、运行手册、资源和请求关联号的详情及动作时间线;路由版本和临时静默管理;最近50次接收器联通测试及失败重试。只读运营角色不会看到写操作按钮,审批角色才可激活路由,操作角色可处置、静默、发起联通测试和重试失败任务。Grafana安全仪表盘同时展示7天/30天证书到期数量和当前安全事件,技术仪表盘展示供应商通知积压与可靠任务状态。
确认接手、记录缓解和验证关闭从告警详情打开页面内处置表单,不再调用浏览器输入弹窗。表单固定告警公开编号、规则、分类、级别、当前状态、命中次数和行版本,处置说明必填且最长1000字符;关闭还必须确认缓解措施已经验证、监测指标恢复、关联影响和遗留风险完成复核。重新筛选、切换客户组织、选择其他告警、刷新或取消会清除处置草稿,不发送状态请求;服务端仍执行角色、租户、状态及`If-Match`并发校验。
接收器联通测试、失败测试受控重试和静默提前撤销也统一使用页面内受控操作区。联通测试固定活动路由编号、分类、级别、版本、接收组、运行手册和行版本,要求确认接收组知晓测试,且原因仅进入审计、不外发;失败重试固定任务编号、测试关联号、尝试次数、人工重试次数和稳定错误码,要求确认依赖问题已经修复;静默撤销固定静默编号、范围、原因、窗口和行版本,要求确认仍在发生的信号会重新进入队列。三类原因最长500字符,刷新、切换客户组织或取消会清除草稿。
## 7. 验证与运行
执行后端、契约和前端门禁:
```powershell
.\scripts\check.ps1
```
执行真实OIDC、PostgreSQL、API和Worker组合烟测:
```powershell
pwsh -NoProfile -File .\scripts\smoke-alerts.ps1
```
该脚本验证跨客户组织拒绝、路由幂等、自动发现、首次通知接收组、路由冻结、超时升级及升级通知接收组、旧ETag返回412、确认—缓解—关闭时间线、静默命中、撤销静默和重新打开,并在结束后清理全部合成数据及通知任务。
日常页面增量采用低强度真实OIDC无提交检查:`test/e2e/visual-ops-alert-transitions.mjs`使用三条分别处于`OPEN`、`ACKNOWLEDGED`和`MITIGATED`的临时告警,验证确认、缓解和关闭表单的目标/行版本绑定、空说明阻断、关闭二次确认、取消关闭、零状态POST、零横向溢出、零控制台错误和零非预期失败请求。截图保存到`test/e2e/screenshots/ops-alert-transition-form.png`,夹具在`finally`中精确清理。该检查证明页面不会误处置,不替代既有服务端状态机纵向烟测。
剩余三类告警操作复用一条真实OIDC无提交检查:`test/e2e/visual-ops-alert-controlled-actions.mjs`使用临时活动路由、活动静默和失败联通任务,验证目标/版本绑定、原因与影响确认门禁、取消关闭,以及零测试发送、零重试、零静默撤销请求;同时检查零横向溢出、零控制台错误和零非预期失败请求。截图为`test/e2e/screenshots/ops-alert-controlled-actions.png`,路由、静默和死信任务夹具在`finally`中精确清理。
执行接收器真实投递纵向烟测:
```powershell
pwsh -NoProfile -File .\scripts\smoke-alert-delivery-test.ps1
```
该脚本在没有活动路由时临时建立一条路由,通过真实OIDC创建联通任务,等待真实Worker出队,验证本地接收器HMAC签名、状态回读、非正式告警隔离和幂等重放,最后清理路由、Outbox、审计和幂等记录。
执行Chrome布局检查:
```powershell
$env:PLAYWRIGHT_REQUIRE_FROM='C:\Users\19926\.cache\codex-runtimes\codex-primary-runtime\dependencies\node\node_modules'
node .\scripts\visual-alert-ui.cjs
```
## 8. 生产接入待办
- 配置真实Webhook通知网关,把逻辑接收组映射到PagerDuty、短信、电话或企业协作平台;先从活动路由发起联通测试并确认`DELIVERED`和接收端唯一事件,再在预生产环境验证正式告警的去重、送达、限流和失败重试。
- 将异常访问、密钥轮换失败和核价失败接入规范Signal;证书到期、供应商不可用和供应商通知积压已经接入。
- 在类生产环境校准规则阈值、扫描周期、事件量和存储保留期。
- 使用外部探针验证PostgreSQL、Redis、对象存储和区域级故障;主库失联不能仅依赖主库内事件。
- 完成值班表、接收组映射、每条规则运行手册和故障演练签署。