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

9.1 KiB
Raw Blame History

可观测性与运维基础设施设计

日期: 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,非关键组件失败 → degraded200关键组件失败 → unhealthy503

对现有代码的影响: 仅修改 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 条