Skip to content

部署拓扑 ​

一个 NestJS 应用 + 一个 React 前端 + 一个 VitePress 文档站,背后是 MySQL、MongoDB、Redis。这一页说明生产环境如何组装、如何发布、如何排障。


进程拓扑 ​

                        Internet
                           │
                           ▼
                  ┌─────────────────┐
                  │  Nginx :80/443  │
                  └────────┬────────┘
        ┌──────────────────┼──────────────────┐
        │                  │                  │
        ▼                  ▼                  ▼
   /api/*            /socket.io/*         /docs/*
        │                  │                  │
        ▼                  ▼                  ▼
  NestJS :3000      NestJS :3000      /var/www/hushwork-docs
  (PM2 cluster)   (Redis adapter)
        │
        ├── MySQL
        ├── MongoDB(副本集 rs0)
        └── Redis
组件端口说明
Nginx80 / 443TLS 终止、反代、静态资源
hushwork API3000PM2 单实例(fork),重启约 1 秒
前端(开发)5173Vite dev server
前端(生产)—构建产物由 Nginx 托管
文档站—构建产物发布到 /var/www/hushwork-docs
MySQL3306仅内网
MongoDB27017副本集,仅内网
Redis6379仅内网

PM2 配置 ​

ecosystem.config.js:

js
module.exports = {
  apps: [{
    name: "hushwork",
    script: "dist/apps/hushwork/main.js",
    instances: 1,
    exec_mode: "fork",
    env_file: ".env",
    output: "./logs/out.log",
    error: "./logs/error.log",
    kill_timeout: 5000
  }]
};

关键点:

  • 单实例(fork):重启秒级完成,不存在新旧版本同时服务
  • 不要随手打开 wait_ready:应用没有调用 process.send('ready'),PM2 会白等满 listen_timeout;再配合多实例滚动重启,就会出现"一次 401、一次 404"的新旧版本混跑。要做多实例零停机,必须同时在 main.ts 补 ready 信号
  • 多实例前提:Socket.IO 需使用 RedisIoAdapter,广播才能跨实例
  • 日志:logs/out.log / logs/error.log

常用命令:

bash
pm2 start ecosystem.config.js
pm2 restart hushwork     # 单实例:秒级完成
pm2 logs hushwork
pm2 status

Nginx 路由 ​

以 hushwork.ai 为例:

路径去向
/api/127.0.0.1:3000
/api/api-docs/Swagger
/socket.io/127.0.0.1:3000(WebSocket upgrade)
/docs//var/www/hushwork-docs/(VitePress)
/admin/管理端静态资源
/前端(开发模式代理 5173;生产模式静态目录)

文档站点的关键配置:

nginx
location = /docs { return 301 /docs/; }

location /docs/ {
    alias /var/www/hushwork-docs/;
    index index.html;
    try_files $uri $uri/ $uri.html =404;
    error_page 404 /docs/404.html;
}

$uri.html 回退是 cleanUrls: true 生效的前提。


构建与发布 ​

后端 ​

bash
npm install
npm run build
pm2 reload hushwork

前端 ​

bash
npm run frontend:build
# 产物发布到 Nginx 托管目录

文档 ​

bash
npm run docs:api       # 若 API 有变更:导出 swagger + 生成 API 文档
npm run docs:build     # VitePress 构建
npm run docs:deploy    # rsync 到 /var/www/hushwork-docs

docs:build 只写入 site/.vitepress/dist,线上不会自动更新;必须执行 docs:deploy。


环境变量 ​

核心变量(见 .env):

变量作用默认
NODE_ENV运行环境development
APP_PORT应用端口3000
APP_ROUTE_PREFIX路由前缀api
MySQL 连接主机 / 端口 / 库 / 用户 / 密码—
MongoDB 连接URI—
Redis 连接主机 / 端口 / 密码—
LLMAPI Key / Base URL / 模型—
COS对象存储配置—
支付商户号 / 密钥 / 证书—

配置读取:apps/hushwork/src/config/configuration.ts 与 config.*.ts。


数据库要求 ​

MongoDB 必须是副本集 ​

跨层事务依赖 MongoDB 事务,因此必须运行副本集:

javascript
// mongod.conf
replication:
  replSetName: rs0
security:
  keyFile: /etc/mongo/keyfile
  authorization: enabled

初始化:

bash
mongosh --eval 'rs.initiate()'

检查:

bash
node scripts/mongo-promote-replset.js
# 期望输出 rs0 / PRIMARY

详细步骤见 docs/ops/mongo-replset-upgrade-runbook.md。


健康检查 ​

检查命令 / 地址
应用存活GET /api/a2a/agent-card → 200
SwaggerGET /api/api-docs/
MongoDBnode scripts/mongo-promote-replset.js
状态层一致性npx jest ... --testPathPatterns="state-layer"
整条业务npm run case:reference -- --commit

发布检查清单 ​

  • [ ] npm run build 通过
  • [ ] 单测与关键 e2e 通过
  • [ ] 数据库迁移已执行
  • [ ] 环境变量已更新
  • [ ] pm2 reload hushwork 成功
  • [ ] /api/a2a/agent-card 返回 200
  • [ ] 日志无新增 error
  • [ ] 如 API 变更:npm run docs:api && npm run docs:build && npm run docs:deploy

常见故障 ​

症状可能原因排查
502 Bad Gateway应用未启动 / 端口不对pm2 status、ss -ltnp | grep 3000
所有 API 404APP_ROUTE_PREFIX 不一致检查环境变量与 Nginx 路径
事务报错MongoDB 不是副本集rs.status()
WebSocket 连不上Nginx 缺少 Upgrade 头检查 proxy_set_header Connection
文档 404未执行 docs:deploy / 权限不足curl -I /docs/、检查 alias 与目录权限
文档旧页面只 build 未 deploy重新 npm run docs:deploy

下一步 ​

Released under the MIT License.