517 lines
18 KiB
Markdown
517 lines
18 KiB
Markdown
# Docker 快速部署
|
||
|
||
本章节介绍如何使用 Docker 快速部署后端服务,无需手动安装 Python、MySQL、Redis 等环境。
|
||
|
||
:::tip 温馨提示
|
||
Docker 部署适合快速体验和生产环境。本地开发建议使用 [后端启动](/zh/volume1/backend) 章节的方式。
|
||
:::
|
||
|
||
## 前置条件
|
||
|
||
确保已安装 Docker 和 Docker Compose:
|
||
|
||
```bash
|
||
# 验证 Docker 安装
|
||
docker --version
|
||
# Docker version 24.0.x
|
||
|
||
# 验证 Docker Compose 安装
|
||
docker compose version
|
||
# Docker Compose version v2.x.x
|
||
```
|
||
|
||
## 快速启动
|
||
|
||
### 1. 准备环境变量
|
||
|
||
```bash
|
||
# 复制环境变量模板
|
||
cp .env.example .env
|
||
|
||
# 编辑 .env,配置数据库密码、Redis 密码、JWT 密钥等
|
||
```
|
||
|
||
:::danger 重要提示
|
||
Docker 部署时,`.env` 中的 `DB_HOST` 和 `REDIS_HOST` 应指向 Docker 网络中的服务名称(如 `mysql`、`redis`),而非 `127.0.0.1`。
|
||
:::
|
||
|
||
### 2. 使用 Docker Compose 启动
|
||
|
||
```bash
|
||
# 构建并启动所有服务
|
||
docker compose up -d --build
|
||
|
||
# 查看服务状态
|
||
docker compose ps
|
||
|
||
# 查看日志
|
||
docker compose logs -f app
|
||
```
|
||
|
||
### 3. 访问服务
|
||
|
||
```
|
||
后端 API: http://127.0.0.1:8041/api/v1/health
|
||
```
|
||
|
||
## Dockerfile 说明
|
||
|
||
项目使用多阶段构建,减小镜像体积:
|
||
|
||
```dockerfile
|
||
# ======================================================================
|
||
# Stage 1: 构建依赖
|
||
# 使用一个临时镜像来解析 Poetry 依赖,避免把 Poetry 本身
|
||
# 和构建缓存带入最终运行镜像,从而减小镜像体积。
|
||
# ======================================================================
|
||
FROM python:3.12-slim AS builder
|
||
|
||
# 设置构建阶段的工作目录
|
||
WORKDIR /build
|
||
|
||
# 只拷贝依赖描述文件(先拷这些可以利用 Docker 层缓存:
|
||
# 只要依赖没变,后续 poetry export 就不会重新执行)
|
||
COPY pyproject.toml poetry.lock* ./
|
||
|
||
# 安装 Poetry,并把 pyproject.toml / poetry.lock 中的依赖
|
||
# 导出为普通的 requirements.txt,方便运行阶段用 pip 安装。
|
||
# --without-hashes:不写入哈希校验,简化后续安装。
|
||
RUN pip install --no-cache-dir poetry \
|
||
&& poetry export -f requirements.txt -o requirements.txt --without-hashes
|
||
|
||
|
||
# ======================================================================
|
||
# Stage 2: 运行
|
||
# 基于干净的 python:3.12-slim 构建最终镜像,只保留运行所需内容。
|
||
# ======================================================================
|
||
FROM python:3.12-slim AS runtime
|
||
|
||
# 环境变量配置:
|
||
# PYTHONUNBUFFERED=1 → 不缓冲 stdout/stderr,日志实时输出
|
||
# PYTHONDONTWRITEBYTECODE=1 → 不生成 .pyc 文件,保持镜像干净
|
||
# TZ=Asia/Shanghai → 设置容器时区为上海
|
||
ENV PYTHONUNBUFFERED=1 \
|
||
PYTHONDONTWRITEBYTECODE=1 \
|
||
TZ=Asia/Shanghai
|
||
|
||
# 设置应用运行的工作目录
|
||
WORKDIR /data/apps
|
||
|
||
# 从构建阶段拷贝导出的 requirements.txt
|
||
COPY --from=builder /build/requirements.txt ./
|
||
|
||
# 安装运行所需的 Python 依赖
|
||
RUN pip install --no-cache-dir -r requirements.txt
|
||
|
||
# 拷贝应用代码和资源文件到镜像中
|
||
COPY src ./src # 源码目录
|
||
COPY app.py ./ # 应用入口
|
||
COPY alembic ./alembic # 数据库迁移脚本
|
||
COPY alembic.ini ./ # 数据库迁移配置
|
||
COPY .env.example ./.env # 环境变量示例(默认作为 .env 使用)
|
||
COPY scripts ./scripts # 辅助脚本
|
||
COPY templates ./templates # 模板文件
|
||
COPY static ./static # 静态资源
|
||
|
||
# 创建运行时需要的上传目录和日志目录
|
||
RUN mkdir -p uploads logs
|
||
|
||
# 声明容器对外暴露的端口
|
||
EXPOSE 8041
|
||
|
||
# 容器启动命令:运行 Tornado 应用入口
|
||
CMD ["python", "app.py"]
|
||
```
|
||
|
||
### 多阶段构建原理
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Stage 1: builder │
|
||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||
│ │ python:3.12-slim │ │
|
||
│ │ + Poetry │ │
|
||
│ │ + pyproject.toml / poetry.lock │ │
|
||
│ │ ─────────────────────────────────────────────────────── │ │
|
||
│ │ → poetry export → requirements.txt │ │
|
||
│ └─────────────────────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ ▼ 仅拷贝 requirements.txt │
|
||
│ Stage 2: runtime │
|
||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||
│ │ python:3.12-slim │ │
|
||
│ │ + pip install -r requirements.txt │ │
|
||
│ │ + 应用源码 + 配置 + 脚本 │ │
|
||
│ │ ─────────────────────────────────────────────────────── │ │
|
||
│ │ → 最终镜像(不含 Poetry、不含构建缓存) │ │
|
||
│ └─────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## docker-compose.yml 说明
|
||
|
||
### 基础版(仅应用服务)
|
||
|
||
项目自带的 `docker-compose.yml` 仅包含应用服务,适用于数据库和 Redis 已有外部实例的场景:
|
||
|
||
```yaml
|
||
# ======================================================================
|
||
# Docker Compose 编排文件(基础版)
|
||
# 仅包含应用服务,适用于数据库和 Redis 已有外部实例的场景
|
||
# 使用方式:docker compose up -d
|
||
# 依赖根目录下的 .env 提供环境变量
|
||
# ======================================================================
|
||
|
||
services:
|
||
# ====================================================================
|
||
# 应用服务:构建并运行本项目
|
||
# ====================================================================
|
||
tornado_elevue:
|
||
image: tornado_elevue:latest # 镜像名称
|
||
container_name: tornado_elevue # 容器名称
|
||
build:
|
||
context: ./ # 构建上下文为项目根目录
|
||
dockerfile: Dockerfile # 使用项目根目录的 Dockerfile
|
||
restart: always # 总是自动重启(生产环境推荐)
|
||
ports:
|
||
- "8041:8041" # 宿主机端口:容器端口
|
||
environment:
|
||
- TORNADO_PORT=8041 # 应用监听端口
|
||
- TORNADO_ENV=production # 运行环境设为生产模式
|
||
env_file:
|
||
- .env # 从 .env 文件加载数据库密码等环境变量
|
||
volumes:
|
||
- ./uploads:/data/apps/uploads # 持久化上传文件到宿主机
|
||
- ./logs:/data/apps/logs # 持久化日志(与 Dockerfile WORKDIR 对应)
|
||
command: python app.py # 启动命令
|
||
logging:
|
||
driver: "json-file" # 日志驱动:JSON 文件
|
||
options:
|
||
max-size: "500m" # 单个日志文件最大 500MB
|
||
max-file: "10" # 最多保留 10 个日志文件
|
||
```
|
||
|
||
### 完整版(应用 + MySQL + Redis)
|
||
|
||
快速体验或小型部署推荐使用包含 MySQL 和 Redis 的完整版:
|
||
|
||
```yaml
|
||
# ======================================================================
|
||
# Docker Compose 编排文件(完整版)
|
||
# 包含三个服务:app(应用)、mysql(数据库)、redis(缓存)
|
||
# 使用方式:docker compose up -d
|
||
# 依赖根目录下的 .env 提供密码等环境变量
|
||
# ======================================================================
|
||
|
||
services:
|
||
# ====================================================================
|
||
# 应用服务:构建并运行本项目
|
||
# ====================================================================
|
||
app:
|
||
build: . # 使用当前目录的 Dockerfile 构建镜像
|
||
ports:
|
||
- "8041:8041" # 宿主机端口:容器端口(供外部访问)
|
||
env_file:
|
||
- .env # 从 .env 文件加载环境变量(数据库密码等)
|
||
environment:
|
||
- TORNADO_ENV=production # 运行环境设为生产模式
|
||
- DB_HOST=mysql # 数据库地址指向 Docker 网络中的 mysql 服务
|
||
- REDIS_HOST=redis # Redis 地址指向 Docker 网络中的 redis 服务
|
||
volumes:
|
||
- ./uploads:/data/apps/uploads # 持久化上传文件到宿主机
|
||
- ./logs:/data/apps/logs # 持久化日志(与 Dockerfile WORKDIR 对应)
|
||
depends_on:
|
||
- mysql # 声明依赖,但不保证 mysql 已就绪
|
||
- redis
|
||
restart: unless-stopped # 除非手动停止,否则总是自动重启
|
||
|
||
# ====================================================================
|
||
# MySQL 数据库
|
||
# ====================================================================
|
||
mysql:
|
||
image: mysql:8.0 # 官方 MySQL 8.0 镜像
|
||
environment:
|
||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} # root 密码,从 .env 读取
|
||
MYSQL_DATABASE: ${DB_DATABASE} # 初始化时自动创建的数据库名
|
||
ports:
|
||
- "3306:3306" # 暴露到宿主机(生产环境建议去掉)
|
||
volumes:
|
||
- mysql_data:/var/lib/mysql # 命名卷持久化数据库数据
|
||
restart: unless-stopped
|
||
|
||
# ====================================================================
|
||
# Redis 缓存
|
||
# ====================================================================
|
||
redis:
|
||
image: redis:7-alpine # 轻量级 Redis 7 镜像
|
||
command: redis-server --requirepass ${REDIS_PASSWORD} # 启动时设置访问密码
|
||
ports:
|
||
- "6379:6379" # 暴露到宿主机(生产环境建议去掉)
|
||
volumes:
|
||
- redis_data:/data # 持久化 Redis 数据
|
||
restart: unless-stopped
|
||
|
||
# ======================================================================
|
||
# 命名卷:由 Docker 管理,数据存放在宿主机 /var/lib/docker/volumes 下
|
||
# ======================================================================
|
||
volumes:
|
||
mysql_data: # MySQL 数据持久化卷
|
||
redis_data: # Redis 数据持久化卷
|
||
```
|
||
|
||
### 完整版 .env 配置示例
|
||
|
||
使用完整版 docker-compose.yml 时,`.env` 文件中的数据库和 Redis 地址应指向 Docker 服务名称:
|
||
|
||
```bash
|
||
# ============================================================
|
||
# 数据库配置(指向 Docker 网络中的 mysql 服务)
|
||
# ============================================================
|
||
DB_DRIVER=mysql
|
||
DB_HOST=mysql # Docker 服务名称,不是 127.0.0.1
|
||
DB_PORT=3306
|
||
DB_DATABASE=djangoadmin.tornado.elevue
|
||
DB_USERNAME=root
|
||
DB_PASSWORD=your_mysql_password # 与 MYSQL_ROOT_PASSWORD 一致
|
||
DB_PREFIX=tornado_
|
||
|
||
# ============================================================
|
||
# Redis 配置(指向 Docker 网络中的 redis 服务)
|
||
# ============================================================
|
||
REDIS_HOST=redis # Docker 服务名称,不是 127.0.0.1
|
||
REDIS_PORT=6379
|
||
REDIS_PASSWORD=your_redis_password
|
||
REDIS_AUTH=True
|
||
|
||
# ============================================================
|
||
# JWT 配置(必填)
|
||
# ============================================================
|
||
JWT_SECRET=your_jwt_secret_key_at_least_32_bytes
|
||
|
||
# ============================================================
|
||
# 应用配置
|
||
# ============================================================
|
||
TORNADO_ENV=production
|
||
TORNADO_DEBUG=False
|
||
```
|
||
|
||
## 常用 Docker 命令
|
||
|
||
```bash
|
||
# 构建镜像
|
||
docker compose build
|
||
|
||
# 启动服务(后台运行)
|
||
docker compose up -d
|
||
|
||
# 停止服务
|
||
docker compose down
|
||
|
||
# 停止服务并删除数据卷(⚠️ 会丢失数据)
|
||
docker compose down -v
|
||
|
||
# 查看日志(实时跟踪)
|
||
docker compose logs -f app
|
||
|
||
# 查看所有服务日志
|
||
docker compose logs -f
|
||
|
||
# 进入容器
|
||
docker compose exec app bash
|
||
|
||
# 重启服务
|
||
docker compose restart app
|
||
|
||
# 查看容器状态
|
||
docker compose ps
|
||
|
||
# 查看容器资源占用
|
||
docker stats
|
||
```
|
||
|
||
## 数据持久化
|
||
|
||
Docker Compose 使用命名卷和绑定挂载持久化数据:
|
||
|
||
| 存储方式 | 卷名/路径 | 说明 |
|
||
|:---------|:----------|:-----|
|
||
| 命名卷 | `mysql_data` | MySQL 数据文件(由 Docker 管理) |
|
||
| 命名卷 | `redis_data` | Redis 数据文件(由 Docker 管理) |
|
||
| 绑定挂载 | `./uploads` | 上传文件(映射到宿主机) |
|
||
| 绑定挂载 | `./logs` | 日志文件(映射到宿主机) |
|
||
|
||
:::warning 数据备份
|
||
生产环境定期备份数据库:
|
||
|
||
```bash
|
||
# 备份 MySQL
|
||
docker compose exec mysql mysqldump -uroot -p${DB_PASSWORD} ${DB_DATABASE} > backup_$(date +%Y%m%d).sql
|
||
|
||
# 恢复 MySQL
|
||
docker compose exec -T mysql mysql -uroot -p${DB_PASSWORD} ${DB_DATABASE} < backup.sql
|
||
```
|
||
:::
|
||
|
||
## 生产环境部署建议
|
||
|
||
### 1. 关闭调试模式
|
||
|
||
```bash
|
||
# .env
|
||
TORNADO_DEBUG=False
|
||
TORNADO_ENV=production
|
||
```
|
||
|
||
### 2. 使用强密钥
|
||
|
||
```bash
|
||
# 生成 JWT 密钥
|
||
python -c "import secrets; print(secrets.token_urlsafe(48))"
|
||
```
|
||
|
||
### 3. 配置 Nginx 反向代理
|
||
|
||
```nginx
|
||
server {
|
||
# 监听 80 端口(HTTP)
|
||
listen 80;
|
||
|
||
# 域名配置(替换为实际域名)
|
||
server_name your-domain.com;
|
||
|
||
# 反向代理配置:将所有请求转发到 Tornado 应用
|
||
location / {
|
||
# 转发目标:Tornado 应用监听的地址和端口
|
||
proxy_pass http://127.0.0.1:8041;
|
||
|
||
# 传递原始请求头,让后端获取真实的客户端信息
|
||
proxy_set_header Host $host; # 原始域名
|
||
proxy_set_header X-Real-IP $remote_addr; # 客户端真实 IP
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 代理链 IP 列表
|
||
proxy_set_header X-Forwarded-Proto $scheme; # 原始协议(http/https)
|
||
|
||
# 超时配置(根据业务需求调整)
|
||
proxy_connect_timeout 60s; # 连接后端超时时间
|
||
proxy_send_timeout 60s; # 发送请求超时时间
|
||
proxy_read_timeout 60s; # 读取响应超时时间
|
||
|
||
# 缓冲配置
|
||
proxy_buffering on; # 开启响应缓冲,减轻后端压力
|
||
proxy_buffer_size 4k; # 响应头缓冲区大小
|
||
proxy_buffers 8 4k; # 响应体缓冲区数量和大小
|
||
}
|
||
|
||
# 静态文件直接由 Nginx 提供(可选,提升性能)
|
||
location /static/ {
|
||
alias /data/apps/static/; # 映射到容器内的静态文件目录
|
||
expires 7d; # 浏览器缓存 7 天
|
||
access_log off; # 关闭静态文件访问日志
|
||
}
|
||
|
||
# 上传文件直接由 Nginx 提供(可选)
|
||
location /uploads/ {
|
||
alias /data/apps/uploads/; # 映射到容器内的上传文件目录
|
||
expires 1d; # 浏览器缓存 1 天
|
||
access_log off;
|
||
}
|
||
|
||
# 安全配置:拒绝访问隐藏文件
|
||
location ~ /\. {
|
||
deny all;
|
||
access_log off;
|
||
log_not_found off;
|
||
}
|
||
}
|
||
```
|
||
|
||
:::tip HTTPS 配置
|
||
生产环境建议启用 HTTPS。可使用 Let's Encrypt 免费证书:
|
||
|
||
```bash
|
||
# 安装 certbot
|
||
apt install certbot python3-certbot-nginx
|
||
|
||
# 申请证书并自动配置 Nginx
|
||
certbot --nginx -d your-domain.com
|
||
```
|
||
:::
|
||
|
||
### 4. 限制端口暴露
|
||
|
||
生产环境建议不暴露 MySQL 和 Redis 端口到宿主机:
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
mysql:
|
||
# ports: # 注释掉或删除
|
||
# - "3306:3306" # 生产环境不暴露
|
||
...
|
||
|
||
redis:
|
||
# ports: # 注释掉或删除
|
||
# - "6379:6379" # 生产环境不暴露
|
||
...
|
||
```
|
||
|
||
### 5. 配置日志轮转
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
services:
|
||
app:
|
||
logging:
|
||
driver: "json-file"
|
||
options:
|
||
max-size: "500m" # 单个日志文件最大 500MB
|
||
max-file: "10" # 最多保留 10 个日志文件
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### 容器启动失败
|
||
|
||
```bash
|
||
# 查看容器日志
|
||
docker compose logs app
|
||
|
||
# 常见原因:
|
||
# 1. .env 配置错误(数据库密码、Redis 密码)
|
||
# 2. 端口被占用(修改 docker-compose.yml 中的端口映射)
|
||
# 3. 磁盘空间不足
|
||
# 4. JWT_SECRET 未配置或过短
|
||
```
|
||
|
||
### 数据库连接失败
|
||
|
||
```
|
||
1. 确认 MySQL 容器已启动:docker compose ps
|
||
2. 确认 .env 中 DB_HOST=mysql(Docker 服务名称,不是 127.0.0.1)
|
||
3. 确认 DB_PASSWORD 与 MYSQL_ROOT_PASSWORD 一致
|
||
4. 确认 MySQL 已就绪:docker compose logs mysql
|
||
```
|
||
|
||
### Redis 连接失败
|
||
|
||
```
|
||
1. 确认 Redis 容器已启动:docker compose ps
|
||
2. 确认 .env 中 REDIS_HOST=redis(Docker 服务名称,不是 127.0.0.1)
|
||
3. 确认 REDIS_PASSWORD 与 docker-compose.yml 中的密码一致
|
||
```
|
||
|
||
### 容器内时区不对
|
||
|
||
Dockerfile 已设置 `TZ=Asia/Shanghai`,如果仍有时区问题:
|
||
|
||
```bash
|
||
# 进入容器检查时区
|
||
docker compose exec app date
|
||
|
||
# 手动设置时区(docker-compose.yml)
|
||
environment:
|
||
- TZ=Asia/Shanghai
|
||
```
|
||
|
||
## 总结
|
||
|
||
Docker 部署通过 Docker Compose 一键启动应用、MySQL、Redis 三个服务。多阶段构建减小镜像体积,命名卷保证数据持久化。生产环境建议关闭调试模式、使用强密钥、配置 Nginx 反向代理、限制端口暴露、配置日志轮转。
|