Files
sanguo_vnpy_v2/docs/design/deployment/docker-web-deployment.md
T
claude_dev 918bbed0fc fix: 修复登录500错误和移除明文密码提示
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service)
- 移除登录页面上的明文密码提示
- 改进前端错误处理,避免数据加载失败导致登录显示错误
2026-07-02 12:23:55 +08:00

895 lines
23 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.
# Docker Web 版本部署设计
## 1. 架构概述
### 1.1 整体架构
```
┌─────────────────────────────────────────────────┐
│ NAS 系统 │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Docker 容器 │ │
│ │ │ │
│ │ ┌───────────────────────────────────────┐ │ │
│ │ │ Nginx (反向代理) │ │ │
│ │ │ - HTTPS │ │ │
│ │ │ - 静态文件服务 │ │ │
│ │ └───────────────────────────────────────┘ │ │
│ │ ↑ │ │
│ │ ┌───────────────────────────────────────┐ │ │
│ │ │ FastAPI Web 服务 │ │ │
│ │ │ - REST API │ │ │
│ │ │ - WebSocket (实时行情) │ │ │
│ │ │ - 认证授权 │ │ │
│ │ └───────────────────────────────────────┘ │ │
│ │ ↑ │ │
│ │ ┌───────────────────────────────────────┐ │ │
│ │ │ VeighNa 核心引擎 │ │ │
│ │ │ - 交易接口 │ │ │
│ │ │ - 策略引擎 │ │ │
│ │ │ - 事件引擎 │ │ │
│ │ └───────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ SQLite │ │ 数据文件 │ │ 日志文件 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ ↓ ↓ ↓ │ │
│ │ 持久化卷 (Docker Volume) │ │
│ └────────────────────────────────────────────┘ │
│ │
│ NAS 存储 (数据持久化) │
└─────────────────────────────────────────────────┘
```
### 1.2 组件说明
| 组件 | 技术 | 说明 |
|-----|------|------|
| Web 框架 | FastAPI | 高性能异步 Web 框架 |
| 前端 | Vue.js 3 | 现代化响应式界面 |
| 反向代理 | Nginx | HTTPS + 静态文件 |
| 数据库 | SQLite | 轻量级,可选 PostgreSQL |
| 实时通信 | WebSocket | 行情推送 |
| 容器编排 | Docker Compose | 多容器管理 |
## 2. 技术方案
### 2.1 目录结构
```
sanguo_vnpy_v2/
├── docker/
│ ├── Dockerfile # 主容器镜像
│ ├── docker-compose.yml # 编排配置
│ ├── nginx/
│ │ ├── Dockerfile # Nginx 镜像
│ │ ├── nginx.conf # Nginx 配置
│ │ └── ssl/ # SSL 证书
│ ├── entrypoint.sh # 启动脚本
│ └── requirements-docker.txt # Docker 依赖
├── sanguo_web/ # Web 服务
│ ├── api/ # FastAPI 接口
│ │ ├── trading.py # 交易接口
│ │ ├── strategy.py # 策略接口
│ │ ├── data.py # 数据接口
│ │ └── auth.py # 认证接口
│ ├── websocket/ # WebSocket 处理
│ │ └── handler.py # 行情推送
│ ├── static/ # 前端静态文件
│ └── templates/ # HTML 模板
└── config/
├── docker_config.json # Docker 配置
└── users.json # 用户配置
```
### 2.2 Dockerfile 设计
```dockerfile
# 多阶段构建
FROM python:3.11-slim as builder
# 安装编译依赖
RUN apt-get update && apt-get install -y \
gcc g++ make \
&& rm -rf /var/lib/apt/lists/*
# 安装 TA-Lib
ENV TA_LIBRARY_PATH=/usr/local/lib
RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \
tar -xzf ta-lib-0.4.0-src.tar.gz && \
cd ta-lib && \
./configure --prefix=/usr && \
make && make install
# 复制依赖文件
COPY requirements-docker.txt .
RUN pip install --no-cache-dir -r requirements-docker.txt
# 运行阶段
FROM python:3.11-slim
# 复译 TA-Lib
COPY --from=builder /usr/lib/libta*.* /usr/local/lib/
COPY --from=builder /usr/include/ta-lib/ /usr/include/
# 设置工作目录
WORKDIR /app
# 复制应用代码
COPY sanguo_trader/ ./sanguo_trader/
COPY sanguo_research/ ./sanguo_research/
COPY sanguo_data/ ./sanguo_data/
COPY sanguo_common/ ./sanguo_common/
COPY sanguo_web/ ./sanguo_web/
COPY vnpy_v4.4.0/vnpy/ ./vnpy/
COPY config/ ./config/
# 暴露端口
EXPOSE 8000 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 启动命令
COPY docker/entrypoint.sh .
RUN chmod +x entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]
```
### 2.3 Docker Compose 配置
```yaml
version: '3.8'
services:
vnpy:
build:
context: .
dockerfile: docker/Dockerfile
container_name: sanguo-vnpy
restart: unless-stopped
# 环境变量
environment:
- PYTHONUNBUFFERED=1
- TZ=Asia/Shanghai
- VNPY_LOG_LEVEL=INFO
# 端口映射
ports:
- "8000:8000" # FastAPI
- "8080:8080" # WebSocket
# 数据卷
volumes:
- vnpy_data:/app/data
- vnpy_logs:/app/logs
- vnpy_config:/app/config
- ./config:/app/config:ro
# 网络
networks:
- vnpy-network
nginx:
build:
context: .
dockerfile: docker/nginx/Dockerfile
container_name: sanguo-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./docker/nginx/ssl:/etc/nginx/ssl:ro
- nginx_cache:/var/cache/nginx
depends_on:
- vnpy
networks:
- vnpy-network
# 数据卷
volumes:
vnpy_data:
driver: local
vnpy_logs:
driver: local
vnpy_config:
driver: local
nginx_cache:
driver: local
# 网络
networks:
vnpy-network:
driver: bridge
```
### 2.4 Web API 设计
#### REST API 端点
```python
# sanguo_web/api/__init__.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(
title="Sanguo VeighNa Web",
description="量化交易平台 Web API",
version="1.0.0"
)
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 挂载静态文件
app.mount("/static", StaticFiles(directory="static"), name="static")
# API 路由
from . import trading, strategy, data, auth
app.include_router(trading.router, prefix="/api/trading", tags=["交易"])
app.include_router(strategy.router, prefix="/api/strategy", tags=["策略"])
app.include_router(data.router, prefix="/api/data", tags=["数据"])
app.include_router(auth.router, prefix="/api/auth", tags=["认证"])
```
#### 交易接口示例
```python
# sanguo_web/api/trading.py
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
router = APIRouter()
class OrderRequest(BaseModel):
symbol: str
exchange: str
direction: str
offset: str
price: float
volume: int
price_type: str = "LIMIT"
@router.post("/orders")
async def send_order(req: OrderRequest):
"""发送订单"""
# 调用 VeighNa 引擎
order_id = engine.send_order(req)
return {"status": "success", "order_id": order_id}
@router.delete("/orders/{order_id}")
async def cancel_order(order_id: str):
"""撤销订单"""
engine.cancel_order(order_id)
return {"status": "success"}
@router.get("/orders")
async def get_orders():
"""查询订单"""
orders = engine.get_all_orders()
return {"orders": orders}
@router.get("/positions")
async def get_positions():
"""查询持仓"""
positions = engine.get_all_positions()
return {"positions": positions}
```
### 2.5 WebSocket 行情推送
```python
# sanguo_web/websocket/handler.py
from fastapi import WebSocket
from typing import Dict
import json
active_connections: Dict[str, WebSocket] = {}
async def websocket_endpoint(websocket: WebSocket, client_id: str):
await websocket.accept()
active_connections[client_id] = websocket
try:
while True:
# 接收客户端消息
data = await websocket.receive_text()
msg = json.loads(data)
if msg["type"] == "subscribe":
# 订阅行情
subscribe_market_data(client_id, msg["symbol"])
elif msg["type"] == "unsubscribe":
# 取消订阅
unsubscribe_market_data(client_id, msg["symbol"])
except Exception as e:
print(f"Connection error: {e}")
finally:
del active_connections[client_id]
async def broadcast_tick(tick_data):
"""广播行情数据"""
tick_json = json.dumps({
"type": "tick",
"data": tick_data
})
for connection in active_connections.values():
await connection.send_text(tick_json)
```
## 3. 部署方案
### 3.1 NAS 部署步骤
#### Synology NAS
```bash
# 1. 安装 Container Manager
# 2. 导入镜像
# 3. 创建项目
# 4. 启动容器
```
#### QNAP NAS
```bash
# 1. 安装 Container Station
# 2. 导入镜像
# 3. 创建容器
# 4. 启动
```
#### 通用 Linux NAS
```bash
# 1. 克隆项目
git clone http://192.168.2.154:3000/sanguo/sanguo_vnpy_v2
# 2. 启动服务
cd sanguo_vnpy_v2
docker-compose up -d
# 3. 查看日志
docker-compose logs -f
# 4. 停止服务
docker-compose down
```
### 3.2 配置管理
```yaml
# config/docker_config.json
{
"server": {
"host": "0.0.0.0",
"port": 8000,
"workers": 4
},
"database": {
"type": "sqlite",
"path": "/app/data/vnpy.db"
},
"logging": {
"level": "INFO",
"path": "/app/logs"
},
"security": {
"enable_auth": true,
"jwt_secret": "your-secret-key",
"session_timeout": 3600
},
"trading": {
"gateway_name": "CTP",
"md_address": "",
"td_address": "",
"userid": "",
"password": "",
"appid": "",
"authcode": ""
}
}
```
### 3.3 数据持久化
```bash
# Docker 卷管理
docker volume create vnpy_data
docker volume create vnpy_logs
docker volume create vnpy_config
# 查看卷
docker volume ls
# 备份数据
docker run --rm -v vnpy_data:/data -v $(pwd):/backup \
alpine tar czf /backup/vnpy_data_backup.tar.gz /data
```
## 4. 安全设计
### 4.1 认证方案
```python
# sanguo_web/api/auth.py
from fastapi import APIRouter, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from datetime import datetime, timedelta
router = APIRouter()
security = HTTPBearer()
SECRET_KEY = "your-secret-key"
@router.post("/login")
async def login(username: str, password: str):
"""用户登录"""
# 验证用户名密码
if verify_user(username, password):
token = create_access_token(username)
return {"access_token": token}
raise HTTPException(401, "Invalid credentials")
@router.get("/verify")
async def verify_token(
credentials: HTTPAuthorizationCredentials = Depends(security)
):
"""验证 Token"""
try:
payload = jwt.decode(
credentials.credentials,
SECRET_KEY,
algorithms=["HS256"]
)
return {"valid": True, "user": payload["sub"]}
except:
raise HTTPException(401, "Invalid token")
```
### 4.2 HTTPS 配置
```nginx
# docker/nginx/nginx.conf
server {
listen 443 ssl http2;
server_name localhost;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location / {
proxy_pass http://vnpy:8000;
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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /static/ {
alias /app/static/;
}
}
server {
listen 80;
server_name localhost;
return 301 https://$server_name$request_uri;
}
```
## 5. 监控与日志
### 5.1 健康检查
```python
@app.get("/health")
async def health_check():
"""健康检查接口"""
return {
"status": "healthy",
"services": {
"database": check_database(),
"gateway": check_gateway(),
"strategy": check_strategy()
}
}
```
### 5.2 日志配置
```python
# sanguo_common/logger.py
import logging
from pathlib import Path
def setup_logger():
"""配置日志"""
log_path = Path("/app/logs")
log_path.mkdir(exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_path / "vnpy.log"),
logging.StreamHandler()
]
)
```
## 6. 扩展设计
### 6.1 多容器部署
```yaml
# docker-compose.cluster.yml
version: '3.8'
services:
vnpy-api:
<<: *vnpy-service
container_name: sanguo-vnpy-api-1
vnpy-api-2:
<<: *vnpy-service
container_name: sanguo-vnpy-api-2
nginx:
# 负载均衡配置
# ...
```
### 6.2 数据库升级
```yaml
# 使用 PostgreSQL
services:
postgres:
image: postgres:15
container_name: sanguo-db
environment:
POSTGRES_DB: vnpy
POSTGRES_USER: vnpy
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
```
## 7. 实施计划
### 阶段 1: 基础框架
- [ ] 创建 Dockerfile
- [ ] 创建 docker-compose.yml
- [ ] 实现基础 Web API
- [ ] 实现认证授权
### 阶段 2: 核心功能
- [ ] 实现交易接口
- [ ] 实现策略接口
- [ ] 实现 WebSocket 行情
- [ ] 实现数据查询接口
### 阶段 3: 前端界面
- [ ] 实现登录页面
- [ ] 实现交易面板
- [ ] 实现行情显示
- [ ] 实现策略管理
### 阶段 4: 部署优化
- [ ] Nginx 配置
- [ ] HTTPS 配置
- [ ] 数据持久化
- [ ] 监控日志
### 阶段 5: 测试验证
- [ ] 功能测试
- [ ] 性能测试
- [ ] 部署测试
- [ ] 用户验收
## 8. 构建经验总结
### 8.1 已知问题与解决方案
#### 问题 1: polars CPU 兼容性
**错误现象**
```
Missing required CPU features: avx, avx2, fma, bmi1, bmi2, lzcnt
Container exit code: 132
```
**原因分析**
- NAS CPU (Intel Celeron J4125) 不支持 AVX2 指令集
- polars 默认版本依赖 AVX 指令优化性能
- 在不支持的 CPU 上运行会直接崩溃
**解决方案**
1. 修改 `requirements-docker.txt`:
```diff
- polars>=1.26.0
+ polars[rtcompat]>=1.26.0
```
2. 修改 `docker/entrypoint.sh`:
```bash
#!/bin/bash
set -e
export POLARS_SKIP_CPU_CHECK=1 # 添加此行
# ... 其余内容
```
#### 问题 2: 依赖冲突
**错误现象**
```
ERROR: Cannot install -r requirements-docker.txt (line 59)
```
**原因分析**
- statsmodels 与其他包版本冲突
- 部分科学计算包版本不兼容
**解决方案**
- 使用经过测试的固定版本组合
- 参考成功的 `rebuild2.log` 中的版本
#### 问题 3: 网络超时
**错误现象**
```
ERROR: Could not find a version that satisfies the requirement...
Connection timeout
```
**解决方案**
使用清华镜像加速:
```dockerfile
ARG PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
ARG PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn
```
### 8.2 成功构建配置
#### 最终 requirements-docker.txt 关键配置
```txt
# CPU 兼容性配置
polars[rtcompat]>=1.26.0
# 科学计算包(已验证版本)
numpy>=1.24.0,<2.0.0
pandas>=2.0.0
scipy>=1.10.0
statsmodels>=0.14.0
```
#### entrypoint.sh 配置
```bash
#!/bin/bash
set -e
# CPU 兼容性跳过
export POLARS_SKIP_CPU_CHECK=1
# 服务启动
cd /app
exec python -m uvicorn sanguo_web.main:app \
--host 0.0.0.0 \
--port 8000 \
--log-level info
```
## 9. 分层构建设计
### 9.1 设计目标
1. **分离依赖与应用**:固定的 Python 依赖包与变化的应用代码分离
2. **减少重建时间**:依赖包层只需构建一次,后续只重建应用层
3. **降低风险**:基础层冗余设计,包含所有可能需要的依赖
### 9.2 分层架构
```
┌─────────────────────────────────────────────┐
│ 应用层 (Dockerfile.nas) │
│ - sanguo_*/ 源代码 │
│ - config/ 配置文件 │
│ - 只在代码变更时重建 │
└─────────────────────────────────────────────┘
↓ FROM
┌─────────────────────────────────────────────┐
│ 基础层 (Dockerfile.base) │
│ - Python 3.11 │
│ - 所有依赖包 (~7GB) │
│ - TA-Lib │
│ - 只在依赖变更时重建 │
└─────────────────────────────────────────────┘
↓ FROM
┌─────────────────────────────────────────────┐
│ python:3.11-slim │
└─────────────────────────────────────────────┘
```
### 9.3 Dockerfile.base
```dockerfile
# sanguo_vnpy_v2/docker/Dockerfile.base
FROM python:3.11-slim
LABEL maintainer="sanguo"
LABEL description="Sanguo VeighNa Base Image with all dependencies"
# 设置工作目录
WORKDIR /build
# 安装系统依赖
RUN apt-get update && apt-get install -y \
gcc g++ make wget \
build-essential \
libssl-dev libffi-dev \
&& rm -rf /var/lib/apt/lists/*
# 安装 TA-Lib
ENV TA_LIBRARY_PATH=/usr/local/lib
ENV TA_HEADER_PATH=/usr/include
RUN wget -q http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \
tar -xzf ta-lib-0.4.0-src.tar.gz && \
cd ta-lib && \
./configure --prefix=/usr && \
make && make install && \
cd .. && \
rm -rf ta-lib ta-lib-0.4.0-src.tar.gz
# 使用清华镜像加速
ARG PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
ARG PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn
ENV PIP_INDEX_URL=${PIP_INDEX_URL}
ENV PIP_TRUSTED_HOST=${PIP_TRUSTED_HOST}
# CPU 兼容性配置
ENV POLARS_SKIP_CPU_CHECK=1
# 复制依赖文件
COPY requirements-docker.txt .
# 安装所有 Python 依赖
RUN pip install --no-cache-dir --root-user-action=ignore -r requirements-docker.txt
# 清理
RUN apt-get purge -y gcc g++ make wget && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/* /tmp/*
# 设置最终工作目录
WORKDIR /app
# 健康检查基础
RUN pip install --no-cache-dir uvicorn
# 元数据标签
LABEL build_date="2025-07-02"
LABEL python_version="3.11"
LABEL description="Sanguo VeighNa Base - Ready for application layer"
```
### 9.4 Dockerfile.nas (应用层)
```dockerfile
# sanguo_vnpy_v2/docker/Dockerfile.nas
FROM sanguo_vnpy:base
LABEL maintainer="sanguo"
LABEL description="Sanguo VeighNa Application Layer"
# 复制应用代码
COPY sanguo_trader/ ./sanguo_trader/
COPY sanguo_research/ ./sanguo_research/
COPY sanguo_data/ ./sanguo_data/
COPY sanguo_common/ ./sanguo_common/
COPY sanguo_web/ ./sanguo_web/
COPY vnpy_v4.4.0/vnpy/ ./vnpy/
COPY config/ ./config/
# 复制启动脚本
COPY docker/entrypoint.sh .
RUN chmod +x entrypoint.sh
# 暴露端口
EXPOSE 8000 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 启动
ENTRYPOINT ["./entrypoint.sh"]
```
### 9.5 构建流程
#### 首次构建(全量)
```bash
# 1. 构建基础镜像(约 30 分钟)
cd /volume1/stock/sanguo_vnpy_v2
docker build -f docker/Dockerfile.base -t sanguo_vnpy:base .
# 2. 构建应用镜像(约 5 分钟)
docker build -f docker/Dockerfile.nas -t sanguo_vnpy:latest .
# 3. 启动容器
docker run -d \
--name sanguo_vnpy_v2 \
-p 8000:8000 -p 8080:8080 \
-v /volume1/stock/sanguo_data:/app/data \
sanguo_vnpy:latest
```
#### 代码更新后(仅重建应用层)
```bash
# 只需重建应用镜像(约 5 分钟)
docker build -f docker/Dockerfile.nas -t sanguo_vnpy:latest .
# 重启容器
docker restart sanguo_vnpy_v2
```
### 9.6 依赖更新后(重建基础层)
```bash
# 当 requirements-docker.txt 变更时
docker build -f docker/Dockerfile.base -t sanguo_vnpy:base .
docker build -f docker/Dockerfile.nas -t sanguo_vnpy:latest .
```
### 9.7 文件结构
```
sanguo_vnpy_v2/
├── docker/
│ ├── Dockerfile.base # 基础镜像(依赖层)
│ ├── Dockerfile.nas # 应用镜像
│ ├── entrypoint.sh # 启动脚本
│ └── requirements-docker.txt # 依赖清单
├── sanguo_trader/ # 交易模块
├── sanguo_web/ # Web 服务
├── vnpy_v4.4.0/ # VeighNa 上游
└── config/ # 配置文件
```
## 10. 相关文档
- 需求文档: ../requirements/functional/feature-001-docker-web-deployment.md
- VeighNa 文档: ../../vnpy_v4.4.0/docs/
- 构建日志: ~/build.log, ~/rebuild2.log, ~/build4.log