QuantDinger 宝塔 Linux 部署完整指南
版本 : v4.0.x (PostgreSQL 18 + Redis 8)
适用 : 宝塔面板 / 纯 Linux 服务器
最后更新: 2026-08-16
目录
- 一、概述
- 二、系统要求
- 三、环境准备
- [3.1 安装 Docker](#3.1 安装 Docker)
- [3.2 配置 Docker 镜像加速](#3.2 配置 Docker 镜像加速)
- [3.3 创建 Swap 虚拟内存](#3.3 创建 Swap 虚拟内存)
- 四、部署方式一:官方镜像模式(快速)
- [4.1 拉取预构建镜像](#4.1 拉取预构建镜像)
- [4.2 克隆代码获取 compose 文件](#4.2 克隆代码获取 compose 文件)
- [4.3 修复 compose 配置](#4.3 修复 compose 配置)
- [4.4 配置环境变量](#4.4 配置环境变量)
- [4.5 启动服务](#4.5 启动服务)
- [4.6 验证部署](#4.6 验证部署)
- 五、部署方式二:源码构建模式(全本地)
- [5.1 克隆代码](#5.1 克隆代码)
- [5.2 修改前端 Dockerfile](#5.2 修改前端 Dockerfile)
- [5.3 配置环境变量](#5.3 配置环境变量)
- [5.4 构建并启动](#5.4 构建并启动)
- [5.5 验证部署](#5.5 验证部署)
- 六、一键部署脚本
- [6.1 源码构建一键脚本](#6.1 源码构建一键脚本)
- [6.2 官方镜像一键脚本](#6.2 官方镜像一键脚本)
- [七、配置宝塔反向代理与 SSL](#七、配置宝塔反向代理与 SSL)
- 八、日常管理命令
- [8.1 容器管理](#8.1 容器管理)
- [8.2 日志查看](#8.2 日志查看)
- [8.3 更新升级](#8.3 更新升级)
- [8.4 数据备份与恢复](#8.4 数据备份与恢复)
- [8.5 系统维护](#8.5 系统维护)
- 九、常见问题排查
- 十、安全建议
- 十一、两种部署模式对比
一、概述
QuantDinger 是一个量化交易平台,包含后端 API、前端 Web、移动端 H5、Celery 异步任务、PostgreSQL 数据库和 Redis 缓存等多个服务组件。
本指南提供两种部署方式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 官方镜像模式 | 直接从 GHCR 拉取预构建镜像,无需编译 | 快速部署、低内存服务器(≥1GB) |
| 源码构建模式 | 本地编译所有组件,不依赖 GHCR | GHCR 不通、需要自定义代码 |
服务架构
| 服务 | 容器名 | 端口 | 说明 |
|---|---|---|---|
| 前端 Web | quantdinger-frontend | 8888 | Vue 前端 |
| 移动端 H5 | quantdinger-mobile | 8889 | 移动端页面 |
| 后端 API | quantdinger-backend | 5000 | Python Gunicorn |
| PostgreSQL | quantdinger-db | 5432 | 数据库 |
| Redis | quantdinger-redis | 6379 | 缓存 |
| Redis Jobs | quantdinger-redis-jobs | - | 任务队列 |
| Celery Worker | quantdinger-celery-worker | - | 异步任务 |
| Celery Beat | quantdinger-celery-beat | - | 定时任务 |
| Scheduler Worker | quantdinger-scheduler-worker | - | 调度器 |
| Trading Worker | quantdinger-trading-worker | - | 交易执行 |
二、系统要求
| 项目 | 官方镜像模式 | 源码构建模式 |
|---|---|---|
| CPU | 1 核 | 1 核(推荐 2 核+) |
| 内存 | 1GB | 2GB(需加 8GB Swap) |
| 磁盘 | 10GB | 20GB |
| 系统 | CentOS 7+ / Ubuntu 20+ / alinux 3 | 同左 |
| Docker | 24.0+ | 24.0+ |
| Docker Compose | v2.20+ | v2.20+ |
三、环境准备
3.1 安装 Docker
bash
# 方式一:宝塔面板安装
# 宝塔面板 -> 软件商店 -> 搜索 "Docker 管理器" -> 安装
# 方式二:命令行安装
curl -fsSL https://get.docker.com | sh
systemctl enable docker
systemctl start docker
3.2 配置 Docker 镜像加速
国内服务器必须配置镜像加速,否则拉取基础镜像会超时。
bash
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://mirror.ccs.tencentyun.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
systemctl restart docker
# 验证(注意:URL 不应该有反引号)
docker info | grep -A5 "Registry Mirrors"
常见坑 :daemon.json 中的 URL 如果被反引号包裹会导致配置失效。正确格式是 "https://docker.m.daocloud.io",错误格式是 "`https://docker.m.daocloud.io`"。
3.3 创建 Swap 虚拟内存
仅源码构建模式需要。如果服务器内存 < 4GB,必须创建 Swap,否则前端编译时会 OOM 崩溃(JavaScript heap out of memory)。
bash
# 关闭旧 Swap
swapoff /swapfile 2>/dev/null
swapoff /www/swap 2>/dev/null
rm -f /swapfile /www/swap 2>/dev/null
# 创建 8GB Swap
fallocate -l 8G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 设置系统积极使用 Swap
sysctl vm.swappiness=100
sysctl vm.overcommit_memory=1
# 持久化(重启不丢失)
echo "/swapfile none swap sw 0 0" >> /etc/fstab
echo "vm.swappiness=100" >> /etc/sysctl.conf
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
# 验证
free -h
# Swap 应显示 8.0Gi
四、部署方式一:官方镜像模式(快速)
适用场景 :快速部署、服务器内存 ≥ 1GB、无需编译代码
原理 :直接从 GHCR(GitHub Container Registry)拉取预构建镜像,启动即可使用
部署时间:约 1-2 分钟
4.1 拉取预构建镜像
bash
# 后端镜像
docker pull ghcr.io/brokermr810/quantdinger-backend:latest
# 前端镜像
docker pull ghcr.io/brokermr810/quantdinger-frontend:latest
# 移动端镜像
docker pull ghcr.io/brokermr810/quantdinger-mobile:latest
# 验证
docker images | grep quantdinger
如果 GHCR 拉取失败,使用南京大学代理:
bash
# 南大 GHCR 代理
docker pull ghcr.nju.edu.cn/brokermr810/quantdinger-backend:latest
docker pull ghcr.nju.edu.cn/brokermr810/quantdinger-frontend:latest
docker pull ghcr.nju.edu.cn/brokermr810/quantdinger-mobile:latest
# 给代理镜像打上正确的 tag
docker tag ghcr.nju.edu.cn/brokermr810/quantdinger-backend:latest ghcr.io/brokermr810/quantdinger-backend:latest
docker tag ghcr.nju.edu.cn/brokermr810/quantdinger-frontend:latest ghcr.io/brokermr810/quantdinger-frontend:latest
docker tag ghcr.nju.edu.cn/brokermr810/quantdinger-mobile:latest ghcr.io/brokermr810/quantdinger-mobile:latest
4.2 克隆代码获取 compose 文件
bash
cd /www/wwwroot
# GitCode(国内推荐)
git clone https://gitcode.com/weixin_48616345/QuantDinger.git
# 备选:GitHub
# git clone https://github.com/brokermr810/QuantDinger.git
4.3 修复 compose 配置
compose 文件默认用 ghcr.io/openbyteinc/...,需改为 ghcr.io/brokermr810/...:
bash
cd /www/wwwroot/QuantDinger
# 修改镜像路径
sed -i 's|ghcr.io/openbyteinc/quantdinger-backend|ghcr.io/brokermr810/quantdinger-backend|g' docker-compose.ghcr.yml
sed -i 's|ghcr.io/openbyteinc/quantdinger-frontend|ghcr.io/brokermr810/quantdinger-frontend|g' docker-compose.ghcr.yml
sed -i 's|ghcr.io/openbyteinc/quantdinger-mobile|ghcr.io/brokermr810/quantdinger-mobile|g' docker-compose.ghcr.yml
# 修改 pull_policy(always -> missing,避免每次启动都去拉镜像)
sed -i 's/pull_policy: always/pull_policy: missing/g' docker-compose.ghcr.yml
4.4 配置环境变量
4.4.1 创建 backend.env
bash
cd /www/wwwroot/QuantDinger
# 从模板创建
cp backend_api_python/env.example backend_api_python/.env
# 生成 SECRET_KEY
SECRET_KEY=$(openssl rand -hex 32)
# 设置管理员账号和密码
ADMIN_USER="admin"
ADMIN_PASSWORD="你的密码" # 至少 8 位
# 获取服务器 IP
SERVER_IP=$(curl -s ifconfig.me)
# 写入配置
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${SECRET_KEY}|" backend_api_python/.env
sed -i "s|^ADMIN_USER=.*|ADMIN_USER=${ADMIN_USER}|" backend_api_python/.env
sed -i "s|^ADMIN_PASSWORD=.*|ADMIN_PASSWORD=${ADMIN_PASSWORD}|" backend_api_python/.env
sed -i "s|^FRONTEND_URL=.*|FRONTEND_URL=http://${SERVER_IP}|" backend_api_python/.env
# 复制为 backend.env(compose 挂载的是 ./backend.env)
cp backend_api_python/.env backend.env
4.4.2 创建 Compose 编排配置
bash
cat > /www/wwwroot/QuantDinger/.env << EOF
POSTGRES_DB=quantdinger
POSTGRES_USER=quantdinger
POSTGRES_PASSWORD=quantdinger123
# 端口配置(纯端口号,不要带 127.0.0.1: 前缀)
DB_PORT=5432
REDIS_PORT=6379
BACKEND_PORT=5000
FRONTEND_PORT=8888
MOBILE_PORT=8889
FRONTEND_URL=http://${SERVER_IP}
# 镜像路径覆盖
BACKEND_IMAGE=ghcr.io/brokermr810/quantdinger-backend
FRONTEND_IMAGE=ghcr.io/brokermr810/quantdinger-frontend
MOBILE_IMAGE=ghcr.io/brokermr810/quantdinger-mobile
EOF
重要 :端口变量必须是纯端口号(如 5000),不能带 IP 前缀(如 127.0.0.1:5000),否则会报 invalid IP address: 127.0.0.1:127.0.0.1 错误。
4.5 启动服务
bash
cd /www/wwwroot/QuantDinger
# 启动(无需编译,几秒钟完成)
docker compose -f docker-compose.ghcr.yml up -d
# 查看容器状态
docker compose -f docker-compose.ghcr.yml ps
# 查看日志
docker compose -f docker-compose.ghcr.yml logs -f
4.6 验证部署
bash
# 后端健康检查
curl http://127.0.0.1:5000/api/health
# 前端页面
curl -I http://127.0.0.1:8888
# 移动端页面
curl -I http://127.0.0.1:8889
五、部署方式二:源码构建模式(全本地)
适用场景 :无法访问 GHCR 镜像仓库、需要自定义代码、服务器内存 ≥ 2GB(需加 Swap)
部署时间:约 10-20 分钟(取决于网络和磁盘速度)
5.1 克隆代码
5.1.1 克隆主仓库
bash
cd /www/wwwroot
# 方式一:GitCode(国内推荐)
git clone https://gitcode.com/weixin_48616345/QuantDinger.git
# 方式二:GitHub 加速
git clone https://ghproxy.com/https://github.com/brokermr810/QuantDinger.git
# 方式三:GitHub 直连
git clone https://github.com/brokermr810/QuantDinger.git
5.1.2 克隆前端仓库(全源码构建必须)
bash
cd /www/wwwroot/QuantDinger
# Vue 前端
git clone https://gitcode.com/weixin_48616345/QuantDinger-Vue.git
# 备选:git clone https://ghproxy.com/https://github.com/brokermr810/QuantDinger-Vue.git
# 移动端 H5
git clone https://gitee.com/ycwhmm/QuantDinger-Mobile.git
# 备选:git clone https://ghproxy.com/https://github.com/brokermr810/QuantDinger-Mobile.git
5.2 修改前端 Dockerfile
在 pnpm build 前加 NODE_OPTIONS,防止 V8 堆内存溢出:
bash
cd /www/wwwroot/QuantDinger/QuantDinger-Vue
sed -i 's/pnpm build/NODE_OPTIONS=--max-old-space-size=4096 pnpm build/g' Dockerfile
# 验证修改
grep NODE_OPTIONS Dockerfile
# 应输出包含 NODE_OPTIONS=--max-old-space-size=4096 的内容
5.3 配置环境变量
5.3.1 创建 backend.env
bash
cd /www/wwwroot/QuantDinger
# 从模板创建
cp backend_api_python/env.example backend_api_python/.env
# 生成 SECRET_KEY
SECRET_KEY=$(openssl rand -hex 32)
# 设置管理员账号和密码
ADMIN_USER="admin"
ADMIN_PASSWORD="你的密码" # 至少 8 位
# 获取服务器 IP
SERVER_IP=$(curl -s ifconfig.me)
# 写入配置
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${SECRET_KEY}|" backend_api_python/.env
sed -i "s|^ADMIN_USER=.*|ADMIN_USER=${ADMIN_USER}|" backend_api_python/.env
sed -i "s|^ADMIN_PASSWORD=.*|ADMIN_PASSWORD=${ADMIN_PASSWORD}|" backend_api_python/.env
sed -i "s|^FRONTEND_URL=.*|FRONTEND_URL=http://${SERVER_IP}|" backend_api_python/.env
# 复制为 backend.env(compose 挂载的是这个文件)
cp backend_api_python/.env backend.env
5.3.2 创建 Compose 编排配置
bash
cat > /www/wwwroot/QuantDinger/.env << EOF
POSTGRES_DB=quantdinger
POSTGRES_USER=quantdinger
POSTGRES_PASSWORD=quantdinger123
# 端口配置(纯端口号,不要带 127.0.0.1: 前缀)
DB_PORT=5432
REDIS_PORT=6379
BACKEND_PORT=5000
FRONTEND_PORT=8888
MOBILE_PORT=8889
FRONTEND_URL=http://${SERVER_IP}
EOF
5.4 构建并启动
方式一:后台构建(推荐,终端断开不影响)
bash
cd /www/wwwroot/QuantDinger
nohup docker compose -f docker-compose.build.yml up -d --build > /tmp/build.log 2>&1 &
# 查看构建进度
tail -f /tmp/build.log
方式二:前台构建(可实时看到输出)
bash
cd /www/wwwroot/QuantDinger
docker compose -f docker-compose.build.yml up -d --build
5.5 验证部署
bash
cd /www/wwwroot/QuantDinger
# 查看容器状态
docker compose -f docker-compose.build.yml ps
# 查看日志
docker compose -f docker-compose.build.yml logs -f
# 健康检查
curl http://127.0.0.1:5000/api/health
curl -I http://127.0.0.1:8888
六、一键部署脚本
6.1 源码构建一键脚本
文件名:
quantdinger-build-deploy.sh功能:自动检测系统环境、配置 Swap、克隆代码、修改 Dockerfile、配置环境变量、构建并启动
bash
#!/bin/bash
# =============================================================
# QuantDinger 宝塔 Linux 一键部署脚本 - 源码构建版
# 适用:低内存服务器(2GB+,需加 swap)
# 架构:PostgreSQL 18 + Redis 8 + 全源码本地构建
# =============================================================
set -euo pipefail
# ==================== 配置区 ====================
DEPLOY_DIR="/www/wwwroot/QuantDinger"
LOG_FILE="/tmp/quantdinger_build_$(date +%Y%m%d_%H%M%S).log"
GIT_TERMINAL_PROMPT=0
export GIT_TERMINAL_PROMPT
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $1"; }
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
step() { echo -e "\n${CYAN}[STEP]${NC} $1"; }
# ==================== 函数定义 ====================
check_system() {
step "检测系统环境..."
info "操作系统: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d'"' -f2 || echo unknown)"
info "CPU 架构: $(uname -m)"
local total_mem=$(free -m | awk '/^Mem:/{print $2}')
info "总内存: ${total_mem} MB"
if [ "$total_mem" -lt 4096 ]; then
warn "内存不足 4GB,源码构建可能 OOM"
check_swap
fi
}
check_swap() {
local swap_total=$(free -m | awk '/^Swap:/{print $2}')
if [ "$swap_total" -lt 2048 ]; then
warn "当前 Swap 仅 ${swap_total}MB,源码构建需要更多虚拟内存"
echo ""
echo " 是否创建 Swap 虚拟内存(用磁盘当内存)?"
echo " - 2GB 内存服务器:建议创建 8GB Swap"
echo " - 4GB 内存服务器:建议创建 4GB Swap"
echo ""
read -p " 创建 Swap?(y/n, 默认 y): " create_swap
create_swap=${create_swap:-y}
if [[ "$create_swap" == "y" || "$create_swap" == "Y" ]]; then
local swap_size=8
if [ "$total_mem" -ge 4096 ]; then
swap_size=4
fi
local swap_path="/swapfile"
# 如果已有 swap,先关掉
swapoff /swapfile 2>/dev/null || true
swapoff /www/swap 2>/dev/null || true
rm -f /swapfile /www/swap 2>/dev/null || true
info "创建 ${swap_size}GB Swap..."
fallocate -l ${swap_size}G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 设置系统积极使用 swap
sysctl vm.swappiness=100
sysctl vm.overcommit_memory=1
# 持久化
if ! grep -q "/swapfile" /etc/fstab 2>/dev/null; then
echo "/swapfile none swap sw 0 0" >> /etc/fstab
fi
if ! grep -q "vm.swappiness=100" /etc/sysctl.conf 2>/dev/null; then
echo "vm.swappiness=100" >> /etc/sysctl.conf
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
fi
info "Swap 创建完成: $(free -h | awk '/^Swap:/{print $2}')"
else
warn "跳过 Swap 创建,构建可能因内存不足失败"
fi
else
info "Swap 已有 ${swap_total}MB,满足要求"
fi
}
check_docker() {
step "检查 Docker 环境..."
if ! command -v docker &>/dev/null; then
error "Docker 未安装!"
echo ""
echo " 安装方式:"
echo " 1. 宝塔面板 -> 软件商店 -> 搜索 Docker 管理器"
echo " 2. 命令行: curl -fsSL https://get.docker.com | sh"
echo ""
exit 1
fi
info "Docker 已安装: $(docker --version)"
if ! docker compose version &>/dev/null; then
error "Docker Compose 未安装!"
exit 1
fi
info "Docker Compose 已安装: $(docker compose version)"
}
configure_mirror() {
step "配置 Docker 镜像加速..."
if [ -f /etc/docker/daemon.json ]; then
info "Docker 配置已存在,检查是否需要修复..."
if grep -q '`' /etc/docker/daemon.json 2>/dev/null; then
warn "检测到 daemon.json 中有非法反引号,修复中..."
sed -i 's/`//g' /etc/docker/daemon.json
systemctl restart docker
info "已修复并重启 Docker"
else
info "镜像加速配置正常,跳过"
fi
return
fi
info "创建 Docker 镜像加速配置..."
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://mirror.ccs.tencentyun.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
systemctl restart docker
info "镜像加速配置完成"
}
clone_repo() {
step "获取 QuantDinger 代码..."
if [ -d "$DEPLOY_DIR/.git" ]; then
info "代码目录已存在: $DEPLOY_DIR"
read -p "是否执行 git pull 更新?(y/n, 默认 n): " do_pull
if [[ "$do_pull" == "y" || "$do_pull" == "Y" ]]; then
cd "$DEPLOY_DIR" && git pull || warn "git pull 失败,使用现有代码"
else
info "使用现有代码"
fi
else
info "克隆代码到: $DEPLOY_DIR"
local repos=(
"https://gitcode.com/weixin_48616345/QuantDinger.git"
"https://ghproxy.com/https://github.com/brokermr810/QuantDinger.git"
"https://github.com/brokermr810/QuantDinger.git"
)
local cloned=false
for repo_url in "${repos[@]}"; do
info "尝试: $repo_url"
if git clone --depth 1 "$repo_url" "$DEPLOY_DIR" >> "$LOG_FILE" 2>&1; then
info "克隆成功: $repo_url"
cloned=true
break
else
warn "克隆失败: $repo_url"
rm -rf "$DEPLOY_DIR" 2>/dev/null
fi
done
if [ "$cloned" = false ]; then
error "所有源均克隆失败,请手动克隆代码"
exit 1
fi
fi
if [ -f "$DEPLOY_DIR/docker-compose.build.yml" ]; then
info "代码验证通过(支持全源码构建)"
else
error "代码验证失败:未找到 docker-compose.build.yml"
exit 1
fi
}
clone_frontend_repos() {
step "获取前端仓库(全源码构建需要)..."
local vue_dir="$DEPLOY_DIR/QuantDinger-Vue"
local mobile_dir="$DEPLOY_DIR/QuantDinger-Mobile"
# QuantDinger-Vue
if [ -d "$vue_dir/.git" ]; then
info "QuantDinger-Vue 已存在,跳过"
else
local vue_repos=(
"https://gitcode.com/weixin_48616345/QuantDinger-Vue.git"
"https://ghproxy.com/https://github.com/brokermr810/QuantDinger-Vue.git"
"https://github.com/brokermr810/QuantDinger-Vue.git"
)
local cloned=false
for repo_url in "${vue_repos[@]}"; do
info "尝试克隆 QuantDinger-Vue: $repo_url"
if git clone --depth 1 "$repo_url" "$vue_dir" >> "$LOG_FILE" 2>&1; then
info "QuantDinger-Vue 克隆成功"
cloned=true
break
else
warn "克隆失败: $repo_url"
rm -rf "$vue_dir" 2>/dev/null
fi
done
if [ "$cloned" = false ]; then
error "QuantDinger-Vue 克隆失败"
exit 1
fi
fi
# QuantDinger-Mobile
if [ -d "$mobile_dir/.git" ]; then
info "QuantDinger-Mobile 已存在,跳过"
else
local mobile_repos=(
"https://gitee.com/ycwhmm/QuantDinger-Mobile.git"
"https://ghproxy.com/https://github.com/brokermr810/QuantDinger-Mobile.git"
"https://github.com/brokermr810/QuantDinger-Mobile.git"
)
local cloned=false
for repo_url in "${mobile_repos[@]}"; do
info "尝试克隆 QuantDinger-Mobile: $repo_url"
if git clone --depth 1 "$repo_url" "$mobile_dir" >> "$LOG_FILE" 2>&1; then
info "QuantDinger-Mobile 克隆成功"
cloned=true
break
else
warn "克隆失败: $repo_url"
rm -rf "$mobile_dir" 2>/dev/null
fi
done
if [ "$cloned" = false ]; then
error "QuantDinger-Mobile 克隆失败"
exit 1
fi
fi
info "前端仓库就绪"
}
patch_dockerfile_memory() {
step "修改前端 Dockerfile,增加内存限制..."
local vue_dockerfile="$DEPLOY_DIR/QuantDinger-Vue/Dockerfile"
if [ -f "$vue_dockerfile" ]; then
if ! grep -q "max-old-space-size" "$vue_dockerfile"; then
sed -i 's/pnpm build/NODE_OPTIONS=--max-old-space-size=4096 pnpm build/g' "$vue_dockerfile"
info "已在前端 Dockerfile 中添加 4GB V8 堆内存限制"
else
info "Dockerfile 已有内存限制配置,跳过"
fi
fi
}
interactive_config() {
step "配置 QuantDinger 参数..."
echo ""
echo "================================================="
echo " 请填写以下配置信息 "
echo "================================================="
echo ""
read -p "管理员用户名 (默认 admin): " ADMIN_USER
ADMIN_USER=${ADMIN_USER:-admin}
while true; do
read -s -p "管理员密码 (至少 8 位): " ADMIN_PASSWORD
echo ""
if [ ${#ADMIN_PASSWORD} -ge 8 ]; then
read -s -p "确认密码: " ADMIN_PASSWORD_CONFIRM
echo ""
if [ "$ADMIN_PASSWORD" == "$ADMIN_PASSWORD_CONFIRM" ]; then
break
else
warn "两次密码不一致,请重新输入"
fi
else
warn "密码太短,至少 8 位"
fi
done
info "管理员账号已设置: $ADMIN_USER"
local server_ip=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")
read -p "访问地址(域名或IP,默认 $server_ip): " frontend_url
frontend_url=${frontend_url:-$server_ip}
FRONTEND_URL="http://$frontend_url"
info "FRONTEND_URL 将设为: $FRONTEND_URL"
echo ""
echo " 如果服务器在国内,访问交易所和 AI 服务可能需要代理。"
read -p "是否配置网络代理?(y/n, 默认 n): " config_proxy
if [[ "$config_proxy" == "y" || "$config_proxy" == "Y" ]]; then
read -p "代理地址(如 socks5h://host.docker.internal:10808): " proxy_url
if [ -z "$proxy_url" ]; then
warn "代理地址为空,跳过"
fi
fi
}
generate_secret_key() {
step "生成 SECRET_KEY..."
SECRET_KEY=$(openssl rand -hex 32)
info "SECRET_KEY 已生成(64 字符)"
}
configure_env() {
step "配置环境变量文件..."
local env_file="$DEPLOY_DIR/backend_api_python/.env"
local env_template="$DEPLOY_DIR/backend_api_python/env.example"
if [ ! -f "$env_file" ]; then
if [ -f "$env_template" ]; then
cp "$env_template" "$env_file"
info "已从模板创建 .env 文件"
else
error "未找到 env.example 模板文件"
exit 1
fi
else
info ".env 文件已存在,将更新关键配置"
fi
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${SECRET_KEY}|" "$env_file" 2>/dev/null || echo "SECRET_KEY=${SECRET_KEY}" >> "$env_file"
sed -i "s|^ADMIN_USER=.*|ADMIN_USER=${ADMIN_USER}|" "$env_file" 2>/dev/null || echo "ADMIN_USER=${ADMIN_USER}" >> "$env_file"
sed -i "s|^ADMIN_PASSWORD=.*|ADMIN_PASSWORD=${ADMIN_PASSWORD}|" "$env_file" 2>/dev/null || echo "ADMIN_PASSWORD=${ADMIN_PASSWORD}" >> "$env_file"
sed -i "s|^FRONTEND_URL=.*|FRONTEND_URL=${FRONTEND_URL}|" "$env_file" 2>/dev/null || echo "FRONTEND_URL=${FRONTEND_URL}" >> "$env_file"
if [ -n "${proxy_url:-}" ]; then
sed -i "s|^PROXY_URL=.*|PROXY_URL=${proxy_url}|" "$env_file" 2>/dev/null || echo "PROXY_URL=${proxy_url}" >> "$env_file"
fi
info "环境变量配置完成"
info "配置文件: $env_file"
local compose_env="$DEPLOY_DIR/.env"
cat > "$compose_env" << EOF
# Compose 编排配置
POSTGRES_DB=quantdinger
POSTGRES_USER=quantdinger
POSTGRES_PASSWORD=quantdinger123
FRONTEND_URL=${FRONTEND_URL}
EOF
info "Compose 编排配置已创建: $compose_env"
cp "$env_file" "$DEPLOY_DIR/backend.env"
info "已创建 backend.env"
}
start_docker_services() {
step "启动 Docker 服务..."
cd "$DEPLOY_DIR"
info "使用全源码构建模式(docker-compose.build.yml)"
info "构建并启动(首次编译约需 10-20 分钟,请耐心等待)..."
info "此模式完全不依赖 GHCR,基础镜像走国内加速"
info "如果内存不足,脚本已自动配置 Swap 虚拟内存"
info ""
info "建议使用 nohup 后台运行,避免终端断开中断构建"
info "日志文件: $LOG_FILE"
info ""
read -p "是否后台运行构建?(y/n, 默认 y): " bg_run
bg_run=${bg_run:-y}
if [[ "$bg_run" == "y" || "$bg_run" == "Y" ]]; then
nohup docker compose -f docker-compose.build.yml up -d --build > /tmp/quantdinger_build.log 2>&1 &
info "构建已在后台启动"
info "查看进度: tail -f /tmp/quantdinger_build.log"
info "等待 10-20 分钟后检查状态: cd $DEPLOY_DIR && docker compose -f docker-compose.build.yml ps"
else
info "前台运行构建..."
docker compose -f docker-compose.build.yml up -d --build 2>&1 | tee "$LOG_FILE"
if [ ${PIPESTATUS[0]} -ne 0 ]; then
error "构建失败"
warn "常见原因: 内存不足、网络问题、前端仓库未克隆"
warn "如内存不足,请确保已创建足够 Swap"
exit 1
fi
fi
}
show_result() {
step "验证部署状态..."
sleep 10
echo ""
echo "================================================="
echo " 容器运行状态 "
echo "================================================="
echo ""
cd "$DEPLOY_DIR"
docker compose -f docker-compose.build.yml ps 2>/dev/null || echo " 容器可能仍在启动中..."
echo ""
echo "================================================="
echo " 部署完成!后续步骤 "
echo "================================================="
echo ""
echo "1. 配置宝塔反向代理(必须)"
echo " 宝塔面板 -> 网站 -> 添加站点"
echo " - 域名: $(echo $FRONTEND_URL | sed 's|https\?://||')"
echo " - PHP版本: 纯静态"
echo " - 创建后 -> 设置 -> 反向代理"
echo " - 目标URL: http://127.0.0.1:8888"
echo ""
echo "2. 配置 SSL 证书(推荐)"
echo " 网站设置 -> SSL -> Let's Encrypt -> 申请"
echo " 开启「强制 HTTPS」"
echo ""
echo "3. 更新 FRONTEND_URL(如已配置 HTTPS)"
echo " 编辑: $DEPLOY_DIR/backend_api_python/.env"
echo " 修改: FRONTEND_URL=https://你的域名"
echo " 重启: docker compose -f docker-compose.build.yml restart backend"
echo ""
echo "4. 访问 QuantDinger"
echo " 地址: $FRONTEND_URL(配置反代后)"
echo " 账号: $ADMIN_USER"
echo " 密码: (你设置的密码)"
echo ""
echo "日志文件: $LOG_FILE"
echo ""
}
# ==================== 主流程 ====================
main() {
echo ""
echo "============================================================="
echo " QuantDinger 宝塔 Linux 一键部署脚本(源码构建版) "
echo " PostgreSQL 18 + Redis 8 + 全源码本地构建 "
echo "============================================================="
echo ""
echo " 部署目录: $DEPLOY_DIR"
echo " 日志文件: $LOG_FILE"
echo ""
check_system
check_docker
configure_mirror
clone_repo
clone_frontend_repos
patch_dockerfile_memory
interactive_config
generate_secret_key
configure_env
start_docker_services
show_result
log "QuantDinger 部署脚本执行完成!"
}
main "$@"
使用方法:
bash
# 赋予执行权限
chmod +x quantdinger-build-deploy.sh
# 执行
sudo bash quantdinger-build-deploy.sh
6.2 官方镜像一键脚本
文件名:
quantdinger-ghcr-deploy.sh功能:自动检测系统环境、配置镜像加速、拉取 GHCR 镜像(含代理回退)、克隆代码、修复 compose 配置、配置环境变量、启动服务
bash
#!/bin/bash
# =============================================================
# QuantDinger 宝塔 Linux 一键部署脚本 - 官方镜像版
# 适用:所有服务器(无需编译,启动快,内存需求低)
# 架构:拉取 GHCR 预构建镜像 + PostgreSQL 18 + Redis 8
# =============================================================
set -euo pipefail
# ==================== 配置区 ====================
DEPLOY_DIR="/www/wwwroot/QuantDinger"
LOG_FILE="/tmp/quantdinger_ghcr_$(date +%Y%m%d_%H%M%S).log"
GIT_TERMINAL_PROMPT=0
export GIT_TERMINAL_PROMPT
# 镜像源
BACKEND_IMAGE="ghcr.io/brokermr810/quantdinger-backend"
FRONTEND_IMAGE="ghcr.io/brokermr810/quantdinger-frontend"
MOBILE_IMAGE="ghcr.io/brokermr810/quantdinger-mobile"
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $1"; }
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
step() { echo -e "\n${CYAN}[STEP]${NC} $1"; }
# ==================== 函数定义 ====================
check_system() {
step "检测系统环境..."
info "操作系统: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d'"' -f2 || echo unknown)"
info "CPU 架构: $(uname -m)"
local total_mem=$(free -m | awk '/^Mem:/{print $2}')
info "总内存: ${total_mem} MB"
if [ "$total_mem" -lt 1024 ]; then
warn "内存不足 1GB,可能影响运行"
else
info "内存充足,官方镜像模式不需要编译,内存消耗低"
fi
local server_ip=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")
info "检测到服务器 IP: $server_ip"
echo "$server_ip" > /tmp/quantdinger_server_ip
}
check_docker() {
step "检查 Docker 环境..."
if ! command -v docker &>/dev/null; then
error "Docker 未安装!"
echo ""
echo " 安装方式:"
echo " 1. 宝塔面板 -> 软件商店 -> 搜索 Docker 管理器"
echo " 2. 命令行: curl -fsSL https://get.docker.com | sh"
echo ""
exit 1
fi
info "Docker 已安装: $(docker --version)"
if ! docker compose version &>/dev/null; then
error "Docker Compose 未安装!"
exit 1
fi
info "Docker Compose 已安装: $(docker compose version)"
}
configure_mirror() {
step "配置 Docker 镜像加速..."
local need_fix=false
local need_create=true
if [ -f /etc/docker/daemon.json ]; then
need_create=false
if grep -q '`' /etc/docker/daemon.json 2>/dev/null; then
warn "检测到 daemon.json 中有非法反引号字符"
need_fix=true
fi
if ! grep -q "registry-mirrors" /etc/docker/daemon.json 2>/dev/null; then
need_fix=true
fi
fi
if [ "$need_create" = true ] || [ "$need_fix" = true ]; then
info "写入正确的镜像加速配置..."
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://mirror.ccs.tencentyun.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
systemctl restart docker
info "镜像加速配置完成并已重启 Docker"
else
info "镜像加速配置正常"
fi
info "当前加速源:"
docker info 2>/dev/null | grep -A5 "Registry Mirrors" | head -6
}
pull_images() {
step "拉取官方预构建镜像..."
info "从 GHCR 拉取预构建镜像(无需编译,直接启动)"
info "镜像列表:"
info " 后端: $BACKEND_IMAGE"
info " 前端: $FRONTEND_IMAGE"
info " 移动端: $MOBILE_IMAGE"
info ""
local images=("$BACKEND_IMAGE" "$FRONTEND_IMAGE" "$MOBILE_IMAGE")
for img in "${images[@]}"; do
info "拉取: $img"
if docker pull "$img:latest" >> "$LOG_FILE" 2>&1; then
info "拉取成功: $img"
else
# 尝试南大代理
local proxy_img=$(echo "$img" | sed "s|ghcr.io|ghcr.nju.edu.cn|")
warn "直接拉取失败,尝试南大代理: $proxy_img"
if docker pull "$proxy_img:latest" >> "$LOG_FILE" 2>&1; then
docker tag "$proxy_img:latest" "$img:latest"
info "通过代理拉取成功: $img"
else
error "拉取失败: $img"
error "请检查网络或手动配置代理后重试"
exit 1
fi
fi
done
info "所有镜像拉取完成"
docker images | grep quantdinger
}
clone_repo() {
step "获取 QuantDinger 代码(用于 compose 文件和配置)..."
if [ -d "$DEPLOY_DIR/.git" ]; then
info "代码目录已存在: $DEPLOY_DIR"
read -p "是否执行 git pull 更新?(y/n, 默认 n): " do_pull
if [[ "$do_pull" == "y" || "$do_pull" == "Y" ]]; then
cd "$DEPLOY_DIR" && git pull || warn "git pull 失败,使用现有代码"
else
info "使用现有代码"
fi
else
info "克隆代码到: $DEPLOY_DIR"
local repos=(
"https://gitcode.com/weixin_48616345/QuantDinger.git"
"https://ghproxy.com/https://github.com/brokermr810/QuantDinger.git"
"https://github.com/brokermr810/QuantDinger.git"
)
local cloned=false
for repo_url in "${repos[@]}"; do
info "尝试: $repo_url"
if git clone --depth 1 "$repo_url" "$DEPLOY_DIR" >> "$LOG_FILE" 2>&1; then
info "克隆成功: $repo_url"
cloned=true
break
else
warn "克隆失败: $repo_url"
rm -rf "$DEPLOY_DIR" 2>/dev/null
fi
done
if [ "$cloned" = false ]; then
error "所有源均克隆失败,请手动克隆代码"
exit 1
fi
fi
if [ -f "$DEPLOY_DIR/docker-compose.ghcr.yml" ]; then
info "代码验证通过(支持 GHCR 模式)"
else
error "代码验证失败:未找到 docker-compose.ghcr.yml"
exit 1
fi
}
patch_compose_file() {
step "修复 compose 文件配置..."
local compose_file="$DEPLOY_DIR/docker-compose.ghcr.yml"
sed -i "s|ghcr.io/openbyteinc/quantdinger-backend|$BACKEND_IMAGE|g" "$compose_file"
sed -i "s|ghcr.io/openbyteinc/quantdinger-frontend|$FRONTEND_IMAGE|g" "$compose_file"
sed -i "s|ghcr.io/openbyteinc/quantdinger-mobile|$MOBILE_IMAGE|g" "$compose_file"
info "镜像路径已修改为 brokermr810"
sed -i 's/pull_policy: always/pull_policy: missing/g' "$compose_file"
info "pull_policy 已改为 missing"
info "compose 文件配置完成"
}
interactive_config() {
step "配置 QuantDinger 参数..."
echo ""
echo "================================================="
echo " 请填写以下配置信息 "
echo "================================================="
echo ""
read -p "管理员用户名 (默认 admin): " ADMIN_USER
ADMIN_USER=${ADMIN_USER:-admin}
while true; do
read -s -p "管理员密码 (至少 8 位): " ADMIN_PASSWORD
echo ""
if [ ${#ADMIN_PASSWORD} -ge 8 ]; then
read -s -p "确认密码: " ADMIN_PASSWORD_CONFIRM
echo ""
if [ "$ADMIN_PASSWORD" == "$ADMIN_PASSWORD_CONFIRM" ]; then
break
else
warn "两次密码不一致,请重新输入"
fi
else
warn "密码太短,至少 8 位"
fi
done
info "管理员账号已设置: $ADMIN_USER"
local server_ip=$(cat /tmp/quantdinger_server_ip 2>/dev/null || echo "localhost")
read -p "访问地址(域名或IP,默认 $server_ip): " frontend_url
frontend_url=${frontend_url:-$server_ip}
FRONTEND_URL="http://$frontend_url"
info "FRONTEND_URL 将设为: $FRONTEND_URL"
echo ""
echo " 如果服务器在国内,访问交易所和 AI 服务可能需要代理。"
read -p "是否配置网络代理?(y/n, 默认 n): " config_proxy
if [[ "$config_proxy" == "y" || "$config_proxy" == "Y" ]]; then
read -p "代理地址(如 socks5h://host.docker.internal:10808): " proxy_url
if [ -z "$proxy_url" ]; then
warn "代理地址为空,跳过"
fi
fi
}
generate_secret_key() {
step "生成 SECRET_KEY..."
SECRET_KEY=$(openssl rand -hex 32)
info "SECRET_KEY 已生成(64 字符)"
}
configure_env() {
step "配置环境变量文件..."
local env_file="$DEPLOY_DIR/backend_api_python/.env"
local env_template="$DEPLOY_DIR/backend_api_python/env.example"
if [ ! -f "$env_file" ]; then
if [ -f "$env_template" ]; then
cp "$env_template" "$env_file"
info "已从模板创建 .env 文件"
else
error "未找到 env.example 模板文件"
exit 1
fi
else
info ".env 文件已存在,将更新关键配置"
fi
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${SECRET_KEY}|" "$env_file" 2>/dev/null || echo "SECRET_KEY=${SECRET_KEY}" >> "$env_file"
sed -i "s|^ADMIN_USER=.*|ADMIN_USER=${ADMIN_USER}|" "$env_file" 2>/dev/null || echo "ADMIN_USER=${ADMIN_USER}" >> "$env_file"
sed -i "s|^ADMIN_PASSWORD=.*|ADMIN_PASSWORD=${ADMIN_PASSWORD}|" "$env_file" 2>/dev/null || echo "ADMIN_PASSWORD=${ADMIN_PASSWORD}" >> "$env_file"
sed -i "s|^FRONTEND_URL=.*|FRONTEND_URL=${FRONTEND_URL}|" "$env_file" 2>/dev/null || echo "FRONTEND_URL=${FRONTEND_URL}" >> "$env_file"
if [ -n "${proxy_url:-}" ]; then
sed -i "s|^PROXY_URL=.*|PROXY_URL=${proxy_url}|" "$env_file" 2>/dev/null || echo "PROXY_URL=${proxy_url}" >> "$env_file"
fi
info "环境变量配置完成"
info "配置文件: $env_file"
local compose_env="$DEPLOY_DIR/.env"
cat > "$compose_env" << EOF
# Compose 编排配置
POSTGRES_DB=quantdinger
POSTGRES_USER=quantdinger
POSTGRES_PASSWORD=quantdinger123
# 端口配置(纯端口号,不要带 IP 前缀)
DB_PORT=5432
REDIS_PORT=6379
BACKEND_PORT=5000
FRONTEND_PORT=8888
MOBILE_PORT=8889
# 前端访问地址
FRONTEND_URL=${FRONTEND_URL}
# 镜像路径覆盖
BACKEND_IMAGE=$BACKEND_IMAGE
FRONTEND_IMAGE=$FRONTEND_IMAGE
MOBILE_IMAGE=$MOBILE_IMAGE
EOF
info "Compose 编排配置已创建: $compose_env"
cp "$env_file" "$DEPLOY_DIR/backend.env"
info "已创建 backend.env"
}
start_docker_services() {
step "启动 Docker 服务..."
cd "$DEPLOY_DIR"
info "使用 GHCR 预构建镜像模式"
info "无需编译,直接启动,几秒钟内完成"
docker compose -f docker-compose.ghcr.yml up -d 2>&1 | tee "$LOG_FILE"
if [ ${PIPESTATUS[0]} -ne 0 ]; then
error "启动失败"
warn "常见原因:镜像未拉取成功、配置文件错误"
warn "请查看日志: $LOG_FILE"
exit 1
fi
}
show_result() {
step "验证部署状态..."
echo ""
echo "================================================="
echo " 容器运行状态 "
echo "================================================="
echo ""
cd "$DEPLOY_DIR"
docker compose -f docker-compose.ghcr.yml ps 2>/dev/null || echo " 容器可能仍在启动中..."
echo ""
echo "================================================="
echo " 服务健康检查 "
echo "================================================="
echo ""
sleep 15
local backend_ok=false
local frontend_ok=false
if curl -s --max-time 5 http://127.0.0.1:5000/api/health &>/dev/null; then
echo " 后端 API (5000): 正常"
backend_ok=true
else
echo " 后端 API (5000): 启动中(可能需要 30 秒初始化)"
fi
if curl -s --max-time 5 http://127.0.0.1:8888 &>/dev/null; then
echo " 前端 Web (8888): 正常"
frontend_ok=true
else
echo " 前端 Web (8888): 启动中"
fi
echo ""
echo "============================================================="
echo " 部署完成!后续步骤 "
echo "============================================================="
echo ""
echo "1. 配置宝塔反向代理(必须)"
echo " 宝塔面板 -> 网站 -> 添加站点"
echo " - 域名: $(echo $FRONTEND_URL | sed 's|https\?://||')"
echo " - PHP版本: 纯静态"
echo " - 创建后 -> 设置 -> 反向代理"
echo " - 目标URL: http://127.0.0.1:8888"
echo ""
echo "2. 配置 SSL 证书(推荐)"
echo " 网站设置 -> SSL -> Let's Encrypt -> 申请"
echo " 开启「强制 HTTPS」"
echo ""
echo "3. 更新 FRONTEND_URL(如已配置 HTTPS)"
echo " 编辑: $DEPLOY_DIR/backend.env"
echo " 修改: FRONTEND_URL=https://你的域名"
echo " 重启: docker compose -f docker-compose.ghcr.yml restart backend"
echo ""
echo "4. 访问 QuantDinger"
echo " 地址: $FRONTEND_URL(配置反代后)"
echo " 账号: $ADMIN_USER"
echo " 密码: (你设置的密码)"
echo ""
echo "5. 常用运维命令"
echo " 查看状态: cd $DEPLOY_DIR && docker compose -f docker-compose.ghcr.yml ps"
echo " 查看日志: cd $DEPLOY_DIR && docker compose -f docker-compose.ghcr.yml logs -f"
echo " 重启服务: cd $DEPLOY_DIR && docker compose -f docker-compose.ghcr.yml restart"
echo " 停止服务: cd $DEPLOY_DIR && docker compose -f docker-compose.ghcr.yml down"
echo " 更新版本: docker pull $BACKEND_IMAGE && docker pull $FRONTEND_IMAGE && docker pull $MOBILE_IMAGE && cd $DEPLOY_DIR && docker compose -f docker-compose.ghcr.yml up -d"
echo ""
echo "6. 安全提醒"
echo " 不要在宝塔安全中放行 5000/5432/6379 端口"
echo " 只放行 80/443/22 端口"
echo ""
echo "日志文件: $LOG_FILE"
echo ""
}
# ==================== 主流程 ====================
main() {
echo ""
echo "============================================================="
echo " QuantDinger 宝塔 Linux 一键部署脚本(官方镜像版) "
echo " PostgreSQL 18 + Redis 8 + GHCR 预构建镜像 "
echo "============================================================="
echo ""
echo " 部署目录: $DEPLOY_DIR"
echo " 日志文件: $LOG_FILE"
echo ""
check_system
check_docker
configure_mirror
pull_images
clone_repo
patch_compose_file
interactive_config
generate_secret_key
configure_env
start_docker_services
show_result
log "QuantDinger 部署脚本执行完成!"
}
main "$@"
使用方法:
bash
# 赋予执行权限
chmod +x quantdinger-ghcr-deploy.sh
# 执行
sudo bash quantdinger-ghcr-deploy.sh
七、配置宝塔反向代理与 SSL
部署完成后,通过宝塔面板配置域名访问和 HTTPS。
7.1 添加站点
- 宝塔面板 → 网站 → 添加站点
- 域名:输入你的域名(如
quant.example.com) - PHP 版本:纯静态
- 数据库:不创建
- 点击 提交
7.2 配置反向代理
| 配置项 | 值 |
|---|---|
| 代理名称 | QuantDinger |
| 目标 URL | http://127.0.0.1:8888 |
| 发送域名 | $host |
步骤:创建站点后 → 设置 → 反向代理 → 添加反向代理
7.3 配置 SSL 证书
- 网站设置 → SSL → Let's Encrypt
- 点击 申请
- 申请成功后开启 强制 HTTPS
7.4 更新 FRONTEND_URL(配置 HTTPS 后必须)
bash
# 官方镜像模式
sed -i 's|FRONTEND_URL=.*|FRONTEND_URL=https://你的域名|' /www/wwwroot/QuantDinger/backend.env
cd /www/wwwroot/QuantDinger && docker compose -f docker-compose.ghcr.yml restart backend
# 源码构建模式
sed -i 's|FRONTEND_URL=.*|FRONTEND_URL=https://你的域名|' /www/wwwroot/QuantDinger/backend_api_python/.env
cp /www/wwwroot/QuantDinger/backend_api_python/.env /www/wwwroot/QuantDinger/backend.env
cd /www/wwwroot/QuantDinger && docker compose -f docker-compose.build.yml restart backend
八、日常管理命令
以下命令中,根据你的部署模式选择对应的 compose 文件:
- 官方镜像模式 :
docker-compose.ghcr.yml - 源码构建模式 :
docker-compose.build.yml
8.1 容器管理
bash
cd /www/wwwroot/QuantDinger
# 查看所有容器状态
docker compose -f docker-compose.ghcr.yml ps
# 查看指定服务状态
docker compose -f docker-compose.ghcr.yml ps backend
docker compose -f docker-compose.ghcr.yml ps frontend
docker compose -f docker-compose.ghcr.yml ps postgres
# 重启所有服务
docker compose -f docker-compose.ghcr.yml restart
# 重启单个服务
docker compose -f docker-compose.ghcr.yml restart backend
docker compose -f docker-compose.ghcr.yml restart frontend
# 停止所有服务
docker compose -f docker-compose.ghcr.yml down
# 启动所有服务
docker compose -f docker-compose.ghcr.yml up -d
# 启动单个服务
docker compose -f docker-compose.ghcr.yml up -d backend
8.2 日志查看
bash
cd /www/wwwroot/QuantDinger
# 查看所有服务日志(实时跟踪)
docker compose -f docker-compose.ghcr.yml logs -f
# 查看指定服务日志
docker compose -f docker-compose.ghcr.yml logs -f backend
docker compose -f docker-compose.ghcr.yml logs -f frontend
docker compose -f docker-compose.ghcr.yml logs -f postgres
# 查看最近 50 行日志
docker compose -f docker-compose.ghcr.yml logs --tail=50 backend
# 查看最近 1 小时的日志
docker compose -f docker-compose.ghcr.yml logs --since=1h backend
# 查看指定时间段的日志
docker compose -f docker-compose.ghcr.yml logs --since="2026-08-16T00:00:00" --until="2026-08-16T06:00:00" backend
8.3 更新升级
官方镜像模式
bash
cd /www/wwwroot/QuantDinger
# 拉取最新镜像
docker pull ghcr.io/brokermr810/quantdinger-backend:latest
docker pull ghcr.io/brokermr810/quantdinger-frontend:latest
docker pull ghcr.io/brokermr810/quantdinger-mobile:latest
# 重启服务(自动使用新镜像)
docker compose -f docker-compose.ghcr.yml up -d
# 如果南大代理拉取
docker pull ghcr.nju.edu.cn/brokermr810/quantdinger-backend:latest
docker tag ghcr.nju.edu.cn/brokermr810/quantdinger-backend:latest ghcr.io/brokermr810/quantdinger-backend:latest
docker compose -f docker-compose.ghcr.yml up -d
源码构建模式
bash
cd /www/wwwroot/QuantDinger
# 更新代码
git pull
cd QuantDinger-Vue && git pull
cd ../QuantDinger-Mobile && git pull
cd ..
# 重新构建并启动
docker compose -f docker-compose.build.yml up -d --build
8.4 数据备份与恢复
数据库备份
bash
# 手动备份
docker exec quantdinger-db pg_dump -U quantdinger quantdinger > /www/backup/quantdinger_$(date +%Y%m%d).sql
# 压缩备份
docker exec quantdinger-db pg_dump -U quantdinger -Fc quantdinger > /www/backup/quantdinger_$(date +%Y%m%d).dump
# 定时备份(添加到 crontab)
# 每天凌晨 3 点自动备份
echo "0 3 * * * docker exec quantdinger-db pg_dump -U quantdinger quantdinger > /www/backup/quantdinger_\$(date +\%Y\%m\%d).sql 2>/dev/null" | crontab -
数据库恢复
bash
# 从 SQL 文件恢复
docker exec -i quantdinger-db psql -U quantdinger quantdinger < /www/backup/quantdinger_20260816.sql
# 从压缩文件恢复
docker exec -i quantdinger-db pg_restore -U quantdinger -d quantdinger < /www/backup/quantdinger_20260816.dump
备份配置文件
bash
# 备份所有配置文件
tar -czf /www/backup/quantdinger_config_$(date +%Y%m%d).tar.gz \
/www/wwwroot/QuantDinger/.env \
/www/wwwroot/QuantDinger/backend.env \
/www/wwwroot/QuantDinger/backend_api_python/.env \
/www/wwwroot/QuantDinger/docker-compose.ghcr.yml
8.5 系统维护
清理 Docker 资源
bash
# 清理已停止的容器
docker container prune -f
# 清理未使用的镜像
docker image prune -a -f
# 清理未使用的卷
docker volume prune -f
# 清理构建缓存
docker builder prune -f
# 一键清理所有未使用的资源
docker system prune -a -f
检查系统资源
bash
# 查看磁盘使用情况
df -h
# 查看内存使用情况
free -h
# 查看 Docker 磁盘占用
docker system df
# 查看容器资源使用
docker stats --no-stream
端口冲突排查
bash
# 查看端口占用
lsof -i :6379
ss -tlnp | grep 6379
netstat -tlnp | grep 6379
# 停掉系统 Redis(宝塔自带的)
systemctl stop redis
systemctl disable redis
# 或者修改 QuantDinger 的 Redis 端口
sed -i 's/REDIS_PORT=6379/REDIS_PORT=6380/' /www/wwwroot/QuantDinger/.env
重启服务
bash
# 重启单个服务
cd /www/wwwroot/QuantDinger
docker compose -f docker-compose.ghcr.yml restart backend
# 重启所有服务
docker compose -f docker-compose.ghcr.yml restart
# 完全重建(保留数据)
docker compose -f docker-compose.ghcr.yml down
docker compose -f docker-compose.ghcr.yml up -d
九、常见问题排查
Q1: 前端构建 OOM 崩溃(JavaScript heap out of memory)
原因:Vite 编译时 V8 堆内存默认限制约 1.4GB,不够用。
解决:
- 确认已创建 8GB Swap
- 确认已修改 Dockerfile 加
NODE_OPTIONS=--max-old-space-size=4096 - 确认
vm.swappiness=100
bash
# 检查 Swap
free -h
# 检查 Dockerfile
grep NODE_OPTIONS /www/wwwroot/QuantDinger/QuantDinger-Vue/Dockerfile
# 检查 swappiness
sysctl vm.swappiness
Q2: Docker 镜像加速无效
原因:daemon.json 中 URL 被反引号包围。
解决:
bash
cat /etc/docker/daemon.json
# 正确格式: "https://docker.m.daocloud.io"
# 错误格式: "`https://docker.m.daocloud.io`"
# 修复
sed -i 's/`//g' /etc/docker/daemon.json
systemctl restart docker
Q3: invalid IP address: 127.0.0.1:127.0.0.1
原因 :.env 文件中端口变量带了 127.0.0.1: 前缀,compose 拼接后变成 127.0.0.1:127.0.0.1:端口。
解决:
bash
# 错误格式
FRONTEND_PORT=127.0.0.1:8888
# 正确格式
FRONTEND_PORT=8888
# 批量修复
sed -i 's/DB_PORT=127.0.0.1:5432/DB_PORT=5432/' /www/wwwroot/QuantDinger/.env
sed -i 's/REDIS_PORT=127.0.0.1:6379/REDIS_PORT=6379/' /www/wwwroot/QuantDinger/.env
sed -i 's/BACKEND_PORT=127.0.0.1:5000/BACKEND_PORT=5000/' /www/wwwroot/QuantDinger/.env
sed -i 's/FRONTEND_PORT=127.0.0.1:8888/FRONTEND_PORT=8888/' /www/wwwroot/QuantDinger/.env
sed -i 's/MOBILE_PORT=127.0.0.1:8889/MOBILE_PORT=8889/' /www/wwwroot/QuantDinger/.env
Q4: GHCR 镜像拉取超时
解决:
- 使用南大代理:
ghcr.nju.edu.cn/brokermr810/... - 检查 daemon.json 是否有加速源
- 多次重试
docker pull
bash
# 南大代理
docker pull ghcr.nju.edu.cn/brokermr810/quantdinger-backend:latest
docker tag ghcr.nju.edu.cn/brokermr810/quantdinger-backend:latest ghcr.io/brokermr810/quantdinger-backend:latest
Q5: 镜像名不匹配(openbyteinc vs brokermr810)
原因 :compose 文件默认用 openbyteinc,实际镜像在 brokermr810 下。
解决:
bash
sed -i 's|ghcr.io/openbyteinc/quantdinger-backend|ghcr.io/brokermr810/quantdinger-backend|g' docker-compose.ghcr.yml
sed -i 's|ghcr.io/openbyteinc/quantdinger-frontend|ghcr.io/brokermr810/quantdinger-frontend|g' docker-compose.ghcr.yml
sed -i 's|ghcr.io/openbyteinc/quantdinger-mobile|ghcr.io/brokermr810/quantdinger-mobile|g' docker-compose.ghcr.yml
Q6: backend.env 文件不存在
原因 :compose 挂载的是 ./backend.env,但默认不存在。
解决:
bash
cp backend_api_python/.env backend.env
Q7: 端口 6379 被占用(Redis 冲突)
原因:宿主机自带 Redis 服务占用了 6379 端口。
解决:
bash
# 查看占用进程
lsof -i :6379
# 停掉系统 Redis
systemctl stop redis
systemctl disable redis
# 或通过宝塔停止
/etc/init.d/redis stop
# 确认端口已释放
ss -tlnp | grep 6379
# 重启 QuantDinger 容器
cd /www/wwwroot/QuantDinger
docker compose -f docker-compose.ghcr.yml down
docker compose -f docker-compose.ghcr.yml up -d
Q8: 构建被终端中断
解决 :使用 nohup 后台运行,不要在前台跑构建。
bash
nohup docker compose -f docker-compose.build.yml up -d --build > /tmp/build.log 2>&1 &
tail -f /tmp/build.log
Q9: 前端仓库克隆失败
解决:使用多个源尝试,GitCode > Gitee > ghproxy > GitHub。
Q10: 日志中出现 duplicate key value 报错
原因:migration 容器首次启动已创建 admin 用户,后端启动再次尝试创建时触发唯一约束冲突。
解决 :这是正常的无害报错,admin 用户已存在且可用,不影响任何功能,可以忽略。
十、安全建议
- 只在宝塔安全中放行 80/443/22 端口
- 不要放行 5000/5432/6379 端口(这些端口只供内部容器间通信)
- 定期备份数据库(建议设置 crontab 定时备份)
- 及时更新系统补丁
- 使用强密码(至少 12 位,含大小写 + 数字 + 符号)
- 启用宝塔面板的登录保护和安全入口
- 定期检查容器日志,关注异常访问
十一、两种部署模式对比
| 项目 | 源码构建模式 | 官方镜像模式 |
|---|---|---|
| 内存要求 | 2GB+(需 Swap) | 1GB+ |
| 部署时间 | 10-20 分钟 | 1-2 分钟 |
| 是否需编译 | 是 | 否 |
| 是否依赖 GHCR | 否 | 是 |
| 代码可定制 | 是 | 否 |
| 适合场景 | 定制需求 / GHCR 不通 | 快速部署 / 低内存 |
| Compose 文件 | docker-compose.build.yml | docker-compose.ghcr.yml |
| 前端仓库 | 需要额外克隆 | 不需要 |
| Swap 配置 | 必须 | 不需要 |
文档结束
如有问题请参考 QuantDinger 官方文档或提交 Issue。