Files
piano-plan/docs/DEPLOYMENT_SOP.md
T

349 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 钢琴练习方案系统 - 部署 SOP
> 版本:v1.1
> 日期:2026-04-21
> 核心原则:**不删除,只备份后新增/替换**
---
## 一、部署原则(铁律)
| 操作 | 允许? | 说明 |
|------|--------|------|
| 删除容器 | ❌ 禁止 | 停止即可,容器配置是资产 |
| 删除 volume | ❌ 禁止 | 数据资产,不可恢复 |
| 删除 host 文件 | ❌ 禁止 | 先备份到 `/tmp/backup_YYYYMMDD/` |
| 覆盖文件 | ⚠️ 先备份 | 任何覆盖操作前必须先备份 |
| 停止容器 | ✅ 允许 | stop 是安全的 |
| 启动新容器 | ✅ 允许 | 配合正确的挂载配置 |
### 脚本优先原则(铁律)
> **当脚本执行失败时:修复脚本,而非绕过脚本。**
| 错误行为 | 正确行为 |
|---------|---------|
| 脚本报错 → `docker rm` 手动清理 | 脚本报错 → 查看日志 → 修复脚本问题 → 重跑脚本 |
| 挂载丢失 → 手动指定新挂载 | 挂载丢失 → 更新脚本的挂载配置 → 重跑脚本 |
| 镜像加载失败 → `docker rmi` 清理 | 镜像加载失败 → 检查错误 → 重跑脚本 |
| 容器启动失败 → `docker rm` 重来 | 容器启动失败 → 查看 `docker logs` → 修复配置 → 重跑脚本 |
**一旦开始用脚本部署,就要坚持用到底,中途放弃脚本去做手动操作,等于打开了破坏系统的潘多拉魔盒。**
---
## 二、部署前检查清单
```
[ ] 确认本地代码已验证通过
[ ] 确认无未提交的代码
[ ] 确认获得用户的明确同意(用户说"部署吧"或"可以部署"
[ ] 确认需要保留的挂载点列表(见下方)
[ ] 确认 Docker Desktop 已启动(Windows
```
### 必须保留的挂载点
| 类型 | 源 | 容器内 | 说明 |
|------|-----|--------|------|
| Bind Mount | `/opt/piano-plan/个性化方案` | `/app/个性化方案` | 问题文件(15个md |
| Volume | `piano-plan-data` | `/app/data` | SQLite 数据库 |
| Volume | `piano-plan-output` | `/app/output` | PDF 输出 |
| Bind Mount | `/opt/piano-plan/config` | `/app/config` | API 配置 |
---
## 三、部署步骤
### 3.1 本地构建
```powershell
# 1. 启动 Docker DesktopWindows
Start-Process "C:\Program Files\Docker\Docker\Docker Desktop.exe"
# 2. 等待 Docker 就绪
docker version # 看到 Server: Docker Desktop 即为就绪
# 3. 进入项目目录
cd "D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统"
# 4. 构建 Docker 镜像
docker build -t piano-plan:latest .
# 5. 保存镜像为 tar 文件
docker save piano-plan:latest -o piano-plan.tar
```
### 3.2 上传到服务器
```powershell
# 6. 上传到服务器临时目录
scp -i ~/.ssh/id_rsa piano-plan.tar root@47.106.65.108:/opt/piano-plan/
```
### 3.3 服务器部署(使用脚本!)
```bash
# 7. SSH 到服务器
ssh -i ~/.ssh/id_rsa root@47.106.65.108
# 8. 使用自动化部署脚本(会自动完成所有步骤并验证)
bash /path/to/deploy.sh /opt/piano-plan/piano-plan.tar
# 或者手动部署(不推荐):
# 8a. 确认当前容器挂载配置
docker inspect piano-plan --format '{{json .Mounts}}'
# 9. 创建备份
mkdir -p /opt/piano-plan/backups
docker cp piano-plan:/app/data/piano_plans.db /opt/piano-plan/backups/piano_plans.db.bak
# 10. 停止旧容器
docker stop piano-plan
docker rm piano-plan
# 11. 加载新镜像
docker load -i /opt/piano-plan/piano-plan.tar
# 12. 启动新容器(挂载配置必须完全正确!)
docker run -d \
--name piano-plan \
-p 5001:5001 \
--restart unless-stopped \
-e FLASK_ENV=production \
-v /opt/piano-plan/个性化方案:/app/个性化方案 \
-v piano-plan-data:/app/data \
-v piano-plan-output:/app/output \
-v /opt/piano-plan/config:/app/config \
piano-plan:latest
```
### 3.4 验证
```bash
# 13. 检查容器状态
docker ps --filter name=piano-plan
# 14. 检查日志
docker logs piano-plan --tail 20
# 15. 验证服务
curl -I http://localhost:5001/
# 16. 验证问题文件(应该看到15个md文件)
docker exec piano-plan ls /app/个性化方案/
# 17. 验证数据库(应该看到 templates 表)
docker exec piano-plan python -c "import sqlite3; conn=sqlite3.connect('/app/data/piano_plans.db'); print([r[0] for r in conn.execute('SELECT name FROM sqlite_master WHERE type=\"table\"')])"
# 18. 验证 API 配置
docker exec piano-plan cat /app/config/api_config.json
```
---
## 四、数据保护规范
### 4.1 必须保护的数据(绝对不删除)
| 数据类型 | 存储位置 | 说明 |
|----------|----------|------|
| 用户数据 | piano-plan-data:/app/data | users 表 |
| 学员数据 | piano-plan-data:/app/data | students, student_problems 表 |
| 班级数据 | piano-plan-data:/app/data | classes 表 |
| 练习方案 | piano-plan-data:/app/data | practice_plans 表 |
| 问题文件 | /opt/piano-plan/个性化方案 | 15个md文件 |
### 4.2 新增/更新的数据
| 数据类型 | 说明 |
|----------|------|
| templates 表 | AI提示词模板、报告导出模板 |
| api_config.json | API 配置(provider, model, api_key |
### 4.3 备份操作
```bash
# 备份数据库(volume
docker cp piano-plan:/app/data/piano_plans.db /opt/piano-plan/backups/piano_plans.db.$(date +%Y%m%d)
# 备份 API 配置
cp /opt/piano-plan/config/api_config.json /opt/piano-plan/backups/
# 列出所有备份
ls -la /opt/piano-plan/backups/
```
### 4.4 更新模板数据(部署后必做)
当代码中的模板内容更新时,需要手动更新生产数据库:
```python
# update_templates.py - 在服务器容器内执行
import sqlite3
NEW_AI_PROMPT = '''你是一位资深的钢琴教师。请根据学员的具体问题详情,生成一份个性化练习方案报告。
## 学员基本信息
- **姓名**: {student_name}
- **微信昵称**: {wechat_nickname}
- **每日可练习时间**: {practice_time}
## 学员被诊断的问题
{student_problems}
## 每个问题的详细信息和练习方法(请务必基于这些内容生成方案)
{problems}
## 任务要求
请根据上述学员的问题诊断和详细信息,生成一份针对性的练习方案报告:
1. 先简述该学员当前存在的主要问题
2. 给出一个每日练习安排建议
3. 针对每个问题给出具体的日常练习方法
4. 给出3-5条重点注意事项
请使用Markdown格式,语言专业、简洁、有鼓励性。'''
conn = sqlite3.connect('/app/data/piano_plans.db')
conn.execute("UPDATE templates SET content = ? WHERE type = 'ai_prompt'", (NEW_AI_PROMPT,))
conn.commit()
conn.close()
```
上传脚本到服务器执行:
```bash
scp update_templates.py root@47.106.65.108:/tmp/
ssh -i ~/.ssh/id_rsa root@47.106.65.108 "docker cp /tmp/update_templates.py piano-plan:/tmp/ && docker exec piano-plan python /tmp/update_templates.py"
```
---
## 五、回滚流程
### 5.1 快速回滚(推荐)
```bash
# 停止当前容器
docker stop piano-plan
docker rm piano-plan
# 使用旧镜像重新启动(如果镜像还在)
docker run -d \
--name piano-plan \
-p 5001:5001 \
--restart unless-stopped \
-e FLASK_ENV=production \
-v /opt/piano-plan/个性化方案:/app/个性化方案 \
-v piano-plan-data:/app/data \
-v piano-plan-output:/app/output \
-v /opt/piano-plan/config:/app/config \
piano-plan:latest
```
### 5.2 从备份恢复
```bash
# 恢复数据库
docker stop piano-plan
cp /opt/piano-plan/backups/piano_plans.db.bak /var/lib/docker/volumes/piano-plan-data/_data/piano_plans.db
docker start piano-plan
```
---
## 六、服务信息
| 项目 | 值 |
|------|-----|
| 生产地址 | https://piano.yoin.fun |
| SSH | `ssh -i ~/.ssh/id_rsa root@47.106.65.108` |
| 容器名 | piano-plan |
| 端口 | 5001 |
| 数据库位置 | piano-plan-data volume |
| 问题文件位置 | /opt/piano-plan/个性化方案 |
| API配置位置 | /opt/piano-plan/config |
### Volume 列表
| Volume | 容器内路径 | 说明 |
|--------|------------|------|
| piano-plan-data | /app/data | SQLite 数据库 |
| piano-plan-output | /app/output | PDF 导出 |
### Bind Mount 列表
| 源 | 容器内路径 | 说明 |
|-----|------------|------|
| /opt/piano-plan/个性化方案 | /app/个性化方案 | 问题文件 |
| /opt/piano-plan/config | /app/config | API 配置 |
---
## 七、API 配置说明
### 7.1 支持的 Provider
| Provider | Endpoint | 模型 |
|----------|----------|------|
| minimax | https://api.minimaxi.com/anthropic/v1 | MiniMax-M2.7-highspeed |
| volcengine | https://ark.cn-beijing.volces.com/api/coding/v3 | doubao-seed-2.0-pro |
| deepseek | https://api.deepseek.com | deepseek-chat |
### 7.2 API 配置存储
- 配置存储在 `/opt/piano-plan/config/api_config.json`
- 每个 provider 的 key 存储在 `api_keys` 映射中
- 切换 provider 时自动使用对应的 key
---
## 八、常见问题
### Q: 部署后问题文件看不到?
A: 检查挂载 `/opt/piano-plan/个性化方案:/app/个性化方案` 是否正确
### Q: 数据库是空的?
A: 检查 volume `piano-plan-data` 是否被错误覆盖,尝试从备份恢复
### Q: 容器无法启动?
A: 检查日志 `docker logs piano-plan`,常见原因:端口被占用、volume 权限问题
### Q: API 配置没生效?
A: 检查 `/opt/piano-plan/config/api_config.json` 是否存在且正确
### Q: SSE 不完整,提示词显示不出来或卡在95%?
A: nginx 需要为 SSE (Server-Sent Events) 配置特定的代理设置:
```nginx
location /api/generate-plan {
proxy_buffering off;
proxy_cache off;
tcp_nodelay on;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_buffers 8 32k;
proxy_buffer_size 32k;
proxy_max_temp_file_size 1024m;
}
```
配置文件位置:`/srv/nginx/conf/conf.d/piano.yoin.fun.conf`
---
## 九、检查清单(部署完成后必填)
```
[ ] 容器状态:running
[ ] 服务响应:HTTP 200/302
[ ] 问题文件数量:15个 md 文件
[ ] 数据库记录:users, students, classes, student_problems, practice_plans 完整
[ ] templates 表存在且包含 AI提示词模板、报告导出模板
[ ] API 配置:provider, model, api_key 正确
[ ] 功能验证:能生成练习方案
```
---
> **最后更新**2026-04-21
> **更新原因**:更新部署流程,添加数据保护规范,明确挂载点配置;添加 SSE 问题排查