新增NAS一键打包部署脚本及Docker部署方案

This commit is contained in:
wkc
2026-03-13 15:13:18 +08:00
parent 77f53cb991
commit d63bdbf7b7
44 changed files with 2728 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
# CCDI Docker 后端部署 Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 为 Spring Boot 后端与 `lsfx mock server` 建立可构建、可上传、可在服务器运行的 Docker 部署链路。
**Architecture:** 后端产物继续使用 Maven 构建出的 `ruoyi-admin.jar`,运行时通过 Java 21 容器加载 `local` profile。`lsfx mock server` 作为独立 Python 服务纳入仓库,并在 Compose 中与后端共享网络命名空间,以兼容现有 `http://localhost:8000` 配置。
**Tech Stack:** Maven, Spring Boot 3, Java 21, Docker Compose, Python 3.11, FastAPI, PowerShell, Paramiko
---
### Task 1: 整理 `lsfx mock server` 到主仓库
**Files:**
- Create: `lsfx-mock-server/**`
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
- Test: `lsfx-mock-server/tests/test_api.py`
**Step 1: 复制并清理运行文件**
- 从现有工作树复制 `main.py``config/``models/``routers/``services/``utils/``requirements.txt``tests/`
- 排除 `__pycache__``.pytest_cache`
**Step 2: 运行 mock server 测试**
Run: `python -m pytest lsfx-mock-server/tests -q`
Expected: 测试通过,接口与健康检查可用
**Step 3: 修正最小必要问题**
- 若路径、依赖或导入失败,仅做最小修复
**Step 4: 记录目录用途**
-`lsfx-mock-server/README.md` 补充与主项目集成的启动说明
### Task 2: 编写后端与 mock 的 Docker 文件
**Files:**
- Create: `docker/backend/Dockerfile`
- Create: `docker/mock/Dockerfile`
- Modify: `lsfx-mock-server/README.md`
**Step 1: 创建后端镜像定义**
- 使用 Java 21 运行时镜像
- 工作目录统一为 `/app`
- 复制 `backend/ruoyi-admin.jar`
- 默认入口使用 `java -jar /app/ruoyi-admin.jar`
**Step 2: 创建 mock 镜像定义**
- 使用 `python:3.11-slim`
- 安装 `lsfx-mock-server/requirements.txt`
- 启动 `python main.py`
**Step 3: 本地验证镜像定义**
Run: `docker build -f docker/mock/Dockerfile -t ccdi-lsfx-mock:test .`
Expected: 构建成功
### Task 3: 编写 Compose 编排
**Files:**
- Create: `docker-compose.yml`
- Create: `.env.example`
**Step 1: 定义 `backend` 服务**
- 端口映射 `62318:8080`
- 环境变量包含 `SPRING_PROFILES_ACTIVE=local``RUOYI_PROFILE=/app/data/ruoyi`
- 卷挂载运行目录与日志目录
**Step 2: 定义 `lsfx-mock-server` 服务**
- 使用 `network_mode: "service:backend"`
- 依赖 `backend`
- 不额外对外暴露端口
**Step 3: 做配置校验**
Run: `docker compose config`
Expected: Compose 文件能正常展开且无语法错误
### Task 4: 编写后端打包与远端部署脚本
**Files:**
- Create: `deploy/deploy.ps1`
- Create: `deploy/remote-deploy.py`
**Step 1: 编写本地打包流程**
- 执行 Maven 打包
- 收集 `ruoyi-admin.jar`
- 检查 `lsfx-mock-server` 运行文件完整性
**Step 2: 编写上传脚本**
- 使用 Paramiko 建立 SSH 与 SFTP 连接
- 创建远端目录 `/volume1/webapp/ccdi`
- 上传 Compose、Dockerfile、后端 JAR、mock 目录
**Step 3: 编写远端启动命令**
- 兼容 `docker compose``docker-compose`
- 执行 `up -d --build`
- 返回容器状态与后端日志摘要
### Task 5: 构建与联调验证
**Files:**
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 本地构建后端**
Run: `mvn clean package -DskipTests`
Expected: `ruoyi-admin/target/ruoyi-admin.jar` 生成成功
**Step 2: 本地跑通 Compose 校验**
Run: `docker compose config`
Expected: 无错误
**Step 3: 远端部署验证**
- 验证 `backend` 容器启动
- 验证 `mock server` 在后端网络命名空间内可访问
- 验证 `http://116.62.17.81:62318/swagger-ui/index.html`
**Step 4: 提交**
```bash
git add lsfx-mock-server docker docker-compose.yml .env.example deploy docs/plans/2026-03-13-ccdi-docker-deployment-*.md
git commit -m "新增Docker后端部署方案"
```

