Files
Nexus/docs/reports/review-deploy-architecture-2026-05-21.md
T
Your Name c9a99f4fb3 fix: 全项目文档对齐 + 代码清理
代码修复:
- web/agent/agent.py: datetime.utcnow() → datetime.now(timezone.utc)
- requirements.txt: 移除 paramiko==3.5.0 (已无活跃引用)
- 删除 server/infrastructure/ssh/pool.py (DEPRECATED, 无引用)

连接池三层对齐 (config.py/.env.example/session.py):
- DB_POOL_SIZE 100→160, DB_MAX_OVERFLOW 100→120
- 基于 MySQL max_connections=400, install.php 公式

文档修复 (21项):
- P0: 硬编码域名/IP替换为配置变量占位符
- P1: 10个过时设计文档加归档标注 (引用旧文件结构)
- P2: step-3-webssh报告 paramiko引用修正
- 6份审查报告连接池参数勘误 100/100→160/120
- ECC安全报告 EC5 datetime.utcnow 标记已修复
- docs/README.md 文档索引重写
- docs/memory/mem_nexus_overview.md 移除硬编码凭证
- docs/project/tech-stack-inventory.md paramiko标记已移除

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 08:19:56 +08:00

352 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 运维总监审查报告
**项目**: Nexus 6.0 — 服务器运维管理平台
**审查时间**: 2026-05-212026-05-22 勘误更新)
**审查范围**: deploy/ 全部配置、docs/project/deploy.md 部署指南、server/config.py 配置项、server/main.py 启动配置、server/background/ 后台任务、server/infrastructure/ 基础设施层
> **勘误说明 (2026-05-22)**: 本报告部分问题在审查后已修复,已在相应位置标注 ✅已修复。
**审查人**: 运维总监
---
## 1. 部署架构总览
```
┌──────────────┐
│ Let's Encrypt │ (生产环境 HTTPS)
│ (宝塔面板) │
└──────┬───────┘
┌──────▼───────┐
│ Nginx │ ← 反向代理 + 静态文件 + PHP-FPM
│ (80/443) │
└──┬───────┬──┘
│ │
┌─────────────▼┐ ┌───▼──────────┐
│ FastAPI │ │ PHP-FPM │
│ :8600 │ │ (旧前端) │
│ (4 workers) │ │ │
└──┬───────┬───┘ └──────────────┘
│ │
┌──────▼──┐ ┌──▼──────────┐
│ Redis │ │ MySQL 8.4 │
│ (实时) │ │ (历史) │
└─────────┘ └─────────────┘
3层守护机制:
Layer 1: Supervisor ─── Python崩溃 → 自动重启
Layer 2: self_monitor ── 30s检查Redis/MySQL/WS → Telegram告警
Layer 3: health_monitor ── 1min HTTP GET /health → 连续3次失败 → restart + Telegram
```
**整体评价**: 架构设计合理,三层守护覆盖全面,数据流清晰(Agent → Redis → 前端直读 → 10min → MySQL)。核心问题在于**配置一致性偏差**、**资源预留过大**、**安全防护薄弱**。
---
## 2. Supervisor配置评估
**文件**: `deploy/nexus.conf`
### 优点
- `autorestart=true` + `startretries=10` — 崩溃自动重启,最多重试10次
- `stopsignal=INT` — SIGINT发送给uvicorn,可实现优雅关闭
- `stopwaitsecs=10` — 给进程10秒处理完当前请求
- 日志自动轮转:50MB切割,保留5个备份
- 不硬编码密钥(依赖.env文件)— 安全合规
### 问题
| 严重度 | 问题 | 说明 |
|--------|------|------|
| **高** | 单Worker运行 (`--workers` 缺失) | `nexus.conf` 只用1个worker,但文档 `deploy.md` 的示例配置写着 `--workers 4`。两者不一致。实际线上用1个worker处理所有请求(包括心跳、WebSocket、API),单点过载风险。详见第8节。 |
| **中** | `startsecs=3` 偏短 | 如果应用启动耗时超过3秒(如数据库表较多时init_db可能较慢),Supervisor会误判为启动失败。建议改为 `startsecs=10` |
| **中** | `user=root` 运行 | Python进程以root权限运行。如果FastAPI出现RCE漏洞,攻击者可获得服务器完全控制权。建议创建专用系统用户 `nexus` |
| **低** | 缺少 `redirect_stderr=true` | 当前将stderr和stdout分别写入两个文件。设为 `redirect_stderr=true` 可统一日志管理 |
| **低** | 无 `environment` 变量 | 虽然依赖.env文件启动,但指定 `PYTHONPATH` 环境变量可以避免潜在的导入路径问题 |
### 建议修复
```ini
[program:nexus]
command=/www/wwwroot/api.synaglobal.vip/venv/bin/uvicorn server.main:app --host 0.0.0.0 --port 8600 --workers 4
directory=/www/wwwroot/api.synaglobal.vip
user=nexus # 改为专用系统用户
autostart=true
autorestart=true
startretries=10
startsecs=10 # 加长启动验证时间
stopwaitsecs=20 # 加长优雅关闭等待
stopsignal=INT
environment=
PATH="/www/wwwroot/api.synaglobal.vip/venv/bin:%(ENV_PATH)s",
PYTHONPATH="/www/wwwroot/api.synaglobal.vip:%(ENV_PATH)s"
redirect_stderr=true
stdout_logfile=/var/log/nexus/nexus.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
```
---
## 3. Nginx配置评估
**文件**: `deploy/nginx_172.31.170.47.conf`WSL环境)
### 优点
- `upstream nexus_api { keepalive 32; }` — 使用连接池,减少TCP握手开销
- 清晰的全域名/API/WebSocket/静态文件分离路由
- 敏感文件保护:`location ~ /\. { deny all; }` + `location ~ /data/config\.php$ { deny all; }`
- 静态资源7天缓存:`expires 7d; add_header Cache-Control "public, immutable";`
- WebSocket代理配置正确:`Upgrade` / `Connection` 头、`proxy_read_timeout 180s`
- `client_max_body_size 100m` — 适合文件管理功能
### 问题
| 严重度 | 问题 | 说明 |
|--------|------|------|
| **高** | 无HTTPS配置 | WSL环境的Nginx配置只有HTTP 80。对于运维管理平台,即使在WSL内网也应启用HTTPS。至少应配置自签名证书。 |
| **高** | 无安全响应头 | 缺少 `X-Content-Type-Options``X-Frame-Options``Content-Security-Policy``Strict-Transport-Security` 等关键安全头 |
| **中** | 无速率限制 | API端点和登录接口缺少 `limit_req` 配置,存在暴力破解和DDoS风险 |
| **中** | 无请求体大小限制 | API代理未设置 `client_max_body_size`,但全局设置了100m,而API代理不应允许这么大的请求体 |
| **中** | `location /health` 缺少转发头 | 健康检查代理未设置 `X-Real-IP` 等头信息(虽然对健康检查影响不大,但不一致) |
| **低** | 无压缩配置 | 未启用gzip/brotli压缩,JSON API响应可能浪费带宽 |
| **低** | 无日志格式自定义 | 使用默认Combined格式,缺少上游响应时间等关键指标 |
| **低** | WSL配置和生产配置分离 | 存在两份Nginx配置(WSL + 生产),但生产配置在deploy.md中而非独立文件 |
### 生产环境安全头建议(补充到Nginx配置)
```nginx
# 在 server block 中添加
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# API速率限制
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
location /api/ {
limit_req zone=api burst=50 nodelay;
# ... 现有配置
}
# 登录接口限制(更严格)
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s;
location /api/auth/ {
limit_req zone=login burst=10 nodelay;
# ...
}
```
---
## 4. 三层守护机制评估
### 4.1 Layer 1: Supervisor
| 维度 | 评估 | 说明 |
|------|------|------|
| 崩溃检测 | ✅ | 进程退出时,Supervisor即时感知 |
| 自动重启 | ✅ | `autorestart=true` + `startretries=10` |
| 优雅关闭 | ✅ | SIGINT + 10秒等待 |
| 启动验证 | ⚠️ | `startsecs=3` 可能偏短,建议10秒 |
| 频繁崩溃防护 | ⚠️ | 10次重试后停止,但未配置 `startretryinterval`(默认0=无间隔),可能瞬间耗尽重试次数 |
### 4.2 Layer 2: Python self_monitor
| 维度 | 评估 | 说明 |
|------|------|------|
| 检查间隔 | ✅ | 每30秒 |
| Redis检测 | ✅ | 返回None时告警 |
| MySQL检测 | ✅ | 执行SELECT 1 |
| WebSocket清理 | ✅ | 移除过期客户端 |
| **自动修复** | ❌ | **仅发送Telegram告警,不做任何修复操作** |
| 连续失败降级 | ❌ | 无降级逻辑,每次失败都发告警(可能造成告警轰炸) |
**严重问题**: Layer 2只有"检测+告警"能力,没有任何自动修复动作。例如Redis连接丢失后,self_monitor只是发一条Telegram消息,不做重连尝试。虽然 `get_redis()` 函数在检测到连接丢失后会置 `_redis_pool = None`,下次调用会尝试重连,但self_monitor自身的Redis检测逻辑并未利用这个机制重连以提高成功率。
### 4.3 Layer 3: Shell health_monitor.sh
| 维度 | 评估 | 说明 |
|------|------|------|
| 独立性 | ✅ | 纯Shell脚本,不依赖Python |
| 连续失败阈值 | ✅ | 3次连续失败才触发重启 |
| 重启后验证 | ✅ | restart后sleep 5秒,再验证 |
| Telegram通知 | ✅ | 失败/恢复/人工介入三类通知 |
| 配置来源 | ✅ | 从config.php或.env读取Telegram凭证 |
**严重问题**: 循环重启风暴风险。当连续3次失败后,脚本调用 `supervisorctl restart`,如果重启后验证仍然失败,**不会重置FAIL计数器**。这意味着每次cron执行都会再次触发 `supervisorctl restart`(因为FAIL计数器保持在3+)。这会造成每分钟一次的循环重启风暴。正确的做法应该是增加"冷却期"机制——例如连续N次重启失败后,停止自动重启并仅发送人类介入通知。
### 4.4 三层联动评估
```
场景: Python进程崩溃
Layer 1: Supervisor 即时检测 → 秒级重启 ✓
Layer 2: 如果Python在30s内恢复,self_monitor不会触发告警 ✓
Layer 3: 如果1分钟内恢复,health_monitor不会触发(FAIL计数器不会达到3)✓
场景: Python进程僵死(不崩溃但无响应)
Layer 1: 进程未退出,Supervisor不触发 ✗
Layer 2: self_monitor只要asyncio事件循环还能运行就能检测,但如果整个进程僵死则不行 ✗
Layer 3: HTTP GET /health超时,3次后触发supervisorctl restart ✓
场景: Redis宕机
Layer 1: 不触发(Python进程正常运行)✓
Layer 2: 30s内检测到Redis丢失,发Telegram告警(但不尝试修复)⚠️
Layer 3: 不触发(/health端点仍能响应,只是标记redis=unavailable)✓(正确)
场景: MySQL连接池耗尽
Layer 1: 不触发 ✓
Layer 2: 30s内检测到MySQL异常,发Telegram告警 ⚠️
Layer 3: 不触发(HTTP健康检查返回python=ok)✓(正确)
```
**评估**: 三层守护的覆盖域合理,各层分工明确。核心缺失是 Layer 2 缺乏自动修复能力。循环重启风暴是需要优先修复的风险。
---
## 5. 运维风险点
### 5.1 高风险
| # | 风险 | 影响 | 建议 |
|---|------|------|------|
| R1 | **单Worker生产运行** | 一个worker处理所有WebSocket连接、心跳接收、API请求。一旦某个请求阻塞(如慢查询),所有其他请求(包括心跳)都会排队等待。后果:Agent心跳超时、WebSocket断连、健康检查失败触发误重启。 | 使用 `--workers 4` 并启用 `--ws max_size=16` 等uvicorn优化参数 |
| R2 | **循环重启风暴** | health_monitor在连续3次失败后,每分钟执行一次 `supervisorctl restart`,无论重启是否成功。如果根本原因(如配置错误、端口冲突)未解决,会造成每分钟的反复重启循环。 | 增加冷却期:连续N次重启失败后,停止自动重启,仅发送"需人工介入"告警,等待操作员处理 |
| R3 | **Root运行所有进程** | Supervisor配置 `user=root`,Python进程以root身份运行。如果FastAPI存在任意代码执行漏洞(如依赖库CVE),攻击者可获得服务器完全控制权。 | 创建 `nexus` 系统用户,最小权限原则 |
| R4 | **CORS全开放** | `allow_origins=["*"]` | ✅**已修复** — 改为 `settings.CORS_ORIGINS.split(",")``NEXUS_CORS_ORIGINS` 环境变量读取白名单 |
### 5.2 中风险
| # | 风险 | 影响 | 建议 |
|---|------|------|------|
| R5 | **数据库连接池监控** | ~~`DB_POOL_SIZE=100` + `DB_MAX_OVERFLOW=100`~~ → 已修正为 `DB_POOL_SIZE=160` + `DB_MAX_OVERFLOW=120`(基于 MySQL max_connections=400)。install.php 已实现自动计算:`poolSize = max(20, maxConn * 0.4)`。 | ✅ install.php 自动计算已落地。建议增加运行时连接池使用率监控告警 |
| R6 | **健康检查端口固定** | `HEALTH_URL` 硬编码为 `http://127.0.0.1:8600/health`,如果后续端口变更需同时修改脚本。 | 改为从配置文件读取或作为脚本参数传入 |
| R7 | **异常处理宽泛** | `load_settings_from_db``except Exception` 静默吞掉所有异常,返回0。如果settings表结构变更或数据类型异常,不会被及时发现。 | 区分可忽略异常和需要告警的异常,对关键错误发送运维告警 |
| R8 | **无日志集中管理** | Supervisor日志写在 `/var/log/nexus/`Nginx日志在 `/www/wwwlogs/`Python日志可能分散到stdout。无统一日志中心,故障排查需要手动查看多个位置。 | 使用 systemd-journald 或 rsyslog 统一收集,考虑接入 ELK/Loki |
| R9 | **文档与配置不一致** | `deploy.md` 中Nexus Supervisor配置示例使用 `--workers 4``startretries`, `startsecs` 等未列出。实际 `nexus.conf` 只有单worker。部署新环境时可能按文档配置4 workers,但本地nexus.conf是1 worker。 | 同步文档和实际配置,确保部署指南与部署文件一致 |
| R10 | **无数据库备份策略** | 项目中未找到自动备份脚本或策略。MySQL存储心跳历史、告警配置、服务器信息,一旦数据丢失将造成严重后果。 | 实现 `mysqldump``XtraBackup` 每日备份 + 7天轮转 |
### 5.3 低风险
| # | 风险 | 影响 | 建议 |
|---|------|------|------|
| R11 | 无监控面板 | 无Grafana/Prometheus等可视化监控 | 接入node_exporter + Prometheus |
| R12 | 无容量规划 | 心跳数据量增长后MySQL性能未知 | 在1000+服务器规模下进行压测 |
| R13 | WSL无HTTPS | 管理流量明文传输 | 至少配置自签名证书 |
| R14 | 缺少staging环境 | 变更直接上生产 | 保持WSL作为staging环境 |
---
## 6. 改进建议
### P0 — 立即修复(影响生产稳定性)
1. **Supervisor启用多Worker**
```bash
# nexus.conf 中 command 添加 --workers 4
command=/www/wwwroot/api.synaglobal.vip/venv/bin/uvicorn server.main:app --host 0.0.0.0 --port 8600 --workers 4
```
**注意**: 多Worker下WebSocket连接会分配给不同进程,需要使用Redis Pub/Sub做跨进程WebSocket广播。如果当前WebSocket实现(`connected_clients` 是内存集合)不支持跨进程,则需评估。推荐启用 `--ws ping_interval 20`。
2. **health_monitor.sh 增加重启冷却**
```bash
# 在重启失败后增加冷却文件
COOLDOWN_FILE="/tmp/nexus_restart_cooldown"
COOLDOWN_PERIOD=10 # 10分钟内不再自动重启
# 在重启失败分支中
if [ "$FAIL" -ge "$MAX_FAIL" ]; then
# 检查冷却期
COOLDOWN=$(cat "$COOLDOWN_FILE" 2>/dev/null || echo "0")
NOW=$(date +%s)
if [ "$COOLDOWN" -gt "$NOW" ]; then
send_telegram "⚠️ Nexus重启冷却中(跳过自动重启)"
exit 0
fi
# ... 执行重启 ...
# 如果重启失败,设置10分钟冷却
echo "$((NOW + COOLDOWN_PERIOD * 60))" > "$COOLDOWN_FILE"
fi
```
3. **创建专用服务用户**
```bash
sudo useradd -r -s /usr/sbin/nologin -M nexus
sudo chown -R nexus:nexus /www/wwwroot/api.synaglobal.vip
sudo chmod 640 /www/wwwroot/api.synaglobal.vip/.env # 限制.env访问
```
### P1 — 短期改进(本周内)
4. ~~**调优数据库连接池**~~ → 已修正:config.py 默认值已调整为 `DB_POOL_SIZE=160` + `DB_MAX_OVERFLOW=120`(基于 MySQL max_connections=400)。install.php 自动计算逻辑在生产环境会覆盖 .env 值。
5. **Nginx增加安全头和生产配置文件**
- 从 `deploy.md` 中提取生产Nginx配置为独立文件 `deploy/nginx_production.conf`
- 增加安全响应头、速率限制
6. **self_monitor增加自动修复逻辑**
- Redis丢失时:主动调用 `get_redis()` 触发重连(现有代码已支持,但需确保self_monitor层面触发)
```python
# Redis check — 主动触发重连
redis = await get_redis()
if redis is None and settings.REDIS_URL:
await send_telegram_system_alert("⚠️ Redis连接丢失,正在尝试重连...")
_redis_pool = None # 强制下次get_redis重新创建连接
redis = await get_redis()
if redis:
await send_telegram_system_alert("✅ Redis已重新连接")
```
### P2 — 中期规划(本月内)
7. **实现数据库自动备份**
```bash
# deploy/backup.sh
#!/bin/bash
BACKUP_DIR="/var/backups/nexus/mysql"
mkdir -p "$BACKUP_DIR"
mysqldump --single-transaction --routines --events Nexus | gzip > "$BACKUP_DIR/nexus_$(date +%Y%m%d).sql.gz"
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete # 保留30天
```
配置cron`0 3 * * * /www/wwwroot/api.synaglobal.vip/deploy/backup.sh`
8. **接入运维监控**
- 安装 `node_exporter` + `Prometheus` + `Grafana`
- 导入Nexus `/health` 端点到Prometheus
- 设置关键告警规则:CPU>90%、磁盘>85%、Python进程Down、MySQL连接数>150
9. **Redis Sentinel/Cluster高可用**
- 当前单点Redis:一旦宕机,所有实时心跳数据和告警状态丢失
- 建议部署Redis Sentinel实现自动故障切换,或至少配置Redis AOF持久化
### P3 — 长期规划
10. **容器化部署**Docker Compose/K8s
- 将FastAPI/Nginx/Redis/PHP容器化,消除环境不一致问题
- 便于水平扩展
11. **灰度发布策略**
- 实现蓝绿部署或canary发布机制,减少变更风险
---
## 7. 总结
Nexus的部署架构在整体设计上是稳健的,三层守护机制覆盖了进程崩溃、服务僵死、依赖服务故障等常见场景。然而,存在几个**必须立即处理**的风险:
1. **单worker瓶颈** — 这是当前最致命的性能隐患,2000+服务器的Agent心跳(60秒间隔)单worker可能无法承受
2. **循环重启风暴** — health_monitor脚本在连续失败后每分钟执行restart,可能造成服务抖动
3. **数据库连接池超配** — 100+100的池大小已经超过MySQL 151的默认上限,多worker后问题更严重
4. **Root运行和服务用户缺失** — 安全基线问题
5. **CORS全开** — ✅已修复:`NEXUS_CORS_ORIGINS` 环境变量白名单
短期改进优先级:
1. 先调低数据库连接池(写死的风险 > 配置不一致的风险)
2. 再修复health_monitor冷却期
3. 然后评估多worker+WebSocket兼容性后启用
4. 最后补充Nginx安全配置
以上改进建议的预计工时:P0项约2小时,P1项约4小时,P2项约2天(含Grafana部署调试)。