# 48 生产发布 本流程由维护电脑发起,使用已有 SSH 密钥。生产只更新 `ai-chatbot`,不运行数据库迁移, 不更新 ZCode 原生运行时,不重启主机、Nginx 或其他 PM2 应用。 ## 已核实的机器 | 用途 | SSH 入口 | 主机 | | --- | --- | --- | | 构建机 | `root@192.168.102.146:22` | `dao02-Default-string`,Ubuntu 24.04,Node 20 | | 同一构建机的公网入口 | `root@45.192.99.48:23322` | FRP 转发,不是生产宿主机 | | 生产机 | `root@45.192.99.48:64192` | `SfSTE0ttmsycO`,CentOS Stream 9,Node 20 | | 维护连接跳板 | `root@192.168.58.20:22` | 已信任的 NAS,仅转发 SSH | 公网页面 `https://ai.yydd.ai/` 由生产 Nginx 转发至 `127.0.0.1:3125` 的 PM2 应用。 脚本固定验证生产、构建机的 SSH 指纹和主机名;私钥留在维护电脑,不转发 SSH Agent。 构建机原有 `/var/www/ai-chatbot` 是旧实例,本流程不会使用或覆盖它。 ## 操作入口 维护电脑需要 Python 3.9+、Paramiko 3.3+、Git,以及已信任的 SSH `known_hosts`。 默认使用 `~/.ssh/45.192.99.48.pem` 和跳板密钥 `~/.ssh/youyidian_prod_ed25519`。 可用 `--key`、`--jump-key`、`--known-hosts` 覆盖本机路径,不要把私钥提交到仓库。 ```powershell python scripts/production48.py status python scripts/production48.py jobs # 完成 PR 合并后,确认远端 main 的最新提交,工作区必须干净。 git fetch origin git switch main git merge --ff-only origin/main python scripts/production48.py build ``` `build` 将确定的 `origin/main` 提交通过 `git archive` 送到构建机, 在 `/opt/ai-chatbot-builds/<时间-提交>/source` 安装锁定依赖并构建。 使用 pnpm 9.12.3,不改变全局 pnpm。安装时不运行依赖脚本,构建时禁用自动安装系统服务。 构建保持 TypeScript 检查,限制 Next 构建并行度和 Node 内存,并降低进程优先级。 构建机仅收到 `NEXT_PUBLIC_*` 等公开页面配置,以及指向本机关闭端口的占位数据库配置。 不会收到生产数据库密码、AI Key、认证密钥或存储凭据。随后在构建机回环地址启动隔离候选实例, 核对构建 ID 并执行公开路由烟测,测试结束自动关闭该候选实例。 成功后会输出发布 ID,例如 `20261030T120000Z-0123456789ab`。后续步骤使用这个完整 ID: ```powershell python scripts/production48.py stage --release <发布ID> python scripts/production48.py activate --release <发布ID> python scripts/production48.py status # 仅对当前生效的这一版执行回滚: python scripts/production48.py rollback --release <发布ID> ``` `stage` 校验传输 SHA256,在 `/var/www/ai-chatbot-releases/<发布ID>` 解包。 它检查压缩包路径、磁盘容量、锁文件、实际依赖文件/链接清单和 public 文件校验和。 完成解包后至少保留 1 GiB 空间;不足时停止,不自动删除历史构建。 当前和旧浏览器标签页所需的静态资源通过硬链接保留,不改动旧构建。 `activate` 会重新核对生产版本和任务状态,拒绝在最近一小时仍有未结束生成任务时切换。 更早的历史挂起记录只写入发布回执,不会自动修改数据库中的状态。 它保存旧 PM2 配置和启动快照,然后仅重建 `ai-chatbot` 这一项 PM2 定义, 让新版本的工作目录、可执行路径和环境变量全部生效,不改变其他应用, 因此会有短暂切换窗口。健康检查同时核对进程目录、构建 ID、首页和路由。 检查失败自动恢复旧配置并再次验活;通过后执行 `pm2 save`,让重启后使用已验收版本。 PM2 的普通 restart 会保留旧工作目录和程序路径,因此不能用它替代本流程的单应用定义重建。 若一次切换已经明确回滚并验证原版本健康,修复发布工具后,可保留已校验制品并显式重试: ```powershell python scripts/production48.py activate --release <发布ID> --retry-rolled-back ``` 只有日志阶段为 `rolled-back` 且当前工作目录/构建目录仍匹配原版本才允许重试。 旧尝试日志另外保留。运行制品的 `sourceCommit` 始终指向当时实际构建的 main 提交, 后续工具修复不改写这个来源;`operatorCommit` 单独记录执行切换的维护工具版本。 ## 运行资料与边界 每个发布目录包含构建机按锁文件安装的完整运行依赖和 public 源码资源。 节点之间不复用 node_modules 链接,避免服务器漏装依赖补丁却被误认为同一版本。 对内容完全相同的依赖文件,通过 XFS 的写入时复制创建独立 inode,复用底层数据块; 缺失或不同的文件从已校验制品写入。任何一份副本后续修改均不会改动另一份。 生产文件系统不支持该操作时停止,不自动回退成可能耗尽磁盘的完整复制。 public 先从当前运行版本创建写入时复制副本,保留未纳入 Git 的运行资源,再叠加本次源码资源。 完整文件和链接清单在切换前再次验证,Node ABI 必须一致,并在生产机测试 sharp 原生模块。 原目录 `/var/www/ai-chatbot` 中的 `.env.local`、`tmp` 和模型配置备份仍通过链接使用, 不会复制、删除或覆盖。新版本也不在生产机执行依赖安装或构建。 本流程适用于应用代码迭代;数据库、共享资源的删除/迁移和原生 Agent 升级各自单独发布。 原生产 checkout 的支线及未提交改动仍保留,不能据其 Git HEAD 判断已发布版本。 实际发布证据是:运行 cwd、`.next/BUILD_ID`、该目录 `release.json` 和 `activation.json`。 维护电脑的 `.git/releases/<发布ID>/` 保留提交、构建日志、烟测、传输校验和及操作回执。 服务器回滚配置可能含环境元数据,目录权限为 0700、文件为 0600,不放入公开目录或 Git。 仓库历史全量 lint 仍有已记录的问题,不能声称全部质量门禁通过。 每次 PR 须记录本次改动的验证、历史失败和未覆盖项目;当前流程不会自动触发生产发布。