View File

@@ -0,0 +1,156 @@
# CCDI Docker 部署设计
**日期**: 2026-03-13
**目标**: 将当前项目的前端、后端与 `lsfx mock server` 打包后上传到服务器 `116.62.17.81:9444``/volume1/webapp/ccdi`,并使用 Docker 统一部署运行。
## 背景与约束
- 前端对外端口固定为 `62319`
- 后端对外端口固定为 `62318`
- `lsfx mock server` 对外端口固定为 `62320`
- 后端运行时必须使用 Java 21
- 后端运行 profile 固定为 `local`
- 后端继续使用现有 [`application-local.yml`](/D:/ccdi/ccdi/ruoyi-admin/src/main/resources/application-local.yml) 中的 MySQL、Redis 与 `lsfx.api.base-url`
- `lsfx.api.base-url` 当前为 `http://localhost:8000`,希望不改动既有配置
- 服务端部署根目录固定为 `/volume1/webapp/ccdi`
## 方案选择
### 方案一:`mock server` 与后端共用网络命名空间
前端、后端、`mock server` 全部使用 Docker 部署,其中 `lsfx mock server` 通过 `network_mode: "service:backend"` 与后端共享网络命名空间。
优点:
- 不需要修改 `application-local.yml` 中的 `http://localhost:8000`
- 后端容器内访问 `localhost:8000` 时,实际就是同网络命名空间内的 `mock server`
- 对外暴露前端、后端和 `lsfx mock server` 端口,同时仍保持后端对 `localhost:8000` 的兼容访问
缺点:
- Compose 编排方式比普通三容器互联稍特殊
### 方案二:三服务独立组网
后端访问 `http://lsfx-mock-server:8000`
优点:
- Compose 结构最常规
缺点:
- 需要修改现有 `local` 配置,不符合本次要求
### 方案三:本地构建镜像后上传镜像包
优点:
- 服务器上不需要源码级构建
缺点:
- 容易受到本地与服务器架构差异影响
- 镜像体积大,上传与迭代成本高
## 最终方案
采用方案一。
## 部署架构
### 前端
- 本地执行 `npm run build:prod`
- 使用 Nginx 容器托管 `ruoyi-ui/dist`
- Nginx 将 `/prod-api``/v3/api-docs` 反向代理到后端容器 `http://backend:8080`
- Docker 对外暴露 `62319`
### 后端
- 本地执行 `mvn clean package -DskipTests`
- 使用 Java 21 运行 `ruoyi-admin/target/ruoyi-admin.jar`
- 通过环境变量设置:
- `SPRING_PROFILES_ACTIVE=local`
- `RUOYI_PROFILE=/app/data/ruoyi`
- Docker 对外暴露 `62318`
- 同时额外映射 `62320 -> 8000`,让宿主机可直接访问共享网络命名空间中的 `lsfx mock server`
### LSFX Mock Server
- 将现有 FastAPI 实现整理为主仓库正式目录
- 使用 Python 3.11 容器运行
- 默认监听 `8000`
- 通过后端共享网络命名空间,对外暴露 `62320`
- 通过 `network_mode: "service:backend"` 让后端继续使用 `http://localhost:8000`
## 目录规划
服务器目录规划如下:
```text
/volume1/webapp/ccdi/
├── docker-compose.yml
├── .env
├── deploy/
│ ├── deploy.ps1
│ └── remote-deploy.py
├── docker/
│ ├── backend/Dockerfile
│ ├── frontend/Dockerfile
│ ├── frontend/nginx.conf
│ └── mock/Dockerfile
├── backend/
│ └── ruoyi-admin.jar
├── frontend/
│ └── dist/
├── lsfx-mock-server/
└── runtime/
├── ruoyi/
└── logs/
```
## 关键配置设计
### `ruoyi.profile`
当前 [`application-local.yml`](/D:/ccdi/ccdi/ruoyi-admin/src/main/resources/application-local.yml) 未定义 `ruoyi.profile`。后端代码中的 [`RuoYiConfig.java`](/D:/ccdi/ccdi/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java) 依赖该值计算上传、导入与头像目录。
因此在 Docker 运行时通过环境变量补充:
```text
RUOYI_PROFILE=/app/data/ruoyi
```
并挂载到服务器目录,确保容器重启后数据保留。
### 反向代理
前端仍保持生产构建时的 `VUE_APP_BASE_API=/prod-api`避免改动业务代码。Nginx 负责将:
- `/prod-api/` 转发到 `http://backend:8080/`
- `/v3/api-docs/` 转发到 `http://backend:8080/v3/api-docs/`
## 部署流程
1. 本地整理并提交部署文件
2. 本地打包前端与后端产物
3. 本地通过 SSH/SFTP 上传到服务器目标路径
4. 远端执行 `docker compose up -d --build`
5. 验证前端、后端、`mock server` 与代理链路
## 验证点
- `http://116.62.17.81:62319` 可打开前端
- `http://116.62.17.81:62318/swagger-ui/index.html` 可访问后端文档
- `http://116.62.17.81:62320/docs` 可访问 `lsfx mock server` 文档
- 前端登录与接口请求经 `/prod-api` 正常转发
- 后端容器可访问 `http://localhost:8000`
- `mock server` 健康检查正常
## 风险与处理
- 若服务器仅支持 `docker-compose`,部署脚本需兼容 `docker compose``docker-compose`
- 若服务器无法访问 `192.168.0.111` 上的 MySQL/Redis则后端启动会失败本次不改该配置
- 若服务器无 Docker 运行环境,需要先补齐 Docker 与 Compose 插件

