Files
YelayMaths/deploy/README.md
T
2026-09-30 11:02:58 +08:00

198 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 一键部署脚本
在本机运行一条命令,自动完成「打包源码 → 上传服务器 → 构建 → 重启进程」。
服务器上**不需要安装任何东西**,它复用的就是你已经能用的 SSH。
## 用哪个脚本
| 你的系统 | 脚本 | 配置文件 |
|---|---|---|
| Windows | `deploy.ps1` | `deploy.config.ps1`(照抄 `deploy.config.example.ps1`) |
| macOS / Linux | `deploy.sh` | `deploy.config.sh`(照抄 `deploy.config.example.sh`) |
Mac 上先执行一次 `chmod +x deploy/deploy.sh`,之后用法和 Windows 版完全一样:
```bash
./deploy/deploy.sh api
./deploy/deploy.sh admin
./deploy/deploy.sh
./deploy/deploy.sh api --dry-run # 只打包、只显示会上传哪些文件,不真传
./deploy/deploy.sh api --with-install # 顺带装依赖(只有改了 package.json 才需要)
```
> 第一次用先跑 `--dry-run`,它会打印打包大小和文件清单,核对无误再去掉这个参数。
---
## 一次性配置
### 1. 填配置文件
编辑 `deploy/deploy.config.ps1`(Mac 上是 `deploy/deploy.config.sh`,字段名多一层下划线,含义完全一样),需要改这几项:
| 配置项 | 填什么 | 怎么查 |
|---|---|---|
| `Host` | 服务器 IP 或域名 | — |
| `Port` | SSH 端口 | 宝塔 → 安全 → SSH 端口 |
| `User` | SSH 登录用户 | 一般是 `root` |
| `NodeBin` | Node 的 bin 目录 | 宝塔 → 软件商店 → Node 版本管理器 |
| `Api.RemotePath` | 服务端在服务器上的绝对路径 | 你部署时放的位置 |
| `Admin.RemotePath` | 管理端在服务器上的绝对路径 | 同上 |
| `Api.RestartCmd` | 重启服务端的命令 | 服务器上执行 `pm2 list` 看进程名 |
| `Admin.RestartCmd` | 重启管理端的命令 | 同上 |
| `Api.StartCmd` / `Admin.StartCmd` | 杀掉旧进程后把服务拉起来的命令 | 照抄模板;项目路径或端口变了才需要改 |
| `Api.HealthUrl` / `Admin.HealthUrl` | 启动后的探活地址 | 照抄模板;端口变了才需要改 |
| `NginxCacheDir` | Nginx 反向代理缓存目录 | 宝塔 → 软件商店 → Nginx → 配置修改,搜 `proxy_cache_path` |
| `NginxBin` | Nginx 可执行文件路径 | 一般是 `/www/server/nginx/sbin/nginx` |
### 2. 配置免密登录(强烈建议)
不配的话,每次部署要输两次密码。配好之后全程无提示。
```powershell
# 本机生成密钥(已生成过可跳过,直接第二次命令)
ssh-keygen -t ed25519
# 把公钥传到服务器
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh root@你的服务器 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
```
### 3. 确认服务器上的 .env 已就位
脚本**不会上传** `.env`(这是故意的,避免覆盖服务器上的密钥配置)。
- 服务端:`yelaymaths_api/.env` 必须已在服务器上配好
- 管理端:`yelaymaths_admin/.env.local` 必须已在服务器上配好
- 注意 `NEXT_PUBLIC_*` 是**构建时**写死的,所以第一次构建前它就得在
---
## 日常使用
```powershell
.\deploy\deploy.ps1 api # 只部署服务端
.\deploy\deploy.ps1 admin # 只部署管理端
.\deploy\deploy.ps1 # 两个都部署
```
输出长这样:
```
=== yelaymaths_api (server API) ===
[1/5] Building locally: pnpm prisma:generate && pnpm build
[2/5] Packing yelaymaths_api
100.8 KB (src, prisma, tools, package.json, ...)
[3/5] Uploading to /www/wwwroot/yelaymaths/yelaymaths_api/api
[4/5] Running on server
__DEPLOY_DONE__
[5/5] Restarted
Started, health check passed: http://127.0.0.1:3002/healthz
Nginx proxy cache cleared and reloaded
All done in 12.0s
```
### 其它参数
| 参数 | 作用 |
|---|---|
| `-DryRun` | 只打包、只显示会上传哪些文件,**不真的传**。第一次用建议先跑这个 |
| `-WithInstall` | 额外安装依赖。**只在改了 `package.json` 时才需要** |
---
## 为什么默认不装依赖
服务器上的全局 pnpm 是 **8.x**,而本机是 10.x,两代锁文件格式不兼容——服务器直接读不了我们的 `pnpm-lock.yaml`。
而服务器上**不能升级 pnpm**(其他服务也在用)。
好在只有「装依赖」这一步需要版本匹配,**「构建」不需要**——`pnpm build` 只是执行 `package.json` 里的脚本,pnpm 8 也能跑。
所以日常部署走「上传源码 + 构建」,用服务器上现成的 `node_modules`,既不碰全局 pnpm,也快得多。
只有 `package.json` 变了才需要装依赖,这时用 `-WithInstall`,它会通过 `npx` 临时拉一个 10.27.0 的 pnpm 来跑,同样不影响全局。
---
## 上传哪些文件
脚本用的是**白名单**,只有列在 `DeployPaths` 里的路径会被打包。所以这些东西**结构上不可能**被传上去:
- `.env` / `.env.local`(服务器上的配置不会被覆盖)
- `node_modules/`(依赖在服务器上装)
- `dist/`、`.next/`(构建产物在服务器上生成)
服务端实际约 100 KB,管理端约 25 KB。
---
## 已知限制
**删除的文件不会同步删除。** 脚本是「覆盖式上传」,如果你在本地删掉某个源码文件,服务器上那份还在。
一般不影响运行(多余的文件不会被执行),但如果你发现改了名字的旧文件仍在作怪,手动上服务器删掉即可。
需要「完全同步」的场景(比如删文件、想要版本回滚),改用 git 推送部署更合适——那是另一个方案,需要时再说。
---
## 部署结束后会清一次 Nginx 缓存
宝塔在 `/www/server/nginx/conf/proxy.conf` 里对**所有**反向代理站点开了 `proxy_cache cache_one`,而 Next.js 给页面发的响应头是 `Cache-Control: s-maxage=31536000`——翻译过来就是「这一页允许缓存一年」。
两边一叠加,后果很隐蔽:新构建传上去、进程也重启了,用户访问到的还是缓存里的旧 HTML;而旧 HTML 引用的分包早被新构建删掉,请求直接 404,页面渲染到一半就断,表现成「某个区块整块消失」,看起来像代码写错了,实际跟代码毫无关系。
所以脚本在每次部署的最后会多做一步:
```bash
find /www/server/nginx/proxy_cache_dir -mindepth 1 -delete # 清空缓存文件
/www/server/nginx/sbin/nginx -s reload # 重载 Nginx
```
服务端和管理端两条部署线都带这一步。缓存只影响响应速度,清空后第一次访问会回源到新构建,不会丢数据。
把 `NginxCacheDir` 留空(`''`)就会跳过这一步。
---
## 部署结束会把服务自动拉起来
杀掉旧进程不等于新进程会跑起来。之前这里是「只杀不拉」,所以每次部署完都要去宝塔面板点一下「启动」,忘了点线上就是 502。
现在每个应用多两项配置:
- `RestartCmd`:杀掉旧进程
- `StartCmd`:把新进程拉起来(`setsid` + `nohup` + 重定向,SSH 会话断了也不受影响)
- `HealthUrl`:拉起后探活,30 秒内没响应就让部署直接判失败
所以正常输出结尾会是:
```
[5/5] Restarted
Started, health check passed: http://127.0.0.1:3001/
Nginx proxy cache cleared and reloaded
```
进程日志写在服务器上:`/www/wwwlogs/yelaymaths-api.out.log`、`/www/wwwlogs/yelaymaths-admin.out.log`。
有一点要知道:进程是脚本拉起来的,不是宝塔面板拉起来的,所以**面板里那个项目的状态可能显示成「未启动」**,但服务本身是正常在跑的(脚本每次都会探活确认)。如果想让面板重新接管,在面板点一次「重启」即可,之后照常用面板管。
只有 `RestartCmd` 不想自己启动(比如服务由 PM2 或 systemd 托管),把 `StartCmd` 留空(`''`)就会退回「只杀不拉」的老行为。
---
## 出问题怎么排查
| 现象 | 原因 | 处理 |
|---|---|---|
| `deploy.config.ps1 not found` | 没建配置文件 | 复制 `deploy.config.example.ps1` |
| 一直提示输密码 | 没配免密登录 | 见上面第 2 步 |
| `pnpm: command not found` | `NodeBin` 填错 | 在服务器上执行 `which pnpm` 看真实路径 |
| `pm2: command not found` | PM2 不在 Node 的 bin 目录 | 服务器上执行 `which pm2`,把目录也加到 `NodeBin` |
| 构建成功但页面没变 | 进程没重启,或 Nginx 缓存没清 | 检查 `RestartCmd`、`NginxCacheDir` 填对没有 |
| 页面某个区块整块消失 | 缓存里的旧 HTML 引用了已 404 的旧分包 | 手动清一次 `NginxCacheDir` 并 reload |
| 部署报 `service did not come up` | 新进程没起来 | 看 `/www/wwwlogs/yelaymaths-*.out.log` 的报错,多半是端口被占或依赖没装 |
| `Permission denied` | 目标目录权限不对 | 服务器上 `chown -R root:root 目标目录` |