|
| 1 | +# Gying Movie 上线迁移指南 |
| 2 | + |
| 3 | +> 本文档覆盖从本地开发环境迁移到云端生产环境的完整流程。 |
| 4 | +
|
| 5 | +--- |
| 6 | + |
| 7 | +## 一、当前架构总览 |
| 8 | + |
| 9 | +``` |
| 10 | +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ |
| 11 | +│ Next.js 16 │───▶│ Spring Boot │───▶│ MySQL │ |
| 12 | +│ (Port 3000) │ │ (Port 8880) │ │ (Port 3306) │ |
| 13 | +└──────────────┘ └──────┬───────┘ └──────────────┘ |
| 14 | + │ |
| 15 | + ┌──────▼───────┐ ┌──────────────┐ |
| 16 | + │ Redis │ │ MinIO │ |
| 17 | + │ (Port 6379) │ │ (Port 9000) │ |
| 18 | + └──────────────┘ └──────────────┘ |
| 19 | +``` |
| 20 | + |
| 21 | +**本地环境配置:** |
| 22 | +- MySQL: `localhost:3306`,用户 `root`,数据库 `gying` |
| 23 | +- Redis: `localhost:6379`,无密码 |
| 24 | +- MinIO: Docker 容器 `minio-server`,端口 `9000-9001`,桶名 `gying` |
| 25 | +- 图片存储路径: `gying/{category}/{movieId}/384.avif` |
| 26 | + |
| 27 | +--- |
| 28 | + |
| 29 | +## 二、生产架构(推荐) |
| 30 | + |
| 31 | +``` |
| 32 | + ┌─────────────────────────────┐ |
| 33 | + │ Nginx │ |
| 34 | + │ (80/443 + SSL 终止) │ |
| 35 | + │ /api/** → Backend │ |
| 36 | + │ /* → Frontend │ |
| 37 | + └──────┬──────────┬───────────┘ |
| 38 | + │ │ |
| 39 | + ┌──────▼───┐ ┌───▼──────────┐ |
| 40 | + │ Next.js │ │ Spring Boot │ |
| 41 | + │ (容器) │ │ (容器) │ |
| 42 | + └──────────┘ └──┬─────┬──────┘ |
| 43 | + │ │ |
| 44 | + ┌────────▼┐ ┌──▼──────────┐ |
| 45 | + │ Cloud │ │ Cloud Redis │ |
| 46 | + │ MySQL │ │ │ |
| 47 | + └─────────┘ └──────────────┘ |
| 48 | +
|
| 49 | + 图片 CDN/OSS ← 用户浏览器直接访问(不经过 Nginx) |
| 50 | +``` |
| 51 | + |
| 52 | +--- |
| 53 | + |
| 54 | +## 三、数据库迁移(MySQL → 云端 MySQL) |
| 55 | + |
| 56 | +### 3.1 推荐的云数据库服务 |
| 57 | + |
| 58 | +| 云厂商 | 产品 | 最低规格 | 参考价格 | |
| 59 | +|--------|------|---------|---------| |
| 60 | +| 阿里云 | RDS MySQL | 1核1G | ~¥50/月 | |
| 61 | +| 腾讯云 | CDB MySQL | 1核1G | ~¥45/月 | |
| 62 | +| 华为云 | RDS MySQL | 1核1G | ~¥50/月 | |
| 63 | + |
| 64 | +### 3.2 导出数据 |
| 65 | + |
| 66 | +在本地 WSL 或 Docker 中执行: |
| 67 | + |
| 68 | +```bash |
| 69 | +# 导出整个数据库(含数据) |
| 70 | +mysqldump -u root -p'Root@123' \ |
| 71 | + --single-transaction \ |
| 72 | + --routines \ |
| 73 | + --triggers \ |
| 74 | + --set-gtid-purged=OFF \ |
| 75 | + gying > gying_backup_$(date +%Y%m%d).sql |
| 76 | + |
| 77 | +# 只导出表结构(不含数据,用于初始化) |
| 78 | +mysqldump -u root -p'Root@123' --no-data gying > gying_schema.sql |
| 79 | +``` |
| 80 | + |
| 81 | +### 3.3 云端数据库初始化 |
| 82 | + |
| 83 | +```bash |
| 84 | +# 1. 创建数据库和用户 |
| 85 | +mysql -h <cloud-host> -u <admin-user> -p <<EOF |
| 86 | +CREATE DATABASE gying CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; |
| 87 | +CREATE USER 'gying_app'@'%' IDENTIFIED BY '<strong-password>'; |
| 88 | +GRANT ALL PRIVILEGES ON gying.* TO 'gying_app'@'%'; |
| 89 | +FLUSH PRIVILEGES; |
| 90 | +EOF |
| 91 | + |
| 92 | +# 2. 导入数据 |
| 93 | +mysql -h <cloud-host> -u gying_app -p gying < gying_backup_20260611.sql |
| 94 | +``` |
| 95 | + |
| 96 | +### 3.4 注意事项 |
| 97 | + |
| 98 | +- 云端 MySQL 一般**强制 SSL**,`application-prod.yml` 已配置 `useSSL=true` |
| 99 | +- 创建专用数据库用户,**不要用 root** |
| 100 | +- 确保 `serverTimezone=Asia/Shanghai` |
| 101 | +- 建议开启自动备份(云厂商一般提供) |
| 102 | + |
| 103 | +--- |
| 104 | + |
| 105 | +## 四、图片存储迁移(MinIO → 云端 OSS) |
| 106 | + |
| 107 | +### 4.1 方案对比 |
| 108 | + |
| 109 | +| 方案 | 优点 | 缺点 | |
| 110 | +|------|------|------| |
| 111 | +| **阿里云 OSS** | 国内速度快、CDN 集成好 | 需备案域名 | |
| 112 | +| **腾讯云 COS** | 与腾讯云生态集成 | 需备案域名 | |
| 113 | +| **继续用 MinIO** | 无迁移成本、S3 兼容 | 需自己维护服务器 | |
| 114 | +| **Cloudflare R2** | 免出口流量费、全球 CDN | 国内访问可能偏慢 | |
| 115 | + |
| 116 | +### 4.2 迁移到阿里云 OSS(推荐) |
| 117 | + |
| 118 | +#### Step 1: 创建 OSS Bucket |
| 119 | + |
| 120 | +``` |
| 121 | +Bucket 名称: gying-images |
| 122 | +地域: 华东1(杭州)或与云服务器同地域 |
| 123 | +读写权限: 公共读(Public Read) |
| 124 | +存储类型: 标准存储 |
| 125 | +``` |
| 126 | + |
| 127 | +#### Step 2: 批量迁移 MinIO 中的图片 |
| 128 | + |
| 129 | +```bash |
| 130 | +# 安装 rclone(通用 S3 迁移工具) |
| 131 | +curl https://rclone.org/install.sh | sudo bash |
| 132 | + |
| 133 | +# 配置 MinIO 源 |
| 134 | +rclone config create minio s3 \ |
| 135 | + provider=Minio \ |
| 136 | + access_key_id=admin \ |
| 137 | + secret_access_key=miniopassword \ |
| 138 | + endpoint=http://localhost:9000 |
| 139 | + |
| 140 | +# 配置阿里云 OSS 目标 |
| 141 | +rclone config create alioss s3 \ |
| 142 | + provider=Alibaba \ |
| 143 | + access_key_id=<your-ak> \ |
| 144 | + secret_access_key=<your-sk> \ |
| 145 | + endpoint=https://oss-cn-hangzhou.aliyuncs.com |
| 146 | + |
| 147 | +# 执行迁移(同步所有文件) |
| 148 | +rclone sync minio:gying alioss:gying-images --progress |
| 149 | + |
| 150 | +# 验证 |
| 151 | +rclone ls alioss:gying-images | head -20 |
| 152 | +``` |
| 153 | + |
| 154 | +#### Step 3: 更新后端配置 |
| 155 | + |
| 156 | +在 `.env` 中设置: |
| 157 | + |
| 158 | +```bash |
| 159 | +# 阿里云 OSS 公网域名 |
| 160 | +MINIO_URL_PREFIX=https://gying-images.oss-cn-hangzhou.aliyuncs.com/ |
| 161 | + |
| 162 | +# 如果绑定了 CDN 域名(推荐) |
| 163 | +MINIO_URL_PREFIX=https://img.yourdomain.com/ |
| 164 | +``` |
| 165 | + |
| 166 | +**无需修改后端代码!** `processMovieUrl()` 只拼接前缀,数据库中的相对路径 `tv/1Znq/384.avif` 保持不变。 |
| 167 | + |
| 168 | +#### Step 4: 更新爬虫脚本 |
| 169 | + |
| 170 | +```python |
| 171 | +# crawler/gying_crawler.py 中修改: |
| 172 | + |
| 173 | +# 方案 A:直接上传到 OSS(使用 oss2 SDK) |
| 174 | +import oss2 |
| 175 | +auth = oss2.Auth('<AccessKeyId>', '<AccessKeySecret>') |
| 176 | +bucket = oss2.Bucket(auth, 'https://oss-cn-hangzhou.aliyuncs.com', 'gying-images') |
| 177 | + |
| 178 | +def upload_image_to_oss(type_code, movie_id): |
| 179 | + object_name = f"{type_code}/{movie_id}/384.avif" |
| 180 | + target_url = f"https://s.tutu.pm/img/{type_code}/{movie_id}/384.avif" |
| 181 | + resp = requests.get(target_url, headers=HEADERS, timeout=10) |
| 182 | + if resp.status_code == 200: |
| 183 | + bucket.put_object(object_name, resp.content) |
| 184 | + return object_name |
| 185 | + |
| 186 | +# 方案 B:继续使用 MinIO + rclone 定期同步 |
| 187 | +# 保持现有爬虫不变,用 crontab 定期 rclone sync |
| 188 | +``` |
| 189 | + |
| 190 | +--- |
| 191 | + |
| 192 | +## 五、应用部署 |
| 193 | + |
| 194 | +### 5.1 服务器准备 |
| 195 | + |
| 196 | +推荐配置(入门级): |
| 197 | + |
| 198 | +| 项目 | 规格 | |
| 199 | +|------|------| |
| 200 | +| CPU | 2 核 | |
| 201 | +| 内存 | 4 GB | |
| 202 | +| 系统盘 | 50 GB SSD | |
| 203 | +| 带宽 | 3-5 Mbps(或按量付费) | |
| 204 | +| 系统 | Ubuntu 22.04 / Debian 12 | |
| 205 | + |
| 206 | +### 5.2 安装 Docker |
| 207 | + |
| 208 | +```bash |
| 209 | +# 安装 Docker + Docker Compose |
| 210 | +curl -fsSL https://get.docker.com | sh |
| 211 | +sudo usermod -aG docker $USER |
| 212 | + |
| 213 | +# 验证 |
| 214 | +docker --version |
| 215 | +docker compose version |
| 216 | +``` |
| 217 | + |
| 218 | +### 5.3 部署应用 |
| 219 | + |
| 220 | +```bash |
| 221 | +# 1. 上传项目到服务器 |
| 222 | +scp -r ./gying-movie user@server:/opt/ |
| 223 | + |
| 224 | +# 2. SSH 到服务器 |
| 225 | +ssh user@server |
| 226 | + |
| 227 | +# 3. 配置环境变量 |
| 228 | +cd /opt/gying-movie |
| 229 | +cp .env.example .env |
| 230 | +nano .env # 填入实际值 |
| 231 | + |
| 232 | +# 4. 构建并启动 |
| 233 | +docker compose -f docker-compose.prod.yml up -d --build |
| 234 | + |
| 235 | +# 5. 检查状态 |
| 236 | +docker compose -f docker-compose.prod.yml ps |
| 237 | +docker compose -f docker-compose.prod.yml logs -f backend |
| 238 | +``` |
| 239 | + |
| 240 | +### 5.4 配置 HTTPS |
| 241 | + |
| 242 | +```bash |
| 243 | +# 使用 Let's Encrypt 免费证书 |
| 244 | +apt install certbot |
| 245 | +certbot certonly --standalone -d yourdomain.com |
| 246 | + |
| 247 | +# 将证书复制到 nginx 目录 |
| 248 | +mkdir -p nginx/ssl |
| 249 | +cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem nginx/ssl/ |
| 250 | +cp /etc/letsencrypt/live/yourdomain.com/privkey.pem nginx/ssl/ |
| 251 | + |
| 252 | +# 取消 nginx.conf 中 HTTPS 部分的注释,然后重启 |
| 253 | +docker compose -f docker-compose.prod.yml restart nginx |
| 254 | + |
| 255 | +# 设置自动续期 |
| 256 | +crontab -e |
| 257 | +# 添加:0 3 * * * certbot renew --quiet && docker compose -f /opt/gying-movie/docker-compose.prod.yml restart nginx |
| 258 | +``` |
| 259 | + |
| 260 | +--- |
| 261 | + |
| 262 | +## 六、环境变量速查表 |
| 263 | + |
| 264 | +| 变量 | 说明 | 示例值 | |
| 265 | +|------|------|--------| |
| 266 | +| `DB_HOST` | 云端 MySQL 地址 | `rm-xxx.mysql.rds.aliyuncs.com` | |
| 267 | +| `DB_PORT` | MySQL 端口 | `3306` | |
| 268 | +| `DB_NAME` | 数据库名 | `gying` | |
| 269 | +| `DB_USER` | 数据库用户 | `gying_app` | |
| 270 | +| `DB_PASSWORD` | 数据库密码 | `***` | |
| 271 | +| `REDIS_HOST` | Redis 地址 | `redis`(compose 内部) | |
| 272 | +| `REDIS_PASSWORD` | Redis 密码 | `***` | |
| 273 | +| `MINIO_URL_PREFIX` | 图片 URL 前缀 | `https://img.yourdomain.com/` | |
| 274 | +| `CORS_ALLOWED_ORIGIN` | 允许的前端域名 | `https://yourdomain.com` | |
| 275 | +| `JWT_SECRET` | JWT 签名密钥 | `openssl rand -base64 48` 生成 | |
| 276 | +| `JWT_EXPIRATION_MS` | Token 过期时间 | `86400000`(24小时) | |
| 277 | + |
| 278 | +--- |
| 279 | + |
| 280 | +## 七、上线前检查清单 |
| 281 | + |
| 282 | +### 安全项 |
| 283 | +- [ ] JWT Secret 已替换为强随机值(≥32 字节) |
| 284 | +- [ ] 数据库密码已修改,不使用 root 用户 |
| 285 | +- [ ] Redis 设置了密码 |
| 286 | +- [ ] `.env` 文件未提交到 Git(已在 `.gitignore` 中) |
| 287 | +- [ ] CORS 仅允许生产域名,移除了 `http://localhost:*` |
| 288 | +- [ ] HTTPS 已启用 |
| 289 | + |
| 290 | +### 数据项 |
| 291 | +- [ ] 数据库已导入到云端 MySQL |
| 292 | +- [ ] MinIO 图片已同步到 OSS |
| 293 | +- [ ] `MINIO_URL_PREFIX` 指向正确的 CDN/OSS 域名 |
| 294 | +- [ ] 测试了图片加载是否正常 |
| 295 | + |
| 296 | +### 功能验证 |
| 297 | +- [ ] 首页电影列表加载正常 |
| 298 | +- [ ] 电影详情页显示完整 |
| 299 | +- [ ] 搜索功能正常 |
| 300 | +- [ ] 登录/注册功能正常 |
| 301 | +- [ ] 发表评论/回复正常 |
| 302 | +- [ ] 资源下载链接可点击 |
| 303 | +- [ ] 管理后台功能正常 |
| 304 | + |
| 305 | +### 性能项 |
| 306 | +- [ ] Nginx Gzip 压缩已开启 |
| 307 | +- [ ] 静态资源缓存已配置(30天) |
| 308 | +- [ ] HikariCP 连接池参数合理 |
| 309 | + |
| 310 | +--- |
| 311 | + |
| 312 | +## 八、后续优化建议 |
| 313 | + |
| 314 | +| 优先级 | 项目 | 说明 | |
| 315 | +|--------|------|------| |
| 316 | +| P1 | **接口缓存** | 电影列表/详情加 Redis 缓存,TTL 10-30 分钟,降低数据库压力 | |
| 317 | +| P1 | **图片懒加载** | 首页 MovieCard 使用 `loading="lazy"` + 低质量占位图 | |
| 318 | +| P2 | **全文搜索** | 接入 Elasticsearch 替代 `LIKE` 模糊查询,提升搜索精度和速度 | |
| 319 | +| P2 | **CDN 加速** | 前端静态资源上 CDN(Vercel / Cloudflare Pages) | |
| 320 | +| P2 | **日志监控** | 接入阿里云 SLS 或 Grafana + Loki,实时监控错误和性能 | |
| 321 | +| P3 | **CI/CD** | GitHub Actions 自动构建 Docker 镜像并部署 | |
| 322 | +| P3 | **定时任务** | 资源链接有效性检测,自动标记失效链接 | |
| 323 | +| P3 | **国际化完善** | 补充缺失的翻译 key,增加日语等语言 | |
| 324 | + |
| 325 | +--- |
| 326 | + |
| 327 | +## 九、常用运维命令 |
| 328 | + |
| 329 | +```bash |
| 330 | +# 查看所有服务状态 |
| 331 | +docker compose -f docker-compose.prod.yml ps |
| 332 | + |
| 333 | +# 查看后端日志(最近 100 行 + 实时跟踪) |
| 334 | +docker compose -f docker-compose.prod.yml logs -f --tail=100 backend |
| 335 | + |
| 336 | +# 重启单个服务 |
| 337 | +docker compose -f docker-compose.prod.yml restart backend |
| 338 | + |
| 339 | +# 更新部署(拉取新代码后) |
| 340 | +git pull |
| 341 | +docker compose -f docker-compose.prod.yml up -d --build |
| 342 | + |
| 343 | +# 数据库备份 |
| 344 | +docker exec -it <mysql-container> mysqldump -u gying_app -p gying > backup_$(date +%Y%m%d).sql |
| 345 | + |
| 346 | +# 清理无用 Docker 镜像 |
| 347 | +docker image prune -f |
| 348 | +``` |
0 commit comments