chore: 优化 Docker 配置,添加生产环境部署脚本和文档

This commit is contained in:
爽哒哒
2026-03-18 22:48:36 +08:00
parent f655aa0ede
commit e3a620b395
8 changed files with 610 additions and 43 deletions

73
.env.example Normal file
View File

@@ -0,0 +1,73 @@
# ==========================================
# 创赢未来报名评分系统 - 环境变量配置
# 复制此文件为 .env 并修改为您的实际配置
# ==========================================
# ==========================================
# Django 基础配置
# ==========================================
# 生产环境请设置为 False
DEBUG=False
# 安全密钥,生产环境必须修改!
# 生成方法: python -c "import secrets; print(secrets.token_urlsafe(50))"
SECRET_KEY=your-secret-key-change-this-in-production
# 允许访问的域名,多个用逗号分隔
ALLOWED_HOSTS=localhost,127.0.0.1,your-domain.com
# ==========================================
# 数据库配置 (PostgreSQL)
# ==========================================
DB_NAME=scoring_system
DB_USER=postgres
DB_PASSWORD=your-db-password
DB_HOST=localhost
DB_PORT=5432
# ==========================================
# 微信支付配置
# ==========================================
WECHAT_APPID=wx-your-app-id
WECHAT_SECRET=your-wechat-secret
WECHAT_MCHID=your-merchant-id
WECHAT_API_KEY=your-api-key
WECHAT_API_V3_KEY=your-api-v3-key
WECHAT_CERT_PATH=/path/to/cert.pem
WECHAT_KEY_PATH=/path/to/key.pem
# ==========================================
# 阿里云 OSS 配置 (文件存储)
# ==========================================
ALIYUN_ACCESS_KEY_ID=your-access-key-id
ALIYUN_ACCESS_KEY_SECRET=your-access-key-secret
ALIYUN_OSS_ENDPOINT=https://oss-cn-your-region.aliyuncs.com
ALIYUN_OSS_BUCKET_NAME=your-bucket-name
ALIYUN_OSS_INTERNAL_ENDPOINT=https://oss-cn-your-region-internal.aliyuncs.com
# ==========================================
# 阿里云听悟配置 (语音转写)
# ==========================================
ALIYUN_TINGWU_APP_KEY=your-tingwu-app-key
# ==========================================
# 阿里云百炼/通义千问配置 (AI 评估)
# ==========================================
DASHSCOPE_API_KEY=your-dashscope-api-key
# ==========================================
# 前端 API 地址配置
# ==========================================
VITE_API_URL=/api
# ==========================================
# 其他配置
# ==========================================
# 时区
TZ=Asia/Shanghai
# 日志级别 (DEBUG, INFO, WARNING, ERROR)
LOG_LEVEL=INFO
# 文件上传大小限制 (MB)
MAX_UPLOAD_SIZE=100

249
DEPLOY.md Normal file
View File

