291 lines
9.5 KiB
Markdown
291 lines
9.5 KiB
Markdown
# 生产一键部署脚本设计文档
|
||
|
||
## 1. 背景
|
||
|
||
当前仓库已经存在生产环境部署脚本 [bin/prod/deploy_release.sh](/Users/wkc/Desktop/loan-pricing/loan-pricing/bin/prod/deploy_release.sh) 和 Java 管理脚本 [bin/prod/restart_java.sh](/Users/wkc/Desktop/loan-pricing/loan-pricing/bin/prod/restart_java.sh)。但这套脚本依赖固定的 `/home/webapp` 目录结构、独立启停脚本以及安装好的 Nginx,不符合本次“在脚本同目录直接完成发布”的目标。
|
||
|
||
本次需要设计一份新的生产一键部署脚本,直接放在发布目录内执行。该目录中同时存在:
|
||
|
||
- 部署脚本本身
|
||
- 1 个发布 zip
|
||
- `backend/` 目录
|
||
- `frontend/` 目录
|
||
|
||
发布 zip 内固定包含 1 个后端 `jar` 和 1 个前端 `dist.zip`。脚本执行时需要完成旧版本备份、新版本替换、后端重启和前端解压部署。
|
||
|
||
## 2. 已确认约束
|
||
|
||
- 交付形态必须是单脚本,自包含,不拆分独立重启脚本
|
||
- Java 可执行路径直接写在脚本内常量中,不通过命令行参数或外部配置文件传入
|
||
- 发布包从脚本同目录读取
|
||
- 发布包内必须且只能包含 1 个后端 `jar` 和 1 个前端 `dist.zip`
|
||
- 旧版后端 `jar` 与旧版前端 `dist` 目录必须通过“原地重命名 + 时间戳”的方式保留
|
||
- 后端启动参数固定沿用当前生产约束:`--spring.profiles.active=pro --server.port=63310`
|
||
- 不增加兼容性、补丁性、兜底性或回滚性方案
|
||
- 方案必须保持最短路径实现,并可完成全链路逻辑验证
|
||
|
||
## 3. 当前资产与现状
|
||
|
||
### 3.1 现有生产脚本
|
||
|
||
[bin/prod/deploy_release.sh](/Users/wkc/Desktop/loan-pricing/loan-pricing/bin/prod/deploy_release.sh) 已具备以下能力:
|
||
|
||
- 解压发布包
|
||
- 校验包内 `jar` 和 `dist.zip`
|
||
- 备份旧版后端与前端
|
||
- 替换产物
|
||
- 调用独立 Java 管理脚本完成重启
|
||
|
||
但该脚本默认依赖 `/home/webapp` 固定目录、`restart_java.sh` 外部脚本和 Nginx 管理流程,超出了本次需求边界。
|
||
|
||
### 3.2 现有 Java 管理逻辑
|
||
|
||
[bin/prod/restart_java.sh](/Users/wkc/Desktop/loan-pricing/loan-pricing/bin/prod/restart_java.sh) 已实现:
|
||
|
||
- 使用 PID 文件记录后端进程
|
||
- 通过 `-Dloan.pricing.home` 标记识别托管进程
|
||
- 停止时先 `TERM` 再按需 `KILL`
|
||
- 启动后等待端口 `63310` 监听成功
|
||
|
||
这部分逻辑可以作为新单脚本中的后端启停参考,但最终不再拆成第二个脚本文件。
|
||
|
||
## 4. 方案对比
|
||
|
||
### 方案一:单脚本自包含部署
|
||
|
||
做法:
|
||
|
||
- 新增一份单独的部署脚本
|
||
- 在脚本顶部写死 `JAVA_BIN`、端口、日志路径和目录约定
|
||
- 执行时自动读取脚本同目录发布包
|
||
- 将解包、备份、替换、启停全部内联到同一文件
|
||
|
||
优点:
|
||
|
||
- 与本次“一起写在一个脚本里”的目标完全一致
|
||
- 发布目录内即可直接执行,路径最短
|
||
- 不依赖独立重启脚本或外部配置文件
|
||
- 用户只需维护一个脚本文件
|
||
|
||
缺点:
|
||
|
||
- 后续如果要单独管理 `start`、`stop`、`status`,需要再扩展脚本
|
||
|
||
### 方案二:保留现有双脚本结构,只调整入口
|
||
|
||
做法:
|
||
|
||
- 保留部署脚本和重启脚本两份文件
|
||
- 让部署脚本在同目录发布模式下调用重启脚本
|
||
|
||
优点:
|
||
|
||
- 可复用现有结构,改动相对少
|
||
|
||
缺点:
|
||
|
||
- 不满足“写在一起”的明确要求
|
||
- 交付物仍有两个脚本,使用路径不够直接
|
||
|
||
### 方案三:极简覆盖式脚本
|
||
|
||
做法:
|
||
|
||
- 不维护 PID 文件
|
||
- 直接按文件名覆盖
|
||
- 使用宽泛的 Java 进程匹配方式停止旧进程
|
||
|
||
优点:
|
||
|
||
- 脚本最短
|
||
|
||
缺点:
|
||
|
||
- 容易误伤其他 Java 进程
|
||
- 状态不可控
|
||
- 不满足全链路可验证要求
|
||
|
||
## 5. 设计结论
|
||
|
||
采用方案一:单脚本自包含部署。
|
||
|
||
最终交付为 1 份新的生产一键部署脚本,负责在脚本同目录完成完整发布链路。脚本以当前仓库已有生产脚本为参考,但不保留 Nginx 管理、根目录安装和双脚本调用等超出本次范围的设计。
|
||
|
||
## 6. 脚本结构设计
|
||
|
||
### 6.1 目录约定
|
||
|
||
脚本执行目录固定为脚本所在目录。目录内约定如下:
|
||
|
||
- `deploy_release.sh` 或本次确定的新脚本文件
|
||
- 1 个发布 zip
|
||
- `backend/`
|
||
- `frontend/`
|
||
|
||
脚本不依赖仓库根目录运行,也不依赖外部工作目录传参。
|
||
|
||
### 6.2 脚本内固定配置
|
||
|
||
脚本顶部固定声明以下配置项:
|
||
|
||
- `JAVA_BIN`
|
||
- `BACKEND_PORT=63310`
|
||
- `SPRING_PROFILE=pro`
|
||
- `JAVA_OPTS`
|
||
- `BACKEND_PID_FILE=backend/backend.pid`
|
||
- `BACKEND_LOG_FILE=backend/backend-console.log`
|
||
- `BACKEND_JAR_TARGET=backend/ruoyi-admin.jar`
|
||
- `FRONTEND_DIST_DIR=frontend/dist`
|
||
- `FRONTEND_DIST_ARCHIVE=frontend/dist.zip`
|
||
|
||
其中 `JAVA_BIN` 由维护者直接修改脚本内常量,不再设计其他配置入口。
|
||
|
||
### 6.3 执行流程
|
||
|
||
脚本执行顺序固定如下:
|
||
|
||
1. 定位脚本所在目录
|
||
2. 校验 `backend/` 与 `frontend/` 目录存在,不存在则直接失败
|
||
3. 在脚本同目录查找唯一一个发布 zip
|
||
4. 解压发布 zip 到临时目录
|
||
5. 校验临时目录内必须且只能找到 1 个 `jar` 和 1 个 `dist.zip`
|
||
6. 将 `backend/` 目录下现有 `jar` 重命名为带时间戳的备份文件
|
||
7. 将 `frontend/dist` 重命名为 `dist-时间戳`
|
||
8. 停止当前脚本托管的旧后端进程
|
||
9. 将新 `jar` 移入 `backend/ruoyi-admin.jar`
|
||
10. 将新 `dist.zip` 移入 `frontend/dist.zip`
|
||
11. 删除当前 `frontend/dist`
|
||
12. 解压新 `frontend/dist.zip` 到 `frontend/dist/`
|
||
13. 启动新的后端 `jar`
|
||
14. 等待端口 `63310` 监听成功
|
||
15. 输出部署完成信息
|
||
|
||
## 7. 后端启停设计
|
||
|
||
### 7.1 进程识别规则
|
||
|
||
为避免误杀机器上的其他 Java 进程,脚本只管理自己启动的后端实例。
|
||
|
||
识别规则如下:
|
||
|
||
- 启动时附加标记参数:`-Dloan.pricing.home=<脚本目录>`
|
||
- 优先读取 `backend/backend.pid`
|
||
- 如果 PID 文件失效,则使用 `pgrep -f` 按以下组合特征识别:
|
||
- `-Dloan.pricing.home=<脚本目录>`
|
||
- `backend/ruoyi-admin.jar`
|
||
|
||
只有同时满足托管标记和目标 jar 特征的进程,才允许被停止。
|
||
|
||
### 7.2 停止流程
|
||
|
||
停止逻辑如下:
|
||
|
||
1. 收集当前托管进程 PID
|
||
2. 如无托管进程,则清理失效 PID 文件并直接继续部署
|
||
3. 对托管进程发送 `TERM`
|
||
4. 等待最多 30 秒
|
||
5. 若仍存在残留进程,则发送 `KILL`
|
||
6. 删除 `backend/backend.pid`
|
||
|
||
### 7.3 启动流程
|
||
|
||
启动逻辑如下:
|
||
|
||
1. 校验 `JAVA_BIN` 可执行
|
||
2. 校验 `backend/ruoyi-admin.jar` 存在
|
||
3. 使用 `nohup` 后台启动
|
||
4. 写入 `backend/backend.pid`
|
||
5. 日志输出到 `backend/backend-console.log`
|
||
6. 使用固定参数启动:
|
||
- `-Dloan.pricing.home=<脚本目录>`
|
||
- `--spring.profiles.active=pro`
|
||
- `--server.port=63310`
|
||
7. 轮询端口监听状态,最长等待 30 秒
|
||
|
||
## 8. 备份与替换设计
|
||
|
||
### 8.1 后端备份
|
||
|
||
如果 `backend/` 目录中存在旧版 `jar`,则将其原地重命名为:
|
||
|
||
- `原文件名.YYYYMMDDHHMMSS.bak`
|
||
|
||
不新增独立备份目录,避免引入额外结构。
|
||
|
||
### 8.2 前端备份
|
||
|
||
如果 `frontend/dist` 目录存在,则将其原地重命名为:
|
||
|
||
- `dist-YYYYMMDDHHMMSS`
|
||
|
||
如果 `frontend/dist.zip` 已存在,则允许被新版本覆盖,不再为历史 `dist.zip` 单独追加备份规则。本次备份目标仅聚焦于用户明确提出的旧 `jar` 和旧前端 `dist`。
|
||
|
||
### 8.3 新产物替换
|
||
|
||
替换规则如下:
|
||
|
||
- 后端统一落到 `backend/ruoyi-admin.jar`
|
||
- 前端压缩包统一落到 `frontend/dist.zip`
|
||
- 前端静态资源统一解压到 `frontend/dist/`
|
||
|
||
该规则确保部署目录在每次发布后保持稳定结构,便于后续维护。
|
||
|
||
## 9. 失败处理设计
|
||
|
||
本次失败处理仅保留必要的强校验,不增加回滚或兼容分支。
|
||
|
||
以下场景直接失败退出:
|
||
|
||
- 脚本同目录下没有发布 zip
|
||
- 脚本同目录下存在多个发布 zip
|
||
- 解压后 `jar` 数量不是 1
|
||
- 解压后 `dist.zip` 数量不是 1
|
||
- `JAVA_BIN` 不可执行
|
||
- 新 `jar` 无法写入目标位置
|
||
- `dist.zip` 解压失败
|
||
- 后端 30 秒内未监听 `63310`
|
||
|
||
脚本退出时需要清理临时解压目录,避免残留中间文件。
|
||
|
||
## 10. 交付文件设计
|
||
|
||
本次设计对应的交付物限定为以下内容:
|
||
|
||
- 1 个生产一键部署脚本
|
||
- 1 份设计文档
|
||
- 1 份实施记录
|
||
|
||
建议脚本路径为:
|
||
|
||
- [bin/prod/deploy_from_package.sh](/Users/wkc/Desktop/loan-pricing/loan-pricing/bin/prod/deploy_from_package.sh)
|
||
|
||
保留 [bin/prod/deploy_release.sh](/Users/wkc/Desktop/loan-pricing/loan-pricing/bin/prod/deploy_release.sh) 不动,可以避免影响现有 `/home/webapp` 部署方式;新需求使用独立脚本承载,边界更清晰。
|
||
|
||
## 11. 验证设计
|
||
|
||
本次验证只覆盖该单脚本的一键部署链路,必须包含以下检查:
|
||
|
||
1. 执行 `sh -n` 校验脚本语法
|
||
2. 校验同目录只有 1 个发布 zip 时可继续执行
|
||
3. 校验没有 zip 或存在多个 zip 时直接失败
|
||
4. 校验发布包内必须正好有 1 个 `jar` 和 1 个 `dist.zip`
|
||
5. 校验旧 `jar` 被改名为带时间戳备份文件
|
||
6. 校验旧 `frontend/dist` 被改名为带时间戳目录
|
||
7. 校验新 `jar` 最终位于 `backend/ruoyi-admin.jar`
|
||
8. 校验新 `dist.zip` 最终位于 `frontend/dist.zip`
|
||
9. 校验前端资源成功解压到 `frontend/dist/`
|
||
10. 校验后端进程成功启动并监听 `63310`
|
||
11. 校验 `backend/backend.pid` 和 `backend/backend-console.log` 被正确生成
|
||
|
||
## 12. 非目标
|
||
|
||
本次明确不包含以下内容:
|
||
|
||
- 不接入 Nginx 启停或重载
|
||
- 不处理 Java 安装
|
||
- 不生成独立的后端重启脚本
|
||
- 不增加回滚脚本
|
||
- 不新增命令行参数模式
|
||
- 不新增外部配置文件
|
||
- 不兼容多实例或多应用部署场景
|