# 剧核工厂生产环境部署文档 本文档适用于在 Windows 本地构建项目,并一键部署到 Linux AMD64 服务器。 这里的“一键部署”是指服务器上的 `one_click_deployment.sh` 会一次完成环境初始化、数据库迁移、服务注册、Nginx 和 HTTPS 配置。 完整发布分为两个明确阶段:本地构建并上传、服务器执行一键部署。`push.bat` 只上传文件,不会自动修改数据库或重启线上服务。 ## 1. 最短操作流程 首次部署,在 Windows 项目根目录执行: ```powershell .\API\deploy\push.bat root@服务器IP with-env ssh root@服务器IP sudo bash /opt/juyou_ai/deploy/one_click_deployment.sh ``` 日常更新,在 Windows 项目根目录执行: ```powershell .\API\deploy\push.bat root@服务器IP ssh root@服务器IP sudo bash /opt/juyou_ai/deploy/one_click_deployment.sh ``` 首次部署使用 `with-env`,将本地正式环境配置送到服务器;日常更新不使用 `with-env`,继续保留服务器已有的生产配置。 上传和服务器部署是两个独立步骤,便于在执行数据库迁移和重启前安排备份与维护窗口。 ## 2. 固定路径与服务 | 项目 | 固定值 | | --- | --- | | 服务器项目目录 | `/opt/juyou_ai` | | API 二进制 | `/opt/juyou_ai/bin/jcf-api` | | 部署脚本 | `/opt/juyou_ai/deploy/one_click_deployment.sh` | | WEB 静态目录 | `/opt/juyou_ai/static/web` | | ADMIN 静态目录 | `/opt/juyou_ai/static/admin` | | 数据库迁移目录 | `/opt/juyou_ai/migrations` | | systemd 服务名 | `juyou_ai` | | API 内部监听地址 | `127.0.0.1:8123` | | 生产环境文件 | `/etc/juyou_ai/juyou_ai.env` | | 临时环境文件 | `/tmp/juyou_ai.env` | | Whisper 离线模型 | `/opt/juyou_ai/models/faster-whisper-small` | | Whisper 运行缓存 | `/var/cache/juyou_ai/huggingface` | | Nginx 配置 | `/etc/nginx/conf.d/juyou_ai.conf` | | SSL 证书目录 | `/etc/nginx/ssl` | API 仅监听服务器本机地址,公网请求统一通过 Nginx 进入。 ## 3. 首次部署 ### 3.1 准备本地环境 本地需要安装: - Node.js 和 npm - Go - Python 3;服务器缺少离线模型时,本机还需要 `faster-whisper` - tar - OpenSSH Client(`ssh`、`scp`) 在终端确认命令可用: ```powershell npm --version go version python --version tar --version ssh -V ``` 首次在本机下载 Whisper 模型前安装 Python 依赖: ```powershell python -m pip install faster-whisper==1.2.1 ``` `push.bat` 不会自动安装前端依赖。首次构建或 `package-lock.json` 发生变化后,先执行: ```powershell npm --prefix WEB ci npm --prefix ADMIN ci ``` 首次部署使用的正式环境配置保存在本地: ```text API/.env ``` 至少确认以下字段使用正式值,不能保留 `replace-with`、`change-me`、`example.com` 或开发环境默认值: ```env DATABASE_NAME=juchuang_factory DATABASE_USER=jcf DATABASE_PASSWORD=数据库强密码 JWT_SECRET_KEY=随机长密钥 ADMIN_BOOTSTRAP_USERNAME=admin ADMIN_BOOTSTRAP_PASSWORD=管理员初始强密码 CONFIG_ENCRYPTION_KEY_VERSION=v1 CONFIG_ENCRYPTION_KEY=正式加密密钥 COS_SECRET_ID=腾讯云子账号SecretId COS_SECRET_KEY=腾讯云子账号SecretKey COS_BUCKET=juyouai-1451538162 COS_REGION=ap-chengdu COS_ENDPOINT=https://cos.ap-chengdu.myqcloud.com COS_PUBLIC_BASE_URL=https://cos.youjuhui.xyz ``` COS 媒体使用独立域名时,还必须在腾讯云控制台进入 **对象存储 COS → 对应存储桶 → 安全管理 → 跨域访问 CORS 设置**,添加以下规则。将域名替换为实际访问 WEB 的域名;多个 WEB 域名需要全部列出。 ```json [ { "AllowedOrigins": [ "https://juhefactory.com", "https://www.juhefactory.com" ], "AllowedMethods": [ "GET", "HEAD" ], "AllowedHeaders": [ "*" ], "ExposeHeaders": [ "Accept-Ranges", "Content-Length", "Content-Range", "Content-Type", "ETag" ], "MaxAgeSeconds": 3600 } ] ``` 保存策略后,确认媒体响应包含与当前 WEB 域名一致的 `Access-Control-Allow-Origin`。否则视频可以播放,但浏览器会禁止导出静帧和通过 Blob 直接下载。 可以使用以下命令生成随机密钥: ```bash openssl rand -hex 32 ``` ### 3.2 准备服务器 目标服务器需要满足: - Linux AMD64/x86_64,使用 Debian 或 Ubuntu 最稳妥 - 使用 systemd - SSH 登录用户具备 sudo 权限,推荐使用 root - 云安全组及防火墙已放行 TCP 80、443 - 启用 HTTPS 时,域名已经解析到服务器公网 IP 脚本虽然包含 `apt-get`、`dnf` 和 `yum` 的安装分支,但服务账户固定使用 `www-data`。使用非 Debian/Ubuntu 发行版前,需要先确认系统存在 `www-data` 用户和用户组,并确认 PostgreSQL、Redis 的 systemd 服务名与脚本兼容。 ### 3.3 本地构建并上传 在 Windows 项目根目录执行: ```powershell .\API\deploy\push.bat root@服务器IP with-env ``` 示例: ```powershell .\API\deploy\push.bat root@192.168.1.1 with-env ``` 命令完成并显示以下内容后,才进入服务器执行部署: ```text Deployment push complete. ``` 本地上传完成只表示文件已经同步到 `/opt/juyou_ai`,此时不会自动安装依赖、迁移数据库或重启服务。 ### 3.4 登录服务器并执行首次部署 本地另行登录服务器: ```powershell ssh root@服务器IP ``` 服务器上执行: ```bash bash /opt/juyou_ai/deploy/one_click_deployment.sh ``` 如果当前不是 root 用户,执行: ```bash sudo bash /opt/juyou_ai/deploy/one_click_deployment.sh ``` 首次运行会询问两项配置。 域名: ```text juhefactory.com ``` 多个域名使用空格分隔: ```text juhefactory.com www.juhefactory.com ``` 不申请 SSL 时输入: ```text _ ``` 然后输入 SSL 证书邮箱;不申请 SSL 时直接回车。域名和邮箱会保存到: ```text /opt/juyou_ai/deploy/.deploy.conf ``` 首次部署完成后,将该文件复制到本地: ```powershell scp root@服务器IP:/opt/juyou_ai/deploy/.deploy.conf .\API\deploy\.deploy.conf ``` 后续上传不会携带本地 `.deploy.conf`;服务器会继续使用 `/opt/juyou_ai/deploy/.deploy.conf`,不会被本地文件覆盖。 ### 3.5 验证首次部署 查看服务状态: ```bash systemctl status juyou_ai --no-pager systemctl status nginx --no-pager systemctl status postgresql --no-pager systemctl status redis-server --no-pager ``` 检查 API: ```bash curl -i http://127.0.0.1:8123/health ``` 启用 HTTPS 后检查公网入口: ```bash curl -i https://你的域名/health ``` 页面地址: ```text https://你的域名/ https://你的域名/admin/ ``` ## 4. 更新部署 每次更新都分为本地上传和服务器部署两步,不能只执行其中一步。 ### 4.1 本地重新构建并上传 在 Windows 项目根目录执行: ```powershell .\API\deploy\push.bat root@服务器IP ``` 等待出现: ```text Deployment push complete. ``` ### 4.2 服务器应用更新 登录服务器: ```powershell ssh root@服务器IP ``` 执行: ```bash bash /opt/juyou_ai/deploy/one_click_deployment.sh ``` 更新部署会: - 继续使用服务器现有的 `/etc/juyou_ai/juyou_ai.env` - 执行尚未执行的数据库迁移 - 更新 systemd 与 Nginx 配置 - 重启 systemd 服务 `juyou_ai` ### 4.3 只更新已上传文件但暂不部署 只执行本地 `push.bat` 即可。上传完成后不要在服务器运行 `one_click_deployment.sh`。 此时: - `/opt/juyou_ai` 中已经是新文件 - `/etc/juyou_ai/juyou_ai.env` 保持不变 - 当前运行中的 API 进程仍可能使用旧二进制 - 数据库迁移、Nginx 更新和服务重启均未执行 需要正式应用更新时,再执行服务器部署命令。 ## 5. 推送参数说明 | 命令 | 是否上传 `API/.env` | 是否自动执行服务器部署 | 使用场景 | | --- | --- | --- | --- | | `push.bat root@服务器IP` | 否 | 否 | 日常增量更新 | | `push.bat root@服务器IP stage-only` | 否 | 否 | 与默认推送行为相同,用于明确表达“只暂存” | | `push.bat root@服务器IP with-env` | 是 | 否 | 首次部署或明确替换整套生产环境配置 | 三种命令都只负责构建、打包和上传。`stage-only` 当前是语义标记,执行行为与不传第二个参数相同。只有随后在服务器运行 `one_click_deployment.sh`,才会执行迁移、更新服务配置并重启应用。 ## 6. 常用维护命令 服务管理: ```bash systemctl restart juyou_ai systemctl stop juyou_ai systemctl start juyou_ai systemctl reload nginx nginx -t ``` 查看 API 日志: ```bash journalctl -u juyou_ai -f journalctl -u juyou_ai -n 200 --no-pager ``` 修改域名或证书邮箱: ```bash nano /opt/juyou_ai/deploy/.deploy.conf ``` 也可以删除配置,让下次部署重新询问: ```bash rm /opt/juyou_ai/deploy/.deploy.conf ``` 更新 acme.sh: ```bash bash /opt/juyou_ai/deploy/update_acme.sh ``` ## 7. 备份与故障排查 正式执行数据库迁移前应先备份数据库。以下示例中的数据库名必须替换为生产环境 `DATABASE_NAME` 的实际值: ```bash mkdir -p /opt/juyou_ai/backups sudo -u postgres pg_dump -Fc juchuang_factory | tee /opt/juyou_ai/backups/juchuang_factory.dump >/dev/null ``` PostgreSQL: ```bash systemctl status postgresql --no-pager journalctl -u postgresql -n 200 --no-pager ``` Redis: ```bash systemctl status redis-server --no-pager redis-cli ping ``` Nginx: ```bash nginx -t journalctl -u nginx -n 200 --no-pager ``` API: ```bash systemctl status juyou_ai --no-pager journalctl -u juyou_ai -n 200 --no-pager ls -l /etc/juyou_ai/juyou_ai.env ls -l /opt/juyou_ai/bin/jcf-api ``` 如果部署提示环境字段缺失、格式错误或仍为示例值,修改本地 `API/.env`,重新执行 `push.bat`,然后再次在服务器运行部署脚本。 如果数据库迁移失败,应先核对数据库实际结构和 `jcf_schema_migrations`,不得通过手工插入迁移记录直接绕过错误。数据库迁移不会自动回滚。 ## 8. 自动化部署流程说明 本节用于解释脚本内部做了什么。日常操作只需要按“首次部署”或“更新部署”章节执行命令。 ### 8.1 `push.bat` 的本地流程 1. 检查目标 SSH 地址;仅在使用 `with-env` 时检查本地 `API/.env` 是否存在。 2. 通过 SSH 检查服务器是否已有完整的 Whisper `small` 模型。缺少时,本机运行 `download_whisper_model.py` 下载模型,并把 `models/` 加入本次上传包。 3. 执行 `npm --prefix WEB run build`,类型检查通过后将 WEB 输出到 `API/static/web`。 4. 执行 `npm --prefix ADMIN run build`,类型检查通过后将 ADMIN 输出到 `API/static/admin`。 5. 使用 `GOOS=linux`、`GOARCH=amd64`、`-trimpath` 和裁剪调试信息的链接参数,编译 `API/bin/jcf-api`。 6. 创建本机临时压缩包,包含: ```text bin/ static/ deploy/ migrations/ acme/ workers/ models/ # 仅服务器缺少离线模型时包含 .env # 仅使用 with-env 时包含 ``` 7. 使用 SCP 将压缩包上传到 `/tmp/juyou-deploy.tar.gz`。 8. 使用 SSH 在服务器的 `/tmp/juyou-deploy-work` 临时目录解包。 9. 默认删除可能残留的 `/tmp/juyou_ai.env`;使用 `with-env` 时才将 `.env` 安装为权限 `600` 的 `/tmp/juyou_ai.env`,并从项目文件中删除 `.env`。 10. 服务器存在 `rsync` 时,以 `--delete` 将发布文件同步到 `/opt/juyou_ai`;不存在时使用 `cp -a` 覆盖复制。 11. 删除服务器临时解包目录和压缩包。 12. 删除本机临时压缩包。 同步时保留以下服务器内容: ```text /opt/juyou_ai/media/ /opt/juyou_ai/backups/ /opt/juyou_ai/models/ ``` `models/` 默认不会被 `rsync --delete` 清理;仅当服务器缺少完整模型且本次压缩包含有模型时,才会单独同步模型内容。 `/opt/juyou_ai/deploy/.deploy.conf` 仅在服务器首次运行部署脚本时生成;上传包会排除本地 `.deploy.conf`,后续部署不会覆盖服务器配置。 `.go` 源码、测试代码、`cmd`、`internal`、`tools`、`media` 和 `backups` 不会进入上传包;运行时需要的 `workers` 会进入上传包。 ### 8.2 `one_click_deployment.sh` 的服务器流程 1. 首次运行时只从 JuYouAI 自己的 Nginx 配置恢复域名;没有该配置时询问域名。仅复用输入域名对应的证书和私钥,不扫描其他项目的证书,并保存 `.deploy.conf`。 2. 使用 `with-env` 时检测 `/tmp/juyou_ai.env` 并将其作为本次环境配置来源,否则继续使用服务器生产环境文件。 3. 调用 `deploy_with_ssl.sh` 执行正式部署。 ### 8.3 `deploy_with_ssl.sh` 和 `prepare_server.sh` 的服务器流程 1. 本机执行 `push.bat` 时检查服务器是否已有 Whisper 模型;首次部署会在本机下载并上传到 `/opt/juyou_ai/models/faster-whisper-small`。服务器不访问 Hugging Face。 2. 安装 sudo、Nginx、PostgreSQL、Redis、FFmpeg、curl、socat、ca-certificates、openssl、Python 3 和 pip;缺少时安装 `faster-whisper 1.2.1`。 3. 启用并启动 PostgreSQL、Redis 和 Nginx。 4. 存在 `/tmp/juyou_ai.env` 时将其安装到 `/etc/juyou_ai/juyou_ai.env`;否则保留现有生产环境文件,权限保持 `root:root`、`600`。 5. 将生产环境标准化为: ```env APP_ENV=production DEBUG=false SERVER_ADDRESS=127.0.0.1:8123 DATABASE_HOST=127.0.0.1 DATABASE_PORT=5432 DATABASE_URL= REDIS_HOST=127.0.0.1 REDIS_PORT=6379 CORS_ORIGINS=根据部署域名生成 FFMPEG_PATH=ffmpeg FFPROBE_PATH=ffprobe PYTHON_PATH=python3 ASR_SCRIPT_PATH=/opt/juyou_ai/workers/transcribe.py JCF_ASR_CACHE_DIR=/var/cache/juyou_ai HF_HOME=/var/cache/juyou_ai/huggingface FASTER_WHISPER_MODEL_PATH=/opt/juyou_ai/models/faster-whisper-small ``` 6. 校验离线 Whisper 模型、数据库、JWT、管理员、配置加密和 COS 等必填配置。 7. 创建或更新 PostgreSQL 用户和数据库,配置本机认证。 8. 按文件名顺序执行 `/opt/juyou_ai/migrations/*.up.sql`,并记录到 `jcf_schema_migrations`。 9. 创建或更新 `/etc/systemd/system/juyou_ai.service`: ```ini [Service] User=www-data Group=www-data WorkingDirectory=/opt/juyou_ai EnvironmentFile=/etc/juyou_ai/juyou_ai.env CacheDirectory=juyou_ai ExecStart=/opt/juyou_ai/bin/jcf-api Restart=always RestartSec=3 ``` 10. 配置 Nginx:`/api/` 和健康检查路径代理到 `127.0.0.1:8123`,`/admin/` 提供 ADMIN 静态文件,其他路径提供 WEB 静态文件,上传体积上限为 2 GiB。 11. 配置域名且已有该域名的证书时直接复用;没有证书但填写邮箱时,先创建 HTTP 验证站点,再使用 acme.sh 通过 HTTP-01 申请并安装 HTTPS 证书;域名填 `_`,或没有证书且未填写邮箱时使用 HTTP。 12. 启用 HTTPS 时把 80 端口请求重定向到 HTTPS,并允许 TLS 1.2、TLS 1.3。 13. 执行 `nginx -t`,配置检查成功后重载 Nginx。 14. 重启 systemd 服务 `juyou_ai`。 15. 部署成功后删除 `/tmp/juyou_ai.env`。 如果部署中途失败,临时环境文件可能保留,以便排查并重新执行部署;问题解决并部署成功后会自动清理。 ### 8.4 重复执行与失败边界 - 脚本使用 `set -euo pipefail`,关键命令失败时会立即退出。 - 已有 PostgreSQL 用户会更新密码,已有数据库不会重复创建。 - 已记录在 `jcf_schema_migrations` 的迁移会跳过,未记录迁移按文件名顺序执行。 - systemd 和 Nginx 配置每次都会按当前发布内容重新生成。 - 已保存的域名、证书邮箱和匹配域名的现有证书会复用。 - 部署脚本不会自动恢复旧二进制,也不会自动回滚已经成功执行的数据库迁移。发布前必须自行完成数据库备份;需要应用级回滚时,应重新上传兼容当前数据库结构的旧版本并再次部署。