@@ -0,0 +1,249 @@
# 创赢未来报名评分系统 - 生产环境部署指南
## 📋 部署前准备
### 系统要求
- **操作系统**: Ubuntu 20.04+ / CentOS 7+ / Debian 10+
- **内存**: 至少 2GB RAM
- **磁盘**: 至少 20GB 可用空间
- **Docker**: 20.10+
- **Docker Compose**: 2.0+
### 安装 Docker
```bash
# Ubuntu/Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 安装 Docker Compose
sudo apt-get install -y docker-compose-plugin
# 或
pip install docker-compose
```
## 🚀 快速部署
### 1. 拉取代码
```bash
git clone https://gitea.tangledup-ai.com/quant-speed-AI/Scoring-System.git
cd Scoring-System
```
### 2. 配置环境变量
```bash
# 复制环境变量模板
cp .env.example .env
# 编辑 .env 文件,填入您的实际配置
vim .env
```
**必须配置的项目:**
- `SECRET_KEY`: Django 安全密钥(必须修改!)
- `DB_PASSWORD`: 数据库密码
- `WECHAT_*`: 微信支付相关配置(如需支付功能)
- `ALIYUN_*`: 阿里云 OSS 和 AI 服务配置(如需语音转写和 AI 评估)
### 3. 执行部署脚本
```bash
# 给脚本添加执行权限
chmod +x deploy.sh
# 执行部署
./deploy.sh
```
## 🔧 手动部署(高级)
### 1. 构建镜像
```bash
docker-compose build --no-cache
```
### 2. 启动服务
```bash
# 后台启动
docker-compose up -d
# 查看日志
docker-compose logs -f
# 查看特定服务日志
docker-compose logs -f backend
docker-compose logs -f frontend
```
### 3. 执行数据库迁移
```bash
docker-compose exec backend python manage.py migrate
docker-compose exec backend python manage.py createsuperuser
```
### 4. 收集静态文件
```bash
docker-compose exec backend python manage.py collectstatic --noinput
```
## 🌐 Nginx 反向代理配置(推荐)
如果需要使用域名和 HTTPS在服务器上安装 Nginx
```nginx
# /etc/nginx/sites-available/scoring-system
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
启用配置:
```bash
sudo ln -s /etc/nginx/sites-available/scoring-system /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
## 🔒 HTTPS 配置Let's Encrypt
```bash
# 安装 Certbot
sudo apt-get install -y certbot python3-certbot-nginx
# 申请证书
sudo certbot --nginx -d your-domain.com
# 自动续期
sudo certbot renew --dry-run
```
## 📊 常用命令
```bash
# 查看运行状态
docker-compose ps
# 停止服务
docker-compose down
# 重启服务
docker-compose restart
# 重启单个服务
docker-compose restart backend
# 进入容器
docker-compose exec backend bash
docker-compose exec frontend sh
# 查看容器日志
docker-compose logs -f --tail=100 backend
# 清理未使用的镜像
docker image prune -f
# 更新部署(拉取最新代码并重启)
git pull
./deploy.sh
```
## 🔍 故障排查
### 问题1: 端口被占用
```bash
# 检查端口占用
sudo netstat -tlnp | grep 8000
sudo netstat -tlnp | grep 80
# 修改 docker-compose.yml 中的端口映射
```
### 问题2: 数据库连接失败
```bash
# 检查数据库配置
docker-compose exec backend python manage.py dbshell
# 查看后端日志
docker-compose logs backend | grep -i error
```
### 问题3: 静态文件无法访问
```bash
# 重新收集静态文件
docker-compose exec backend python manage.py collectstatic --noinput
# 检查权限
ls -la backend/static/
ls -la backend/media/
```
### 问题4: 容器无法启动
```bash
# 查看详细日志
docker-compose logs --no-color
# 检查配置
docker-compose config
```
## 💾 数据备份
### 数据库备份
```bash
# 进入容器执行备份
docker-compose exec backend python manage.py dumpdata > backup_$(date +%Y%m%d).json
# 或使用 PostgreSQL 直接备份
docker exec scoring_system_db pg_dump -U postgres scoring_system > db_backup_$(date +%Y%m%d).sql
```
### 媒体文件备份
```bash
# 备份上传的文件
tar -czvf media_backup_$(date +%Y%m%d).tar.gz backend/media/
```
## 🔄 自动部署CI/CD
使用 Gitea Actions 或 Jenkins 实现自动部署:
```yaml
# .gitea/workflows/deploy.yaml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
script: |
cd ~/Scoring-System
git pull
./deploy.sh
```
## 📞 技术支持
- **项目仓库**: https://gitea.tangledup-ai.com/quant-speed-AI/Scoring-System
- **问题反馈**: 在仓库提交 Issue
---
**部署完成后访问地址:**
- 🌐 前端页面: http://your-server-ip/
- 🔧 后台管理: http://your-server-ip:8000/admin/
- 📚 API 文档: http://your-server-ip:8000/api/docs/

View File

