# 可观测性与运维基础设施设计 > 日期: 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 **集成方案:** 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: 深度健康检查 + 生产 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 条 |