Files
hms/docs/superpowers/specs/2026-04-26-observability-and-ops-design.md
iven d1ab8074a3
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
docs: 多专家组头脑风暴产出 — 5 份设计规格
基于全景审计分析,产出 5 份跨领域设计规格:

1. 性能优化 — 后端批量INSERT/合并COUNT/告警预加载 + 前端N+1内联name
2. 安全纵深防御 — PostgreSQL RLS/行级数据范围/session_key Redis/审计哈希链
3. 事件驱动架构增强 — 6个业务域11个缺失事件补发 + Outbox LISTEN/NOTIFY
4. 前端工程化 — 14个大组件拆分 + 3个重复模式统一 + Bundle优化
5. 可观测性与运维 — 深度健康检查/Prometheus/OpenTelemetry/生产Docker
2026-04-27 07:46:36 +08:00

216 lines
9.1 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.
# 可观测性与运维基础设施设计
> 日期: 2026-04-26 | 状态: draft | 主题: 健康检查、Prometheus 指标、分布式追踪、生产 Docker、日志聚合
## 1. 背景
HMS 后端基于 Axum 0.8 + SeaORM 1.1 + Redis 0.27 构建,当前运维能力缺口:
| 能力 | 现状 | 差距 |
|------|------|------|
| 结构化日志 | tracing + tracing-subscriber JSON 格式 | 已实现,无聚合方案 |
| 健康检查 | GET /health 返回 { status, version, modules } | 不验证 DB/Redis 连通性 |
| 指标暴露 | 无 Prometheus endpoint | 需从零搭建 |
| 分布式追踪 | 无 OpenTelemetry | 需从零搭建 |
| 生产 Docker | 仅有开发 docker-composePostgreSQL + Redis | 无 Rust 应用 Dockerfile |
| 日志聚合 | 无 ELK/Loki 集成 | 需从零搭建 |
技术栈tower-http 0.6(已启用 trace feature、自定义 rate_limit/JWT 中间件通过 `axum::middleware::from_fn` 注册。
## 2. 问题分析
### 2.1 健康检查不充分
当前 `/health` 仅返回静态信息(版本号、模块名列表),不验证外部依赖连通性。容器编排无法据此判断服务是否真正可用。
### 2.2 无可观测性指标
缺少请求延迟分布P50/P95/P99、错误率、QPS、DB 连接池使用率、事件 outbox 积压量等关键运行指标。
### 2.3 无分布式追踪
Axum handler -> SeaORM query -> Redis command 之间无 trace_id 串联。排查跨模块问题(预约创建 -> 工作流启动 -> 消息通知)需手动对齐日志时间戳。
### 2.4 无生产级容器镜像
Rust 应用直接 `cargo run` 启动,缺少多阶段构建 Dockerfile、健康检查指令、非 root 用户运行。
## 3. 解决方案
### 3.1 深度健康检查
**Crate 选型**: 无额外依赖,使用已有的 sea_orm + redis。
**改造方案**: 扩展 `HealthResponse` 为分级检查,增加 `/health/live`(存活探针)和 `/health/ready`(就绪探针)两个子路径。
检查项:
| 组件 | 检查方式 | 超时 | 关键性 |
|------|---------|------|--------|
| PostgreSQL | `SELECT 1` via SeaORM | 2s | 关键(失败返回 503 |
| Redis | `PING` via redis::Client | 1s | 非关键(失败标记 degraded |
| 模块状态 | 遍历 registry 检查 on_startup 是否完成 | 0ms | 非关键 |
状态判定:全部通过 → `healthy`,非关键组件失败 → `degraded`200关键组件失败 → `unhealthy`503
**对现有代码的影响**: 仅修改 `handlers/health.rs`~40 行改动),`AppState` 无需变化。
### 3.2 Prometheus 指标
**Crate 选型**: `metrics` 0.24 + `metrics-exporter-prometheus` 0.16
选择理由:`metrics` 是 Rust 生态的指标门面 crate类似 `log`/`tracing` 的解耦设计exporter 内置独立 HTTP server不侵入 Axum 路由。
**指标设计:**
| 类别 | 指标名 | 类型 |
|------|--------|------|
| 请求 | `http_request_duration_seconds{method,path,status}` | histogram |
| 请求 | `http_requests_total{method,path,status}` | counter |
| 数据库 | `db_pool_connections{state}` | gauge |
| 数据库 | `db_query_duration_seconds{operation}` | histogram |
| 事件 | `eventbus_published_total{event_type}` | counter |
| 事件 | `eventbus_outbox_pending_count` | gauge |
| 运行时 | `process_memory_rss_bytes` | gauge |
**Axum middleware 集成要点:**
- 新增 `middleware/metrics.rs`~40 行),记录每个请求的 method/归一化 path/status/耗时
- 路径归一化:`/api/v1/patients/xxx``/api/v1/patients/:id`,避免高基数标签
- main.rs 初始化 exporter 监听独立端口 9090
- 在路由组装处添加 `.layer(axum_middleware::from_fn(metrics_middleware))`
**对现有代码的影响**: main.rs ~10 行、新增 middleware ~40 行、outbox/event_bus 关键路径埋点 ~20 行。
### 3.3 OpenTelemetry 分布式追踪
**Crate 选型**: `opentelemetry` 0.27 + `opentelemetry-otlp` 0.27 + `tracing-opentelemetry` 0.28
**成熟度评估:**
- Rust OTel SDK 0.27+ 版本 API 趋于稳定
- `tracing-opentelemetry` 与 tracing-subscriber 兼容性良好
- SeaORM/Redis 无原生 span 支持,需手动埋点
- 风险SDK 初始化增加启动时间约 100-200ms
**集成方案:**
1. main.rs tracing 初始化重构为条件启用:通过 `OTEL_EXPORTER_OTLP_ENDPOINT` 环境变量决定是否启用
2. 利用已有的 `tower-http` TraceLayer项目已依赖注入 trace_id
3. SeaORM 关键查询点手动创建 `tracing::info_span!("db.query", db.operation = "xxx")`
4. 采用 OTLP 协议导出,兼容 Jaeger/Tempo/Zipkin
**对现有代码的影响**: main.rs ~30 行改动、Cargo.toml 新增 4 个依赖、service 函数 span 注解渐进式添加。
### 3.4 生产 Docker 镜像
**多阶段构建策略:**
| 阶段 | 基础镜像 | 目的 |
|------|---------|------|
| 编译 | rust:1.85-bookworm | cargo build --release |
| 运行 | debian:bookworm-slim | 仅二进制 + ca-certificates |
关键设计:
- **层缓存优化**: 先复制所有 Cargo.toml → 创建空源文件 → 编译依赖 → 复制实际源码 → 编译应用。依赖不变时复用编译缓存。
- **安全**: 非 root 用户erp:erp运行
- **健康检查**: `curl -f http://localhost:3000/health/live`
- **预期运行时镜像**: ~50-80MB
**docker-compose.prod.yml**: erp-server 服务 + 环境变量注入 + depends_on health condition + 健康检查指向 `/health/ready`
### 3.5 日志聚合
**方案: Grafana Loki**
选择理由:与 Prometheus 同属 Grafana 生态,不做全文索引按标签查询,资源消耗远低于 ELK。tracing-subscriber JSON 输出天然兼容 Loki 标签模型。
部署Loki 3.0 + Grafana 11.0 + Prometheus 2.52 通过 `docker-compose.monitoring.yml` 独立管理。日志采集通过 Grafana Alloy 收集 Docker 容器 stdout。
### 3.6 告警规则
基于 Prometheus 指标的 5 条核心告警:
| 规则 | 条件 | 级别 |
|------|------|------|
| HighErrorRate | 5xx 比率 > 5% 持续 2m | critical |
| HighLatencyP99 | P99 > 2s 持续 5m | warning |
| DatabasePoolExhaustion | 连接池使用率 > 85% 持续 3m | warning |
| OutboxBacklog | outbox 积压 > 100 持续 5m | warning |
| HealthCheckFailed | 服务 up == 0 持续 1m | critical |
## 4. 实施步骤
### Phase 1: 深度健康检查 + 生产 Docker1-2 天)
| 任务 | 改动范围 | 优先级 |
|------|---------|--------|
| 扩展 HealthResponse + DB/Redis 检查 | handlers/health.rs ~60 行 | P0 |
| 添加 /health/live 和 /health/ready | handlers/health.rs ~20 行 | P0 |
| 编写生产 Dockerfile | 新文件 ~50 行 | P0 |
| 编写 docker-compose.prod.yml | 新文件 ~40 行 | P0 |
### Phase 2: Prometheus 指标2 天)
| 任务 | 改动范围 | 优先级 |
|------|---------|--------|
| 引入 metrics crate | Cargo.toml ~4 行 | P0 |
| 实现 metrics middleware | 新文件 ~40 行 | P0 |
| 注册 middleware + exporter 初始化 | main.rs ~15 行 | P0 |
| SeaORM/EventBus 指标埋点 | ~40 行 | P1 |
| Prometheus + Grafana Docker 配置 | 新文件 ~60 行 | P1 |
### Phase 3: OpenTelemetry 集成2-3 天)
| 任务 | 改动范围 | 优先级 |
|------|---------|--------|
| 引入 opentelemetry crate | Cargo.toml ~6 行 | P2 |
| 重构 tracing 初始化为条件启用 | main.rs ~30 行 | P2 |
| 添加 TraceLayer | main.rs ~5 行 | P2 |
| Service 函数 span 注解 | 渐进式 | P2 |
### Phase 4: 日志聚合 + 告警1-2 天)
| 任务 | 改动范围 | 优先级 |
|------|---------|--------|
| Loki + Grafana 部署配置 | 新文件 ~40 行 | P2 |
| Grafana Alloy 日志采集配置 | 新文件 ~30 行 | P2 |
| Prometheus 告警规则 | 新文件 ~50 行 | P2 |
## 5. 风险与缓解
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| OTel SDK breaking change | 升级困难 | 锁定 0.27 版本feature flag 条件启用 |
| 指标收集增加延迟 | 性能退化 | histogram 无锁实现,单次 record < 50ns |
| 日志量导致存储膨胀 | 存储成本 | Loki retention 30 天JSON 压缩率高 |
| Docker 编译缓存失效 | CI 时间长 | Cargo.toml 层和源码层分离 |
| Prometheus 暴露内部信息 | 安全风险 | 独立端口 9090网络策略限制访问 |
| 健康检查超时阻塞 | /health 延迟 | 短超时DB 2s/Redis 1s并行检查 |
**Crate 选型对比:**
| 方案 | 优势 | 劣势 | 结论 |
|------|------|------|------|
| `prometheus` crate (原生) | 功能完整 | API 较重 | 不选 |
| `metrics` + exporter | 轻量 facade解耦 | 需额外 crate | 推荐 |
| Jaeger 直接导出 | 简单 | 已废弃 | 不选 |
| OTLP + Tempo/Jaeger | 通用标准 | 需 Collector | 推荐 |
**性能影响评估:**
| 组件 | 额外延迟 | 额外内存 | 启动时间增幅 |
|------|---------|---------|-------------|
| Prometheus middleware | < 0.1ms/req | ~5MB | < 50ms |
| OpenTelemetry (10% 采样) | < 0.5ms/req | ~20MB | 100-200ms |
| 健康检查 (DB ping) | 仅 /health | 无 | 无 |
**成功指标:**
| 指标 | 当前值 | 目标值 |
|------|--------|--------|
| /health 覆盖外部依赖 | 无 | DB + Redis |
| Prometheus 端点 | 无 | :9090/metrics |
| 分布式追踪 | 无 | 请求→DB→Redis 全链路 |
| 生产镜像大小 | 无 | < 80MB |
| 告警规则数 | 0 | >= 5 条 |