@@ -1,26 +1,40 @@
# Use an official Python runtime as a parent image # 使用 Python 3.12 slim 镜像作为基础镜像
FROM python:3.13-slim FROM python:3.12-slim
# Set environment variables # 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV PYTHONIOENCODING=utf-8
# Set work directory # 设置工作目录
WORKDIR /app WORKDIR /app
# Install python dependencies # 安装系统依赖
COPY requirements.txt /app/ RUN apt-get update && apt-get install -y \
RUN pip install --upgrade pip && pip install -r requirements.txt gcc \
libpq-dev \
libffi-dev \
libjpeg-dev \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*
# Copy project # 复制 requirements 文件并安装依赖
COPY . /app/ COPY requirements.txt .
COPY .env /app/ RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Expose port # 复制项目代码
COPY . .
# 创建媒体文件目录
RUN mkdir -p /app/media /app/static
# 暴露端口
EXPOSE 8000 EXPOSE 8000
# Volume for media files # 健康检查
VOLUME ["/app/media"] HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:8000/api/health/')" || exit 1
# Run the application with gunicorn # 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi:application"] CMD ["sh", "-c", "python manage.py collectstatic --noinput && python manage.py migrate && gunicorn --bind 0.0.0.0:8000 --workers 4 --threads 2 --worker-class gthread --access-logfile - --error-logfile - --capture-output --enable-stdio-inheritance config.wsgi:application"]

View File

@@ -2,11 +2,21 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.http import JsonResponse
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView
from competition import judge_views from competition import judge_views
# 健康检查视图
def health_check(request):
return JsonResponse({
'status': 'healthy',
'service': '创赢未来报名评分系统',
'version': '1.0.0'
})
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('api/health/', health_check, name='health_check'),
# Judge System Routes # Judge System Routes
path('judge/', include('competition.judge_urls')), path('judge/', include('competition.judge_urls')),

95
deploy.sh Normal file
View File

@@ -0,0 +1,95 @@
#!/bin/bash
# 创赢未来报名评分系统 - 生产环境部署脚本
# 用法: ./deploy.sh [环境变量]
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 打印带颜色的信息
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查环境
print_info "检查 Docker 环境..."
if ! command -v docker &> /dev/null; then
print_error "Docker 未安装,请先安装 Docker"
exit 1
fi
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
print_error "Docker Compose 未安装,请先安装 Docker Compose"
exit 1
fi
# 检查 .env 文件
if [ ! -f .env ]; then
print_warning ".env 文件不存在,将使用默认配置"
print_warning "建议复制 .env.example 为 .env 并修改配置"
fi
# 显示部署信息
print_info "================================"
print_info "创赢未来报名评分系统 - 部署脚本"
print_info "================================"
# 拉取最新代码(如果是 git 仓库)
if [ -d .git ]; then
print_info "拉取最新代码..."
git pull || print_warning "Git pull 失败,使用本地代码继续"
fi
# 停止旧容器
print_info "停止旧容器..."
docker-compose down --remove-orphans
# 删除旧镜像(可选)
print_info "清理旧镜像..."
docker-compose rm -f
# 构建镜像
print_info "构建 Docker 镜像..."
docker-compose build --no-cache
# 启动服务
print_info "启动服务..."
docker-compose up -d
# 等待服务启动
print_info "等待服务启动..."
sleep 10
# 检查服务状态
print_info "检查服务状态..."
if docker-compose ps | grep -q "Up"; then
print_success "服务启动成功!"
echo ""
print_info "访问地址:"
print_success " - 前端: http://localhost"
print_success " - 后端 API: http://localhost:8000/api/"
print_success " - 后台管理: http://localhost:8000/admin/"
echo ""
print_info "查看日志: docker-compose logs -f"
else
print_error "服务启动失败,请检查日志: docker-compose logs"
exit 1
fi

View File

