可观测性与运维
本页讲Molore云端的可观测性设施与运维纪律:自动运营日报、检索健康度指标、生产安全面(文档端点关闭、安全头、限流)、内存安全网与 pm2 部署纪律,以及「查库比翻日志快」的排障口径。目标是在单台小机上把服务跑稳,并且出问题时能按固定顺序快速定位。
概念
| 概念 | 说明 |
|---|---|
| 自动日报 | 每天 08:30(CST)生成昨日运营日报,落 daily_reports 表 |
| 检索健康度 | 按日聚合的检索指标表 retrieval_metrics_daily,观察检索通道退化 |
| 生产安全面 | 生产环境关闭 API 文档端点、发安全响应头、按 IP 分桶限流 |
| 内存安全网 | 常驻模型上限 + earlyoom + pm2 内存重启帽三件套 |
| 部署探针 | 重启后核对文件 mtime + 内容级断言(不看状态码) |
功能清单
- 自动日报:
app/services/daily_report.py,apscheduler 每天 08:30(CST)触发,默认统计昨日(UTC);Markdown 分段生成,单段失败只影响该段(段内异常降级为「该段数据异常」占位,不拖垮整份日报)。 - 检索健康度:
app/models/metrics.py的RetrievalMetricsDaily(day+metric复合主键,按 UTC 日聚合),app/services/retrieval_metrics.py提供incr(metric, n)计数入口,检索链路各通道打点,用于按日观察通道开火与退化。 - 生产关闭文档端点:
app/main.py中docs_url=None if _is_prod else "/docs",/redoc、/openapi.json同口径——生产不暴露 API 结构。 - 安全响应头(
app/core/security_middleware.py):HSTS(max-age=31536000; includeSubDomains)、CSP(CSP_ENABLED开关,策略串只对 document 生效)、X-Content-Type-Options: nosniff、X-Frame-Options: DENY、Referrer-Policy、Permissions-Policy。 - 限流带 Retry-After:四类桶均返回 429 +
Retry-After: 60。 - 内存安全网三件套:Ollama 常驻模型数压到 1(
MAX_LOADED_MODELS=1)、系统级 earlyoom 兜底、pm2--max-memory-restart 1400M。 - 失败定位走数据库:图谱构建失败原因在
graph_build_state.error(500 字截断),CLI 逐 chunk 错误不进 pm2 日志——查库比翻日志快。
限流口径
按 IP 分桶,客户端 IP 取值规则(security_middleware._get_client_ip):请求来源在 TRUSTED_PROXY_IPS 内时取 X-Forwarded-For 链末段(末段由可信代理写入、客户端无法伪造;取首段等于让攻击者自选限流 key)。不配 TRUSTED_PROXY_IPS 时全站共享 127.0.0.1 一个桶。
| 桶 | 生产限额 | 适用范围 |
|---|---|---|
| login_limiter | 5/min | /api/v1/auth/login、/api/admin/auth/login、注册 |
| chat_limiter | 30/min | /api/v1/llm/chat、/summarize、/extract-tags |
| admin_limiter | 200/min | /api/admin/ |
| api_limiter | 100/min(桌面端 600/min) | 其余 /api/ |
桌面端(PSB_DESKTOP=1)是本机单用户形态、所有请求共享 127.0.0.1 一个桶,通用限流放宽到 600/min(批量删除 100 条素材曾把桌面端自己 429 到「掉线」)。
内存安全网
小机部署先算「所有同时在场者」的内存总账(血泪#53/#88):
| 组件 | 配置 | 作用 |
|---|---|---|
| Ollama 常驻模型 | MAX_LOADED_MODELS=1 | 同时只驻留一个模型,压住常驻内存 |
| earlyoom | 系统级守护 | 内存耗尽前杀进程兜底 |
| pm2 重启帽 | --max-memory-restart 1400M | 主进程超帽自动重启 |
关键教训:内存帽要按「基线 + 请求峰值」配,基线贴帽等于每个请求都是重启抽奖。曾以 780MB 基线配 800M 帽导致连环重启全站 502;根治手段是重型可选依赖(numba/UMAP)计算子进程化,峰值随子进程退出即释放,主进程基线 780→413MB,当前帽 1400M 余量充足。pm2 内存杀在应用日志里无痕,实证看 ~/.pm2/pm2.log 的 "exceeds --max-memory-restart"。
pm2 部署纪律
- 进程形态:
pm2 start bash --name grzhishiku --max-memory-restart 1400M -- -c '.venv/bin/python -m uvicorn app.main:app --host 127.0.0.1 --port 8080',nginx 443 反代,pm2 save固化。 - 后端发布:tar 含
app/(--exclude __pycache__)→pip install -r requirements.txt→pm2 restart;tar 覆盖解压不删旧文件,拆包/删文件的批次必须显式 rm 旧文件(血泪#75)。 - 前端发布:tar 在 dist 内部打包(无前缀),
mv整体换新。 - 重启后必核文件 mtime + 内容级探针:只看状态码会被 SPA fallback 的 200 骗过;探针定串用纯文本(grep -cF),别带反斜杠(经双层 shell 漏成字面量造成假 FAIL,#97)。
- 大文件传输:scp 分片 10MB + 重试 + sha256 双端校验,一个时刻只跑一个上传。
排障顺序
502 先看进程在不在,别先怀疑代码(#56):
/health探活;pm2 uptime/restarts看进程状态与重启次数;- 端点重试;
- 最后才翻日志。
构建类失败直接查库:SELECT error FROM graph_build_state ... 拿失败原因(500 字截断),比翻 pm2 日志快——CLI 逐 chunk 错误不进 pm2 日志(生产实捕)。