文件

93 行
7.2 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 企微公网健康告警防抖与诊断增强
## Goal
让 ARM 公网健康探测准确区分 DNS、TCP、TLS、HTTP 和响应合同故障,以明确来源的企业微信文案报告真实事件,并在链路反复抖动时减少无意义的“告警—恢复”消息,同时保持约 10 分钟量级的持续 OA 不可用发现能力。
## Background and Confirmed Facts
- 上一排查任务已确认群内反复消息来自 ARM 对公网 `/ready` 的健康探测,不是备份失败或数据恢复;生产自动备份和新鲜度 watchdog 近 14 天均为 14/14 成功、0 条失败。
- 实施前基线 `986668a58762` 每 5 分钟探测,连续 2 次失败即告警、1 次成功即恢复;基线实现见该提交的 `scripts/check-public-app-health.py:220-279` 和 `deploy/trialrun/install-offhost-app-health-monitor.sh:94-113`。
- 实施前基线 `scripts/check-public-app-health.py:131-170` 将 DNS 与 TCP 故障合并为 `dns_or_connect_failed`。
- 实施前基线 `scripts/send-wecom-group-alert.py:133-140` 为所有健康恢复统一使用“内部 OA 运维恢复通知”,接收人无法直接判断这是公网健康监控。
- 当前 ARM 健康。单一静态 DNS 是可疑单点,但现有证据不足以断言历史故障均由 DNS 导致,因此本任务先增强诊断和防抖,不直接修改 DNS。
## Requirements
### R1 — 故障阶段分类
- 将 `dns_or_connect_failed` 至少拆分为 `dns_resolution_failed` 与 `tcp_connection_failed`。
- 保留并稳定区分 `tls_validation_failed`、`request_timeout`、`http_unavailable`、`response_contract_invalid` 和 `response_too_large`。
- 分类只记录稳定类别,不记录解析结果、目标地址、异常原文、响应正文或请求 payload。
### R2 — 公网健康专用文案
- `FAIL_APP_HEALTH`、`PASS_APP_HEALTH_READY` 和新增的 `FAIL_APP_HEALTH_FLAPPING` 抖动事件必须明确显示“OA 公网健康”。
- 恢复文案不得使用可能被理解为数据库、附件或备份恢复的泛化标题。
- 自动备份、异机 dead-man、季度提醒等共用告警 helper 的既有标题、发送条件和脱敏合同保持兼容。
### R3 — 均衡防抖状态机
- 保持 5 分钟探测频率和连续 2 次失败告警,持续不可用仍在约 10 分钟量级发送一次告警。
- 已发送故障告警后,必须连续 2 次成功才发送一次恢复;单次短暂成功只进入恢复待确认状态,不发送恢复。
- 正常恢复后的 60 分钟观察窗内,同一故障类别再次连续达到失败阈值时,只发送一次“OA 公网健康链路反复抖动”提醒并进入抖动状态。
- 抖动状态内后续失败、成功及同类切换只更新本地脱敏计数,不逐次发送告警或恢复。
- 抖动状态在连续健康满 30 分钟后静默退出;退出本身不另发消息。之后的新故障按新的正常周期处理。
- 恢复观察窗内出现不同故障类别时,按新的故障事件执行连续 2 次失败门槛,避免隐藏不同根因;同一持续故障期间类别变化仍按既有“类别变化最多通知一次”的兼容行为处理。
### R4 — 状态与隐私边界
- 本地状态仅允许保存 schema 版本、稳定状态/类别、连续成功或失败计数、必要的 Unix 时间戳和抖动计数。
- 状态文件继续使用原子写入、`0600` 权限和严格 schema 校验;损坏或越界状态 fail closed,并只输出稳定脱敏错误类别。
- 不保存主机名、IP、URL、路径、异常原文、Webhook、Secret 或原始响应;测试捕获不得访问真实企业微信。
### R5 — 发布门禁
- 本地实现和合同验证不授权 ARM 写入、systemd 变更、DNS 修改或真实企业微信消息测试。
- 后续 ARM rollout 必须再次取得明确批准,并按“disabled 安装 → 只读检查 → 受控启用”执行;DNS 调整必须依据增强分类后的观测证据另行决策。
## Scope
- `scripts/check-public-app-health.py` 的故障分类、恢复确认、抖动状态机和脱敏状态 schema。
- `scripts/send-wecom-group-alert.py` 的公网健康专用文案及共享行为兼容。
- `scripts/qa/run-app-health-monitor-contract.sh`、`scripts/qa/run-automatic-backup-contract.sh` 和 ARM 安装合同的相关回归。
- 必要的既有运维事实源更新,以及后续 ARM rollout/rollback 操作设计。
## Out of Scope
- 规划阶段修改产品代码、ARM 配置、systemd 或 DNS。
- 自动重启 OA、切换流量、数据库修复或任何数据恢复。
- 部署 Prometheus/Alertmanager 或引入新的消息供应商。
- 未取得证据前指定公共 DNS、覆盖主机网络策略或停用健康监控。
- 在本任务内执行生产部署、真实企微消息发送或真实数据写入。
## Acceptance Criteria
- [x] **AC1 / R1**:无外网依赖的合同测试可稳定区分 DNS、TCP、TLS、超时、HTTP、响应格式及响应过大故障。
- [x] **AC2 / R2**:应用健康告警、恢复及抖动提醒的标题均明确包含“OA 公网健康”,测试证明消息不含地址、路径、异常原文或 payload。
- [x] **AC3 / R3**:5 分钟频率与 2 次失败阈值保持不变;持续故障只发送一次告警,相同故障持续期间不重复发送。
- [x] **AC4 / R3**:状态机测试证明 1 次成功不恢复、2 次连续成功只恢复一次;恢复后 60 分钟内同类复发只提醒一次,抖动期切换静默计数,连续健康 30 分钟后静默退出。
- [x] **AC5 / R3**:恢复观察窗内的不同类别故障不会被同类抖动规则吞掉,并仍经过连续 2 次失败门槛。
- [x] **AC6 / R4**:本地状态 schema、权限、原子写入、损坏状态 fail-closed 和敏感字段禁入均有自动化覆盖。
- [x] **AC7 / R2**:自动备份、异机 dead-man 和既有共享 helper 合同全部通过,非应用健康消息行为无回归。
- [x] **AC8 / R5**:安装合同保持默认 disabled、5 分钟周期、固定安全目标及独立启用门禁;本地验证不产生真实企微或远端写入。
## Test Plan
- `python3 -m py_compile scripts/check-public-app-health.py scripts/send-wecom-group-alert.py`
- `bash scripts/qa/run-app-health-monitor-contract.sh`
- `bash scripts/qa/run-automatic-backup-contract.sh`
- `bash -n deploy/trialrun/install-offhost-app-health-monitor.sh`
- `node scripts/qa/check-process-contract.mjs --baseline --json`
- `node scripts/qa/audit-docs.mjs --check-registry`
- `node scripts/qa/check-trellis-governance.mjs`
- 针对本任务变更执行敏感信息扫描和 `git diff --check`。
- ARM 写入获批后才执行 disabled 安装、只读状态检查、受控启用和回滚验证;真实消息测试与 DNS 修改仍需分别批准。
## Local Verification Result
- `bash scripts/qa/run-app-health-monitor-contract.sh`:PASS,36 项检查。
- `bash scripts/qa/run-automatic-backup-contract.sh`:PASS,74 项检查,共享备份/dead-man 标题和去重无回归。
- Python/Shell 语法、process baseline、文档注册审计、敏感扫描、Trellis governance、task validate 和 `git diff --check` 均通过;文档审计仅保留既有的未登记文件 warning。
- 当前结论为 `LOCAL_FIX_VERIFIED`;ARM 部署、真实群消息、DNS 调整和外部行为验证仍为 `EXTERNAL_GATED`。