代码修复: - 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>
18 KiB
运维总监审查报告
项目: Nexus 6.0 — 服务器运维管理平台 审查时间: 2026-05-21(2026-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 环境变量可以避免潜在的导入路径问题 |
建议修复
[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配置)
# 在 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=100DB_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 — 立即修复(影响生产稳定性)
-
Supervisor启用多Worker
# 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。 -
health_monitor.sh 增加重启冷却
# 在重启失败后增加冷却文件 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 -
创建专用服务用户
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 — 短期改进(本周内)
-
调优数据库连接池→ 已修正:config.py 默认值已调整为DB_POOL_SIZE=160+DB_MAX_OVERFLOW=120(基于 MySQL max_connections=400)。install.php 自动计算逻辑在生产环境会覆盖 .env 值。 -
Nginx增加安全头和生产配置文件
- 从
deploy.md中提取生产Nginx配置为独立文件deploy/nginx_production.conf - 增加安全响应头、速率限制
- 从
-
self_monitor增加自动修复逻辑
- Redis丢失时:主动调用
get_redis()触发重连(现有代码已支持,但需确保self_monitor层面触发)
# 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已重新连接") - Redis丢失时:主动调用
P2 — 中期规划(本月内)
-
实现数据库自动备份
# 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 -
接入运维监控
- 安装
node_exporter+Prometheus+Grafana - 导入Nexus
/health端点到Prometheus - 设置关键告警规则:CPU>90%、磁盘>85%、Python进程Down、MySQL连接数>150
- 安装
-
Redis Sentinel/Cluster高可用
- 当前单点Redis:一旦宕机,所有实时心跳数据和告警状态丢失
- 建议部署Redis Sentinel实现自动故障切换,或至少配置Redis AOF持久化
P3 — 长期规划
-
容器化部署(Docker Compose/K8s)
- 将FastAPI/Nginx/Redis/PHP容器化,消除环境不一致问题
- 便于水平扩展
-
灰度发布策略
- 实现蓝绿部署或canary发布机制,减少变更风险
7. 总结
Nexus的部署架构在整体设计上是稳健的,三层守护机制覆盖了进程崩溃、服务僵死、依赖服务故障等常见场景。然而,存在几个必须立即处理的风险:
- 单worker瓶颈 — 这是当前最致命的性能隐患,2000+服务器的Agent心跳(60秒间隔)单worker可能无法承受
- 循环重启风暴 — health_monitor脚本在连续失败后每分钟执行restart,可能造成服务抖动
- 数据库连接池超配 — 100+100的池大小已经超过MySQL 151的默认上限,多worker后问题更严重
- Root运行和服务用户缺失 — 安全基线问题
- CORS全开 — ✅已修复:
NEXUS_CORS_ORIGINS环境变量白名单
短期改进优先级:
- 先调低数据库连接池(写死的风险 > 配置不一致的风险)
- 再修复health_monitor冷却期
- 然后评估多worker+WebSocket兼容性后启用
- 最后补充Nginx安全配置
以上改进建议的预计工时:P0项约2小时,P1项约4小时,P2项约2天(含Grafana部署调试)。