# 钢琴练习方案系统 - 部署 SOP > 版本:v1.2 > 日期:2026-04-23 > 核心原则:**不删除,只备份后新增/替换** --- ## 一、部署原则(铁律) | 操作 | 允许? | 说明 | |------|--------|------| | 删除容器 | ❌ 禁止 | 停止即可,容器配置是资产 | | 删除 volume | ❌ 禁止 | 数据资产,不可恢复 | | 删除 host 文件 | ❌ 禁止 | 先备份到 `/tmp/backup_YYYYMMDD/` | | 覆盖文件 | ⚠️ 先备份 | 任何覆盖操作前必须先备份 | | 停止容器 | ✅ 允许 | stop 是安全的 | | 启动新容器 | ✅ 允许 | 配合正确的挂载配置 | ### 脚本优先原则(铁律) > **当脚本执行失败时:修复脚本,而非绕过脚本。** | 错误行为 | 正确行为 | |---------|---------| | 脚本报错 → `docker rm` 手动清理 | 脚本报错 → 查看日志 → 修复脚本问题 → 重跑脚本 | | 挂载丢失 → 手动指定新挂载 | 挂载丢失 → 更新脚本的挂载配置 → 重跑脚本 | | 镜像加载失败 → `docker rmi` 清理 | 镜像加载失败 → 检查错误 → 重跑脚本 | | 容器启动失败 → `docker rm` 重来 | 容器启动失败 → 查看 `docker logs` → 修复配置 → 重跑脚本 | **一旦开始用脚本部署,就要坚持用到底,中途放弃脚本去做手动操作,等于打开了破坏系统的潘多拉魔盒。** --- ## 二、部署前检查清单 ``` [ ] 确认本地代码已验证通过 [ ] 确认无未提交的代码 [ ] 确认获得用户的明确同意(用户说"部署吧"或"可以部署") [ ] 确认需要保留的挂载点列表(见下方) [ ] 确认 Docker Desktop 已启动(Windows) ``` ### 必须保留的挂载点 | 类型 | 源 | 容器内 | 说明 | |------|-----|--------|------| | Volume | `piano-plan-data` | `/app/data` | SQLite 数据库 | | Volume | `piano-plan-output` | `/app/output` | PDF 输出 | | Bind Mount | `/opt/piano-plan/config` | `/app/config` | API 配置 | > ⚠️ **已移除**:`/opt/piano-plan/个性化方案` 挂载点(问题文件已迁移到数据库 `problems` 表) --- ## 三、部署步骤 ### 3.1 本地构建 ```powershell # 1. 启动 Docker Desktop(Windows) 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. 创建带时间戳的备份目录 mkdir -p /opt/piano-plan/backups/backup_$(date +%Y%m%d) # 9. 备份当前数据库和配置 docker cp piano-plan:/app/data/piano_plans.db /opt/piano-plan/backups/backup_$(date +%Y%m%d)/ cp /opt/piano-plan/config/api_config.json /opt/piano-plan/backups/backup_$(date +%Y%m%d)/ # 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 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 stop piano-plan # 14. 上传开发环境数据库到服务器(在本地执行) scp -i ~/.ssh/id_rsa data/piano_plans.db root@47.106.65.108:/opt/piano-plan/backups/ # 15. 覆盖生产数据库 docker cp /opt/piano-plan/backups/piano_plans.db piano-plan:/app/data/piano_plans.db # 16. 重启容器 docker start piano-plan ``` ### 3.5 验证 ```bash # 17. 检查容器状态 docker ps --filter name=piano-plan # 18. 检查日志 docker logs piano-plan --tail 20 # 19. 验证服务 curl -I http://localhost:5001/ # 20. 验证数据库表 docker exec piano-plan ls /app/data/ # 21. 验证 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 表 | | 问题数据 | piano-plan-data:/app/data | problems 表(已从文件迁移到数据库) | ### 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 cp /opt/piano-plan/backups/backup_20260423/piano_plans.db piano-plan:/app/data/piano_plans.db # 重启容器 docker start piano-plan ``` ### 5.2 完整回滚(恢复旧镜像+数据库) ```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 piano-plan-data:/app/data \ -v piano-plan-output:/app/output \ -v /opt/piano-plan/config:/app/config \ piano-plan:latest # 从备份恢复数据库 docker stop piano-plan docker cp /opt/piano-plan/backups/backup_YYYYMMDD/piano_plans.db piano-plan:/app/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/config | /app/config | API 配置 | > ⚠️ **已移除**:`/opt/piano-plan/个性化方案` 挂载(问题文件已迁移到数据库) --- ## 七、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: 检查 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 [ ] 数据库记录:users, students, classes, student_problems, practice_plans, problems 完整 [ ] templates 表存在且包含 AI提示词模板、报告导出模板 [ ] API 配置:provider, model, api_key 正确 [ ] 功能验证:能生成练习方案 ``` --- > **最后更新**:2026-04-23 > **更新原因**:v1.2 部署更新;移除个性化方案挂载(问题已迁移到数据库);更新备份和回滚流程