View File

@@ -0,0 +1,89 @@
# CCDI Docker 前端部署 Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 为 Vue 前端建立可打包、可容器化、可上传并在服务器通过 Nginx 对外提供服务的 Docker 部署链路。
**Architecture:** 前端继续使用现有 `npm run build:prod` 产出 `dist`,容器内由 Nginx 提供静态资源与反向代理。通过 `/prod-api``/v3/api-docs` 将请求转发到后端容器,保持现有业务代码与生产环境变量不变。
**Tech Stack:** Vue 2, npm, Nginx, Docker Compose, PowerShell, Paramiko
---
### Task 1: 定义前端容器与 Nginx 代理
**Files:**
- Create: `docker/frontend/Dockerfile`
- Create: `docker/frontend/nginx.conf`
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 创建前端镜像定义**
- 基于 `nginx:stable-alpine`
- 复制 `frontend/dist` 到 Nginx 静态目录
- 复制自定义 `nginx.conf`
**Step 2: 配置反向代理**
- `/` 返回前端 `index.html`
- `/prod-api/` 代理到 `http://backend:8080/`
- `/v3/api-docs/` 代理到 `http://backend:8080/v3/api-docs/`
**Step 3: 校验 Nginx 配置**
Run: `docker run --rm -v ${PWD}/docker/frontend/nginx.conf:/etc/nginx/conf.d/default.conf:ro nginx:stable-alpine nginx -t`
Expected: syntax is ok
### Task 2: 编写前端打包收集流程
**Files:**
- Modify: `deploy/deploy.ps1`
- Create: `frontend/.gitkeep`
**Step 1: 构建前端**
Run: `npm --prefix ruoyi-ui run build:prod`
Expected: `ruoyi-ui/dist` 生成成功
**Step 2: 收集部署目录**
-`ruoyi-ui/dist` 复制到 `frontend/dist`
- 保持部署目录与 Dockerfile 输入一致
### Task 3: 将前端加入 Compose
**Files:**
- Modify: `docker-compose.yml`
- Modify: `.env.example`
**Step 1: 定义 `frontend` 服务**
- 暴露 `62319:80`
- 依赖 `backend`
**Step 2: 校验 Compose**
Run: `docker compose config`
Expected: 前端服务、依赖与端口映射正确
### Task 4: 联调验证
**Files:**
- Modify: `docs/plans/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 检查前端生产产物**
- 验证 `dist/index.html``static/` 文件生成
**Step 2: 远端验证访问**
- 验证 `http://116.62.17.81:62319`
- 登录后检查浏览器请求是否发往 `/prod-api`
- 验证 Swagger 页面可通过前端入口转发访问
**Step 3: 提交**
```bash
git add docker/frontend deploy/deploy.ps1 docker-compose.yml .env.example docs/plans/2026-03-13-ccdi-docker-deployment-*.md
git commit -m "新增Docker前端部署方案"
```

