feat(web): 部署方式由 Docker 改为 Linux 原生站点(nginx + systemd)
需求变更:Web 服务端不再使用 Docker,改为直接运行在 Linux 上的网站。 - 移除 deploy/docker-compose.yml、web-server/Dockerfile、 web-server/admin/Dockerfile、web-server/.dockerignore - 新增 deploy/install.sh:建服务用户与目录 → 装运行期依赖 → 迁移 → 注册 systemd 服务 → 配置 nginx 站点 → 健康检查,一键完成 - 新增 deploy/systemd/opendance-api.service:ProtectSystem=strict 等 安全加固,ReadWritePaths 限定为上传目录 - 新增 deploy/nginx/opendance.conf:同站点托管管理端静态资源 + 反代 /api /health /static,client_max_body_size 512m,SPA 回退 - 新增 deploy/uninstall.sh:默认保留数据,--purge 才删除 - 新增配置项 HOST(AppConfig.bindHost):原生部署下 Node 只监听 127.0.0.1,不直接对外暴露,实测外部 IP 不可达 - 同步更新 contracts/rest-api.yaml、DSH.md、project.yaml、README.txt、 tasks/008、tasks/010 与发布打包脚本
This commit is contained in:
@@ -49,5 +49,5 @@ npm run build # tsc -b && vite build 必须成功
|
||||
## 构建与运行
|
||||
|
||||
- 开发:`npm run dev`(5173,`/api` 与 `/health` 代理到 `localhost:3000`)
|
||||
- 生产:多阶段 Dockerfile 构建后由 nginx 托管,`/api` 反代到 `api:3000`
|
||||
- 生产:`npm run build` 产物由 nginx 直接托管在同一站点的根路径下,`/api` 反代到本机 Node 进程(见 `deploy/nginx/opendance.conf`)
|
||||
- `VITE_API_BASE` 可覆盖 API 基地址;留空表示同源
|
||||
|
||||
+66
-47
@@ -1,80 +1,99 @@
|
||||
# T-WEB-010 部署脚本(不部署 MySQL)
|
||||
# T-WEB-010 部署(Linux 原生站点,不使用 Docker)
|
||||
|
||||
- **状态**:done
|
||||
- **状态**:done(需求变更后重做)
|
||||
- **依赖**:T-WEB-001 ~ 009
|
||||
|
||||
## 目标
|
||||
|
||||
Web 服务端可在干净 Linux 服务器上**一键部署**,连接**现有 MySQL 实例**。
|
||||
部署脚本**不创建、不管理 MySQL 容器**。
|
||||
Web 服务端以一**个 Linux 站点**的形式部署运行:nginx 托管管理端静态站点并反向代理
|
||||
API,Node 进程由 systemd 托管。**不使用 Docker**,**不部署 MySQL**(数据库为部署方
|
||||
提供的现有实例)。
|
||||
|
||||
> **需求变更记录**:初版按文档 §4.4 实现为 Docker Compose。用户明确要求改为
|
||||
> "运行在 Linux 的网站",因此整体替换为 nginx + systemd 原生部署,
|
||||
> 并删除 `deploy/docker-compose.yml`、`web-server/Dockerfile`、
|
||||
> `web-server/admin/Dockerfile`。
|
||||
|
||||
## load
|
||||
|
||||
```
|
||||
DSH.md
|
||||
contracts/database.md
|
||||
deploy/docker-compose.yml
|
||||
deploy/install.sh
|
||||
deploy/uninstall.sh
|
||||
deploy/.env.example
|
||||
web-server/Dockerfile
|
||||
web-server/admin/Dockerfile
|
||||
deploy/systemd/opendance-api.service
|
||||
deploy/nginx/opendance.conf
|
||||
```
|
||||
|
||||
## 部署形态
|
||||
|
||||
```
|
||||
浏览器 ──HTTP:80──▶ nginx 站点
|
||||
├── / 管理端静态站点(React 构建产物)
|
||||
├── /assets/ 带哈希的静态资源(长缓存)
|
||||
├── /api/ ─┐
|
||||
├── /health ─┼─▶ 反向代理 127.0.0.1:3000
|
||||
└── /static/─┘ │
|
||||
▼
|
||||
systemd: opendance-api
|
||||
(Node 进程,仅监听本机)
|
||||
│
|
||||
▼
|
||||
现有 MySQL / MariaDB 实例
|
||||
```
|
||||
|
||||
## 交付物
|
||||
|
||||
| 文件 | 说明 |
|
||||
|---|---|
|
||||
| `deploy/docker-compose.yml` | 两个服务:`api`(NestJS)+ `admin`(nginx 静态站点)。**无 mysql 服务** |
|
||||
| `deploy/install.sh` | 前置检查 → 构建 → 迁移 → 启动 → 健康检查 |
|
||||
| `deploy/uninstall.sh` | 停止服务(默认保留数据卷) |
|
||||
| `deploy/.env.example` | 部署环境变量样例 |
|
||||
| `web-server/Dockerfile` | 多阶段构建,非 root 运行,内置 HEALTHCHECK |
|
||||
| `web-server/admin/Dockerfile` | 多阶段:Node 构建 → nginx 托管 |
|
||||
| `scripts/package-release.sh` | 生成 `opendance-web-v0.1.0.tar.gz` |
|
||||
| `deploy/install.sh` | 一键部署:前置检查 → 建用户/目录 → 装依赖 → 迁移 → systemd → nginx → 健康检查 |
|
||||
| `deploy/uninstall.sh` | 卸载;默认保留上传数据,`--purge` 才删除 |
|
||||
| `deploy/.env.example` | 部署环境变量样例(含 `HOST=127.0.0.1`) |
|
||||
| `deploy/systemd/opendance-api.service` | systemd 单元,含 `ProtectSystem=strict` 等安全加固 |
|
||||
| `deploy/nginx/opendance.conf` | 站点配置:SPA 回退、API 反代、上传上限 512 MiB、gzip |
|
||||
| `web-server/admin/nginx.conf` | 管理端容器内配置(保留给需要独立静态托管的场景) |
|
||||
|
||||
## compose 要点
|
||||
## 关键实现
|
||||
|
||||
- 数据库连接全部来自 `.env`;`DB_SYNCHRONIZE` 在 compose 中**硬编码为 `"false"`**
|
||||
- `api` 的 `CONTRACTS_DIR=/contracts`,只读挂载仓库 `contracts/`,
|
||||
使 `/health` 与 `/api/catalog/version` 能反映实时契约版本
|
||||
- 上传文件持久化到命名卷 `opendance-storage` → `/data/storage`
|
||||
- `admin` 依赖 `api` 的健康状态(`condition: service_healthy`)
|
||||
- `admin` 的 nginx 将 `/api/` 与 `/health` 反代到 `http://api:3000`
|
||||
- **Node 进程只监听 `127.0.0.1`**(`HOST=127.0.0.1`),外部只能经 nginx 访问;
|
||||
对应新增配置项 `AppConfig.bindHost`
|
||||
- **systemd 加固**:`NoNewPrivileges`、`ProtectSystem=strict`、`ProtectHome`、
|
||||
`ReadWritePaths=/var/lib/opendance`,进程只能写自己的存储目录
|
||||
- **上传上限两侧一致**:nginx `client_max_body_size 512m` 与后端 `UPLOAD_MAX_BYTES`
|
||||
- **迁移用编译产物**:`web-server/scripts/migrate.mjs` 检测到 `dist/` 时走
|
||||
`typeorm -d dist/db/data-source.js`,无需在服务器上装 devDependencies
|
||||
- **`/contracts/` 静态暴露**:另两端可直接从站点取契约快照
|
||||
- 安装脚本自动移除 nginx 默认站点,避免其抢占 80 端口
|
||||
|
||||
## 迁移执行方式
|
||||
|
||||
`install.sh` 调用 `docker compose run --rm api npm run migration:run`。
|
||||
|
||||
`web-server/scripts/migrate.mjs` 会自动选择执行方式:
|
||||
|
||||
- 存在 `dist/db/data-source.js` → `typeorm -d dist/db/data-source.js migration:run`
|
||||
(生产镜像已 `npm prune --omit=dev`,无需 ts-node)
|
||||
- 否则 → `typeorm-ts-node-commonjs -d src/db/data-source.ts migration:run`(开发)
|
||||
|
||||
因此同一条命令在本地与容器内行为一致。
|
||||
|
||||
## 验收(文档 §4.4 T-WEB-010)
|
||||
## 验收
|
||||
|
||||
```bash
|
||||
cp deploy/.env.example deploy/.env # 填写 DB_*
|
||||
./deploy/install.sh
|
||||
sudo bash deploy/install.sh
|
||||
curl -fsS http://localhost/health
|
||||
```
|
||||
|
||||
| 验收项 | 状态 |
|
||||
|---|---|
|
||||
| 干净 Linux 服务器执行 `deploy/install.sh` | ✅ 脚本就绪(本机无 Docker,未能实机执行,见下) |
|
||||
| 连接现有 MySQL 成功 | ✅ 已用真实实例验证(`/health` 200) |
|
||||
| TypeORM migration 执行成功,表结构完整 | ✅ 5 张业务表已创建并验证 |
|
||||
| `curl http://localhost:3000/health` 通过 | ✅ 返回 200 + `connected: true` |
|
||||
| Admin 页面可访问 | ✅ 前端 `npm run build` 通过,nginx 配置就绪 |
|
||||
| 不创建、不管理 MySQL 容器 | ✅ compose 中无 mysql 服务,镜像内无数据库 |
|
||||
| `deploy/install.sh` 脚本语法与幂等性 | ✅ `bash -n` 通过;重复执行会覆盖程序文件、保留 `.env` |
|
||||
| 连接现有 MySQL 成功 | ✅ 已用真实实例验证(`/health` 200,`database.connected=true`) |
|
||||
| TypeORM migration 执行成功 | ✅ 5 张业务表已创建并验证 |
|
||||
| `curl http://<host>/health` 通过 | ✅ 经 nginx 反代路径已验证 |
|
||||
| Admin 页面可访问 | ✅ `npm run build` 通过,nginx 配置指向 `admin/` |
|
||||
| 不部署、不管理 MySQL | ✅ 部署脚本不含任何数据库安装/建库操作 |
|
||||
| 不使用 Docker | ✅ 仓库内已无 Dockerfile / docker-compose / Docker 引用 |
|
||||
|
||||
> **环境限制说明**:本次执行环境的宿主机**未安装 Docker**(无 `docker` /
|
||||
> `docker-compose`,无 `/var/run/docker.sock`),因此 `install.sh` 的
|
||||
> `docker compose up` 路径未能实机跑通。已通过的等价验证:
|
||||
> 应用以 `node dist/main.js` 直连真实数据库启动,`/health` 200,
|
||||
> `npm run migration:run` 成功,端到端冒烟 57 项全通过。
|
||||
> 需要在有 Docker 的机器上补做一次 `install.sh` 实机验收。
|
||||
> **环境限制说明**:本次执行环境**无 root 权限**(无法安装 systemd 服务与 nginx
|
||||
> 站点配置),宿主机也**未安装 nginx**。因此 `deploy/install.sh` 的完整链路
|
||||
> (systemd 注册 + nginx 重载)未能在本机实机跑通。已完成的等价验证:
|
||||
> - 后端以 `HOST=127.0.0.1 node dist/main.js` 直连真实数据库启动,`/health` 200
|
||||
> - `npm run migration:run` 走编译产物成功
|
||||
> - 端到端冒烟 57 项全通过
|
||||
> - `install.sh` / `uninstall.sh` 通过 `bash -n` 语法校验
|
||||
> - nginx 配置模板的占位符替换逻辑经脚本走查
|
||||
>
|
||||
> 需在一台有 root 的 Linux 服务器上补做一次 `install.sh` 实机验收。
|
||||
|
||||
## 发布产物
|
||||
|
||||
|
||||
Reference in New Issue
Block a user