@@ -1,31 +1,85 @@
version: '3.8'
services: services:
# 后端服务
backend: backend:
build: ./backend build:
# 使用 gunicorn 替代 runserver提高稳定性并捕获标准输出1 context: ./backend
command: sh -c "python manage.py collectstatic --noinput && python manage.py migrate && gunicorn --bind 0.0.0.0:8000 --access-logfile - --error-logfile - config.wsgi:application" dockerfile: Dockerfile
container_name: scoring_backend
restart: always
volumes: volumes:
- ./backend:/app
- ./backend/media:/app/media - ./backend/media:/app/media
- ./backend/static:/app/static
ports: ports:
- "8000:8000" - "8000:8000"
environment: environment:
- DB_NAME=market - DEBUG=False
- DB_USER=market - SECRET_KEY=${SECRET_KEY:-your-secret-key-change-this}
- DB_PASSWORD=123market - DB_NAME=${DB_NAME:-scoring_system}
- DB_HOST=6.6.6.66 - DB_USER=${DB_USER:-postgres}
- DB_PORT=5432 - DB_PASSWORD=${DB_PASSWORD:-password}
- DB_HOST=${DB_HOST:-localhost}
- DB_PORT=${DB_PORT:-5432}
- WECHAT_APPID=${WECHAT_APPID}
- WECHAT_SECRET=${WECHAT_SECRET}
- WECHAT_MCHID=${WECHAT_MCHID}
- WECHAT_API_KEY=${WECHAT_API_KEY}
- ALIYUN_ACCESS_KEY_ID=${ALIYUN_ACCESS_KEY_ID}
- ALIYUN_ACCESS_KEY_SECRET=${ALIYUN_ACCESS_KEY_SECRET}
- ALIYUN_OSS_ENDPOINT=${ALIYUN_OSS_ENDPOINT}
- ALIYUN_OSS_BUCKET_NAME=${ALIYUN_OSS_BUCKET_NAME}
- ALIYUN_TINGWU_APP_KEY=${ALIYUN_TINGWU_APP_KEY}
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY}
networks:
- scoring_network
healthcheck:
test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8000/api/health/')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 前端服务 (Nginx)
frontend: frontend:
build: build:
context: ./frontend context: ./frontend
dockerfile: Dockerfile
args: args:
- VITE_API_URL=/api - VITE_API_URL=/api
# volumes: container_name: scoring_frontend
# - ./frontend:/app restart: always
# - /app/node_modules
ports: ports:
- "15173:15173" - "80:80"
environment:
- VITE_API_URL=http://localhost:8000/api
depends_on: depends_on:
backend:
condition: service_healthy
networks:
- scoring_network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
# Nginx 反向代理 (可选,用于负载均衡和 SSL)
nginx:
image: nginx:alpine
container_name: scoring_nginx
restart: always
ports:
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- frontend
- backend - backend
networks:
- scoring_network
profiles:
- ssl # 只在需要 SSL 时启动
networks:
scoring_network:
driver: bridge

View File

@@ -1,13 +1,10 @@
# Use an official Node runtime as a parent image # 构建阶段
FROM node:22-alpine FROM node:20-alpine AS builder
# Set working directory # 设置工作目录
WORKDIR /app WORKDIR /app
# Install build dependencies for native modules # 安装依赖
RUN apk add --no-cache autoconf automake libtool make g++ zlib-dev nasm python3
# Install dependencies
COPY package.json package-lock.json* ./ COPY package.json package-lock.json* ./
RUN npm install --registry=https://registry.npmmirror.com RUN npm install --registry=https://registry.npmmirror.com
@@ -16,14 +13,22 @@ COPY . .
# 接收构建参数 # 接收构建参数
ARG VITE_API_URL=/api ARG VITE_API_URL=/api
# 设置环境变量供构建时使用 ENV VITE_API_URL=${VITE_API_URL}
ENV VITE_API_URL=$VITE_API_URL
# 构建生产环境代码 # 构建生产环境代码
RUN npm run build RUN npm run build
# 暴露应用运行的端口 # 生产阶段 - 使用 Nginx
EXPOSE 15173 FROM nginx:alpine
# 启动应用 (Preview 模式) # 复制 Nginx 配置
CMD ["npm", "run", "preview", "--", "--host", "0.0.0.0", "--port", "15173"] COPY nginx.conf /etc/nginx/conf.d/default.conf
# 从构建阶段复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html
# 暴露端口
EXPOSE 80
# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

67
frontend/nginx.conf Normal file
View File

@@ -0,0 +1,67 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# 缓存静态资源
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|otf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# 前端路由支持 - 所有路由指向 index.html
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache";
}
# API 代理 - 将 /api 请求转发到后端
location /api/ {
proxy_pass http://backend:8000/api/;
proxy_http_version 1.1;
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_send_timeout 60s;
proxy_read_timeout 60s;
}
# 媒体文件代理
location /media/ {
proxy_pass http://backend:8000/media/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 静态文件代理
location /static/ {
proxy_pass http://backend:8000/static/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 健康检查
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 错误页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}