View File

@@ -0,0 +1,69 @@
# Deploy To NAS Backend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 为现有后端打包与远端部署链路增加一个可被 `.bat` 入口复用的 `DryRun` 模式。
**Architecture:** 保持 `deploy.ps1` 作为真实执行器不变,仅增加参数解析和轻量分支,让 BAT 可以先走快速验证,再走真实部署。底层上传与远端部署逻辑继续复用现有 Python 脚本。
**Tech Stack:** PowerShell, Python, Docker Compose, Windows CMD
---
### Task 1: 为 `deploy.ps1` 增加 DryRun 模式
**Files:**
- Modify: `deploy/deploy.ps1`
- Test: `tests/deploy/test_deploy_to_nas.py`
**Step 1: 写失败测试**
```python
def test_deploy_ps1_dry_run_prints_target():
...
```
**Step 2: 运行测试确认失败**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_deploy_ps1_dry_run_prints_target -q`
Expected: 失败,因为 `deploy.ps1` 还不支持 `-DryRun`
**Step 3: 最小实现**
- 新增 `-DryRun` 开关
- 打印 `Host/Port/Username/RemoteRoot`
- 直接返回成功
**Step 4: 重新运行测试**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_deploy_ps1_dry_run_prints_target -q`
Expected: 通过
### Task 2: 保持真实部署行为不变
**Files:**
- Modify: `deploy/deploy.ps1`
- Modify: `deploy/remote-deploy.py`
- Test: `tests/deploy/test_deploy_to_nas.py`
**Step 1: 写失败测试**
```python
def test_deploy_ps1_still_accepts_default_parameters():
...
```
**Step 2: 运行测试确认失败**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_deploy_ps1_still_accepts_default_parameters -q`
Expected: 因缺少对应输出或参数处理失败而不通过
**Step 3: 最小实现**
- 保持默认 NAS 参数
- 保持真实执行路径不变
**Step 4: 运行测试**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py -q`
Expected: 通过

View File

