基于全景审计分析,产出 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
9.1 KiB
可观测性与运维基础设施设计
日期: 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-compose(PostgreSQL + 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
集成方案:
- main.rs tracing 初始化重构为条件启用:通过
OTEL_EXPORTER_OTLP_ENDPOINT环境变量决定是否启用 - 利用已有的
tower-httpTraceLayer(项目已依赖)注入 trace_id - SeaORM 关键查询点手动创建
tracing::info_span!("db.query", db.operation = "xxx") - 采用 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: 深度健康检查 + 生产 Docker(1-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 条 |