918bbed0fc
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service) - 移除登录页面上的明文密码提示 - 改进前端错误处理,避免数据加载失败导致登录显示错误
895 lines
23 KiB
Markdown
895 lines
23 KiB
Markdown
# 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
|