可观测性
OCM 的可观测性按「信号层归上游、消费层跟部署情景走、交付层归交付方」分层:
- 信号层(上游,部署无关):后端暴露 Prometheus 格式的
/metrics端点 (RED 请求指标 + Go 运行时 + DB 连接池)、结构化 JSON 日志(stdout)、/healthz与/readyz探针、以及每个响应携带的X-Request-ID。 - 消费层(跟部署情景走):Docker Compose 情景用参考监控栈 overlay
(本文档);Serverless 云原生情景没有 node_exporter/Prometheus 守护进程
的生存空间,信号层交给平台托管监控消费——平台收 stdout 日志、探
healthz、抓
/metrics。 - 交付层(归交付方):面板调优、告警路由、runbook、运维账号,每个 部署站点各不相同。
信号层(后端)
METRICS_ADDR
| 环境变量 | 默认 | 说明 |
|---|---|---|
METRICS_ADDR | 未设即关闭 | /metrics 独立监听地址(如 :9091) |
- 独立 mux、独立监听:
/metrics不在业务 mux 上,第二个监听器永远 不会误暴露业务路由;监听失败只记日志,不拖垮 API 主服务。 - 暴露内容(Prometheus 文本格式):
http_requests_total{method,path,status}— RED 的 R/E,path是 ServeMux 路由 pattern(如GET /api/roles/{id}),不是原始 URL, 基数有界;未匹配路由记为unmatched。http_request_duration_seconds_bucket/sum/count{method,path}— p95/p99 由它派生。http_requests_in_flight— 进行中请求数。ocm_db_*— 连接池开/闲/占用/上限/等待计数与等待时长。go_*/process_*— Go 运行时与进程指 标(client_golang 默认注册)。
- 无鉴权是设计使然:依赖网络隔离。它绝不能发布到宿主机公网接口 (overlay 已遵守;自组部署时同样遵守)。
X-Request-ID
AccessLog 中间件给每个响应(含 4xx/5xx 错误响应)打上
X-Request-ID 响应头,与日志行的 request_id 同源。运维或最终用户
遇到错误时引用该 id,即可在日志(面板/Loki/容器日志)中精确定位那一次
请求——这是「运维反馈错误 → 开发定位」闭环的关键一环。
日志
slog JSON 打 stdout(生产默认 JSON,LOG_LEVEL/LOG_FORMAT 可调),
容器平台原生采集。业务审计记录在 system_logs(数据库),与运维日志
互不掺杂。
Compose 参考监控栈
docker compose -f docker-compose.yml -f docker-compose.observability.yml up -d # dev
docker compose -f docker-compose.prod.yml -f docker-compose.observability.yml up -d # prod
| 服务 | 镜像(已钉版本) | 作用 | 端口 |
|---|---|---|---|
| prometheus | prom/prometheus:v3.13.3 (LTS) | 抓取 + 存储 | 内网 9090 |
| grafana | grafana/grafana:13.2.2 | 运维面板 | 发布 :3000 |
| node-exporter | prom/node-exporter:v1.12.1 | 主机 CPU/内存/磁盘 | 内网 9100 |
| loki | grafana/loki:3.7.8 | 日志存储(30 天保留) | 内网 3100 |
| alloy | grafana/alloy:v1.20.0 | 容器日志采集进 Loki | 无 |
- 配置都在
deploy/observability/(见其 README)。 - Grafana 开箱即用「OCM 服务状态总览」面板:服务在线、请求速率、5xx、
p95 延迟、CPU/内存/磁盘、DB 连接池、最近后端错误日志(反馈流程:
复制日志行里的
request_id反馈给开发)。 GRAFANA_ADMIN_PASSWORD必填(.env或环境变量),缺省拒绝启动。
安全基线
- 只有 Grafana 对外。
/metrics、node-exporter、prometheus、loki 仅 compose 内网可达,不得发布宿主机端口。 - Grafana 强密码 + HTTPS 反向代理之后交付;运维角色视为只读(重启等 维护动作走 runbook,不开面板权限)。
- 面 板不接 system_logs 业务审计(含学生数据);错误日志面板只看 stdout 运行日志(method/path/status/user 登录名,无业务数据)。
- 监控栈独立于被监控对象存活:backend 全挂时,主机指标、探针状态、
up == 0告警照常工作。
为什么不是 OpenTelemetry
单进程架构(MQTT 消费者是进程内 goroutine)没有跨进程 trace 需求;
/metrics 直出 Prometheus 是移动部件最少的方案。拆出第二个常驻进程
(如独立部署的 worker/consumer)之日,才是引入 OTel collector 之时——
届时 OTLP → collector → Prometheus,/metrics 端点与本 overlay 的面板
原样保留,只是数据来源多一条管道。