Files
YelayMaths/deploy/README.md
T

8.8 KiB
Raw Permalink Blame History

一键部署脚本

在本机运行一条命令,自动完成「本地构建(可选)→ 打包 → 上传服务器 → 重启进程」。

服务器上不需要安装任何东西,它复用的就是你已经能用的 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 版完全一样:

./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. 配置免密登录(强烈建议)

不配的话,每次部署要输两次密码。配好之后全程无提示。

# 本机生成密钥(已生成过可跳过,直接第二次命令)
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_* 是构建时写死的,所以第一次构建前它就得在

日常使用

.\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
      160 KB  (dist, prisma, package.json, pnpm-lock.yaml, pnpm-workspace.yaml)
[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/
  • 白名单之外的源码目录

Windows 版配置(deploy.config.ps1)已经在本机构建、只传产物:服务端传 dist + prisma(约 160 KB), 管理端传 .next + next.config.ts(约 540 KB,73 MB 的构建缓存已排除),服务器不再执行编译,部署完自动重启并探活。


已知限制

删除的文件不会同步删除。 脚本是「覆盖式上传」,如果你在本地删掉某个源码文件,服务器上那份还在。

一般不影响运行(多余的文件不会被执行),但如果你发现改了名字的旧文件仍在作怪,手动上服务器删掉即可。

需要「完全同步」的场景(比如删文件、想要版本回滚),改用 git 推送部署更合适——那是另一个方案,需要时再说。


部署结束后会清一次 Nginx 缓存

宝塔在 /www/server/nginx/conf/proxy.conf 里对所有反向代理站点开了 proxy_cache cache_one,而 Next.js 给页面发的响应头是 Cache-Control: s-maxage=31536000——翻译过来就是「这一页允许缓存一年」。

两边一叠加,后果很隐蔽:新构建传上去、进程也重启了,用户访问到的还是缓存里的旧 HTML;而旧 HTML 引用的分包早被新构建删掉,请求直接 404,页面渲染到一半就断,表现成「某个区块整块消失」,看起来像代码写错了,实际跟代码毫无关系。

所以脚本在每次部署的最后会多做一步:

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 目标目录