@@ -0,0 +1,110 @@
# 一键部署 BAT 入口设计
**日期**: 2026-03-13
**目标**: 在现有 PowerShell 与 Python 部署链路之上,新增一个 Windows 下可直接双击或命令行执行的 `.bat` 入口脚本,用于一键打包前后端并部署到 NAS。
## 背景
当前仓库已经有以下部署能力:
- [`deploy/deploy.ps1`](/D:/ccdi/ccdi/deploy/deploy.ps1):负责本地打包、组装部署目录、上传到 NAS、远端执行 Docker Compose
- [`deploy/remote-deploy.py`](/D:/ccdi/ccdi/deploy/remote-deploy.py):负责 SSH/SFTP 上传与远端 Docker 部署
但 Windows 用户直接使用时仍需要显式调用 PowerShell不够直观。
## 方案选择
### 方案一:薄封装 BAT 入口
新增一个 `deploy/deploy-to-nas.bat`,只做以下几件事:
- 定位仓库根目录
- 调用 PowerShell 执行 `deploy.ps1`
- 提供默认的 NAS 连接参数
- 原样透传退出码
优点:
- 复用现有稳定链路
- 维护成本最低
- 双击和命令行都能使用
缺点:
- 底层仍依赖 PowerShell、Python、Maven、npm
### 方案二:把所有逻辑都改写到 BAT
优点:
- 形式上只有一个入口文件
缺点:
- BAT 对目录处理、错误处理、网络部署支持差
- 可维护性明显下降
### 方案三BAT + 独立配置文件
优点:
- 多环境切换更灵活
缺点:
- 对当前固定 NAS 场景偏重
## 最终方案
采用方案一。
## 设计细节
### 入口脚本
新增 [`deploy/deploy-to-nas.bat`](/D:/ccdi/ccdi/deploy/deploy-to-nas.bat)。
职责:
- 默认使用:
- Host: `116.62.17.81`
- Port: `9444`
- Username: `wkc`
- Password: `wkc@0825`
- RemoteRoot: `/volume1/webapp/ccdi`
- 支持命令行覆盖参数
- 统一调用 `powershell -ExecutionPolicy Bypass -File deploy.ps1`
### 可验证性
为避免每次验证都真的触发完整部署,给 [`deploy/deploy.ps1`](/D:/ccdi/ccdi/deploy/deploy.ps1) 增加一个 `-DryRun` 开关:
- 打印将要使用的目标参数
- 不执行 Maven、npm、上传与远端部署
- 直接返回 `0`
这样 `.bat` 可以配合 `--dry-run` 做快速回归验证。
### 参数约定
BAT 入口参数顺序:
```text
deploy-to-nas.bat [host] [port] [username] [password] [remoteRoot] [--dry-run]
```
如果不传,则使用默认值。
## 验证方式
1. `cmd /c deploy\deploy-to-nas.bat --dry-run`
2. 确认输出中的 NAS 地址、端口、路径与默认值一致
3. 可选:`cmd /c deploy\deploy-to-nas.bat 116.62.17.81 9444 wkc wkc@0825 /volume1/webapp/ccdi --dry-run`
4. 最终运行无 `--dry-run` 的真实部署
## 风险与处理
- 若用户机器禁止 PowerShell 脚本执行BAT 通过 `-ExecutionPolicy Bypass` 绕过当前会话限制
- 若路径中存在空格BAT 需统一用双引号包裹
- 若密码中存在特殊字符BAT 只做原样透传,不自行拼接复杂 shell 表达式

View File

@@ -0,0 +1,68 @@
# Deploy To NAS Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 新增一个 Windows 下可双击执行的 `.bat` 一键入口,默认触发前后端打包并部署到 NAS。
**Architecture:** 通过 `deploy-to-nas.bat` 作为薄封装入口,把默认参数与可选覆盖参数转交给现有 `deploy.ps1`。BAT 只负责入口体验,不承载核心部署逻辑。
**Tech Stack:** Windows CMD, PowerShell, pytest
---
### Task 1: 新增 BAT 入口脚本
**Files:**
- Create: `deploy/deploy-to-nas.bat`
- Test: `tests/deploy/test_deploy_to_nas.py`
**Step 1: 写失败测试**
```python
def test_bat_dry_run_uses_default_nas_target():
...
```
**Step 2: 运行测试确认失败**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_bat_dry_run_uses_default_nas_target -q`
Expected: 失败,因为 BAT 文件不存在
**Step 3: 最小实现**
- 新建 BAT 文件
- 默认调用 `deploy.ps1`
- 支持 `--dry-run`
**Step 4: 运行测试**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_bat_dry_run_uses_default_nas_target -q`
Expected: 通过
### Task 2: 支持参数覆盖
**Files:**
- Modify: `deploy/deploy-to-nas.bat`
- Test: `tests/deploy/test_deploy_to_nas.py`
**Step 1: 写失败测试**
```python
def test_bat_dry_run_accepts_override_arguments():
...
```
**Step 2: 运行测试确认失败**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_bat_dry_run_accepts_override_arguments -q`
Expected: 失败,因为 BAT 未透传覆盖参数
**Step 3: 最小实现**
- 按位置参数传递 host、port、username、password、remoteRoot
-`--dry-run` 透传给 PowerShell
**Step 4: 全量测试**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py -q`
Expected: 通过