AI 量化平台 Linux 部署与公网访问排障实录
AI 量化平台 Linux 部署与公网访问排障实录
- 日期:2026-10-14
- 环境:阿里云 ECS(CentOS/Alibaba Linux,公网 IP
x.x.x.x),项目路径/root/ai_stock_server/ai-quant,Python 3.12(源码编译安装于/usr/local/python312) - 目标:在 Linux 服务器上跑通 AI 股票量化平台(
python server.py --port 8080),并通过 nginx 反向代理实现公网访问 - 排障主线:Python 依赖问题 → 解释器残缺问题 → 安全组端口问题 → nginx 域名分发问题 → 浏览器缓存假象
问题一:ModuleNotFoundError: No module named 'sklearn',且 pip install sklearn 构建失败
现象
启动服务报 ModuleNotFoundError: No module named 'sklearn';执行 pip install sklearn 后 pip 下载到一个可疑的 sklearn-0.0.post12.tar.gz(仅 2.6 kB),随后 Getting requirements to build wheel ... error 构建失败。
原因
sklearn 是一个废弃的占位空壳包,作者故意让它安装时报错,目的是引导用户安装正确的包。PyPI 上真正的包名是 scikit-learn(安装后 import 时模块名仍然是 sklearn,代码无需修改)。报错输出中也明确提示:
The ‘sklearn’ PyPI package is deprecated, use ‘scikit-learn’ rather than ‘sklearn’ for pip commands.
解决
pip install scikit-learn
# 国内源慢可换清华源:
pip install scikit-learn -i https://pypi.tuna.tsinghua.edu.cn/simple/
同时检查 requirements.txt,把其中的 sklearn 改为 scikit-learn。
验证:
python -c "from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor; print('ok')"
问题二:依赖装齐后仍报错 —— ImportError: The scipy install you are using seems to be broken
现象
pip install -r requirements.txt 成功执行,scikit-learn 1.9.1、scipy、joblib、narwhals、threadpoolctl 等均已安装。
但再次启动服务时,报错链变成了 scipy 加载失败:
File ".../scipy/_lib/_ccallback_c.py", line 1, in <module>
from . import _ccallback_c
...
ModuleNotFoundError: No module named '_ctypes'
...
ImportError: The scipy install you are using seems to be broken,
(external modules cannot be imported), please try reinstalling.
原因
关键在 ModuleNotFoundError: No module named '_ctypes'。
这台机器的 Python 3.12 是源码编译安装的(路径 /usr/local/python312/),当初编译时系统缺少 libffi-devel 开发库,导致 _ctypes 这个标准库 C 扩展根本没有被编译出来。scipy 依赖 ctypes,因此 import scipy 必然崩溃。
这是解释器本身残缺,不是 pip 包的问题——所以重装 scipy / scikit-learn 多少遍都无效。
判定方法:
python -c "import ctypes" # 同样报 No module named '_ctypes' 即确诊
解决(方案 A:补齐依赖后重新编译 Python,推荐)
# 1. 安装编译依赖
yum install -y libffi-devel gcc make openssl-devel bzip2-devel \
readline-devel sqlite-devel zlib-devel
# 2. 解压 Python 源码(没有源码就重新下载同版本)
wget https://www.python.org/ftp/python/3.12.x/Python-3.12.x.tgz
tar -xzf Python-3.12.x.tgz # 解压,-C 可指定目录
cd Python-3.12.x
# 3. 重新编译安装到同一 prefix(site-packages 里的包全部保留)
./configure --prefix=/usr/local/python312 --with-ensurepip=install
make -j4
make altinstall # 用 altinstall,避免覆盖系统软链
编译输出中留意 “Failed to build these modules” 列表,确认 _ctypes 不在其中。
验证:
python -c "import ctypes; print('ctypes ok')"
python -c "import scipy; print('scipy ok')"
备选方案 B:换 Miniconda(预编译 Python,ctypes/ssl 齐全),代价是所有依赖要重装一遍。本例选择了方案 A,因为已装好的依赖可以原地保留。
修复后服务启动成功(python ./server.py --port 8080 进程正常运行)。
问题三:nginx 反代配好,公网访问 x.x.x.x:8081 超时
现象
nginx 配置 /etc/nginx/conf.d/stock.conf:监听 8081,反代到 127.0.0.1:8080。
浏览器访问 http://x.x.x.x:8081 报 ERR_CONNECTION_TIMED_OUT(响应超时)。
原因
报错是 TIMED_OUT(超时)而不是 REFUSED(拒绝连接)——这个区别是关键线索:
| 浏览器报错 | 含义 | 排查方向 |
|---|---|---|
ERR_CONNECTION_TIMED_OUT | 数据包发出去石沉大海(被防火墙/安全组静默丢弃) | 云安全组、iptables/firewalld |
ERR_CONNECTION_REFUSED | 包到达了机器,但端口没人监听 | nginx 是否 listen、配置是否加载 |
即:阿里云安全组没有放行 8081 端口。8081 是非标准端口,不会像 80/443 那样默认放行。
解决
阿里云控制台 → ECS 实例 → 安全组 → 入方向 → 手动添加规则:
- 端口范围:TCP
8081/8081 - 授权对象:
0.0.0.0/0
修改即时生效,无需重启。添加后公网访问 http://x.x.x.x:8081 成功打开量化平台。
问题四:希望 stock.agenticbtx.com(默认 80 端口)直接访问量化平台
现象
同一台服务器上有两个服务:
stock.agenticbtx.com(80 端口)→ 错误地显示了”宝宝益智乐园”(游戏站)stock.agenticbtx.com:8081→ 正常显示 AI 量化平台
原因
nginx 在 80 端口上按 server_name 分发请求。stock.conf 当时只 listen 8081,没有任何 80 端口的 server 块精确匹配 stock.agenticbtx.com,请求便落到了游戏配置(30-game-http.conf)上,被当作默认站接走。
解决
在 stock.conf 中增加一行 listen 80;(nginx 匹配规则:精确 server_name 优先于 default_server,加上后不影响游戏域名):
server {
listen 8081;
listen 80; # 新增这一行
server_name stock.agenticbtx.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_read_timeout 300s; # 量化回测接口响应慢,放宽超时
proxy_send_timeout 300s;
client_max_body_size 50m;
}
location /ws { # WebSocket 支持(实时行情)
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
}
改完执行:
nginx -t && systemctl reload nginx
问题五(插曲):配置改完”还是不行”——浏览器缓存假象
现象
stock.conf 加上 listen 80 并 reload 后,浏览器访问 stock.agenticbtx.com 依然显示游戏页。
排查思路:绕开浏览器,在服务端自证清白
“改了没生效”的候选原因有四类:没 reload / server_name 撞车 / HTTPS 自动跳转 / 浏览器缓存。用 curl 在服务器本机直接验证,把问题域切开:
# 1. 带 Host 头直接打 80 端口,看返回状态
curl -sI -H "Host: stock.agenticbtx.com" http://127.0.0.1/
结果:HTTP/1.1 200 OK,且 grep -rn "server_name" /etc/nginx/conf.d/ 确认无撞车(游戏站是 game.agenticbtx.com,catchall 配置是 .bak 后缀不会被加载)。
# 2. 进一步确认返回内容是谁
curl -s -H "Host: stock.agenticbtx.com" http://127.0.0.1/ | grep -o "<title>[^<]*</title>"
结果:<title>AI 股票量化平台</title> —— nginx 侧已经完全正确。
真正原因
问题在浏览器:Chrome 对该域名有旧页面缓存,且会自动把曾访问过的站点升级到 HTTPS(HSTS/HTTPS-First),导致看到的仍是旧内容。
解决
- 无痕窗口(Ctrl+Shift+N)+ 显式输入
http://stock.agenticbtx.com - 或清除该站点数据 / 在
chrome://net-internals/#hsts删除域名的 HSTS 策略
清缓存后立刻正常。
最终结果
两个域名各归各位,全部验证通过:
| 访问方式 | 内容 |
|---|---|
http://stock.agenticbtx.com | AI 股票量化平台 |
http://game.agenticbtx.com | 宝宝益智乐园 |
http://x.x.x.x:8081 | AI 量化平台(备用入口) |
经验总结
- 包名 ≠ 模块名:
pip install用scikit-learn,代码里import sklearn。遇到 2~3 kB 的”秒装小包”要警惕是占位空壳。 - 源码编译 Python 必须预装
libffi-devel:缺它则_ctypes静默编译失败,症状是 scipy/numpy 等 C 扩展报 “seems to be broken”,重装 pip 包无效。补依赖后重新make altinstall到同一 prefix 即可原地保住所有已装包。 - 看浏览器报错类型定位网络层:
TIMED_OUT= 防火墙/安全组丢包;REFUSED= 端口没人监听。云主机非标准端口(8081 等)记得查安全组入方向。 - nginx 多站点靠 server_name 分发:精确域名匹配优先于 default_server;同端口同名冲突时按配置文件加载顺序(文件名字母序)先者胜出。
- “改了没生效”先绕开浏览器自证:
curl -H "Host: xxx" http://127.0.0.1/一条命令就能把”服务端对不对”和”浏览器显示对不对”分开,避免在正确的配置上继续瞎改。
后续待办
- HTTPS:
certbot --nginx -d stock.agenticbtx.com(游戏站同理),消除”不安全”提示 - 进程守护:把
server.py交给 systemd 管理(Restart=always),避免 SSH 断开/重启后服务消失 - 收口:80 端口稳定后删除
listen 8081并撤销安全组 8081 规则,减少暴露面