首页 / 互联网开发 / 正文

Node 服务从本地到上线:pm2 守护与 Nginx 反代全记录

本地跑得好好的服务,一上服务器就“水土不服”?SSH 断了服务就没了nginx 502怎么没有 HTTPS 小锁——这条部署链路里的每个坑我都踩过。这篇按从零到线上的顺序,给出一份可以直接照抄的 Node 服务部署记录。

一、部署架构先画清楚

用户请求
   │
   ▼
Nginx :80/:443        ← 对外入口:域名、HTTPS、静态资源
   │ 反向代理
   ▼
pm2 守护的 Node 服务    ← 127.0.0.1:3000(不直接暴露公网)
   │
   ▼
数据库 / Redis         ← 内网或受保护访问
  • 浏览器只访问 Nginx,Node 监听在本地端口,由 Nginx 转发;
  • 这样静态资源(js/css/img)由 Nginx 直接返回,性能更好;HTTPS、Gzip、限流都在 Nginx 层做;
  • Node 进程由 pm2 守护:崩溃自动拉起、开机自启、日志统一管理。

二、服务器基础准备

# 1. 建议用非 root 用户操作,SSH 用密钥登录并禁用密码
adduser deploy
usermod -aG sudo deploy
# 本机生成密钥:ssh-keygen -t ed25519,把公钥写入 ~/.ssh/authorized_keys

# 2. 安装 Node(推荐 nvm,避免版本混乱)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 20            # LTS 版本

# 3. 安装 pm2(全局)
npm i -g pm2
生产环境尽量用 Node LTS 版本,不要追新;同一台机器有多个项目时 nvm + .nvmrc 能锁定每个项目的版本,避免“我本地能跑”的版本差异问题。

三、代码上线与 pm2 守护

# 1. 拉取代码并安装依赖(用 pnpm/npm 按项目来)
cd /www/wwwroot/my-app
git pull
pnpm install --frozen-lockfile
pnpm build                    # 编译出 dist/

# 2. 用 ecosystem 文件启动,配置可提交进仓库
# ecosystem.config.js
module.exports = {
  apps: [{
    name: 'my-app',
    script: 'dist/main.js',
    instances: 1,                    // 多核再考虑 cluster 模式
    autorestart: true,
    max_memory_restart: '500M',      // 内存超过自动重启,防泄漏拖垮机器
    env: { NODE_ENV: 'production', PORT: 3000 },
    out_file: './logs/pm2-out.log',
    error_file: './logs/pm2-error.log',
    merge_logs: true,
  }],
};

# 3. 启动与常用操作
pm2 start ecosystem.config.js
pm2 reload my-app        # 平滑重启(推荐发布用 reload 而非 restart)
pm2 status               # 看进程状态
pm2 logs my-app          # 看日志
pm2 save && pm2 startup  # 保存进程列表 + 生成开机自启
reload 和 restart 的区别:restart 直接杀掉重启,会中断在途请求;reload 是零停机地滚动重启(cluster 模式逐个重启,单实例时也会先起新进程再交接)。发布用 reload,体验好一个量级。

四、Nginx 反向代理配置

# /etc/nginx/sites-available/my-app.conf
server {
    listen 80;
    server_name example.com www.example.com;

    # 前端静态资源(如 SPA 构建产物)直接交给 Nginx
    root /www/wwwroot/my-app/dist;   # 如果有独立前端

    location / {
        proxy_pass http://127.0.0.1:3000;      # 代理到 Node
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # WebSocket 支持(如聊天/实时通知)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 60s;
    }
}

# 启用并检查配置
ln -s /etc/nginx/sites-available/my-app.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
  • X-Forwarded-For / X-Forwarded-Proto 必须转发:Node 里取真实 IP、判断 HTTPS 都靠它,否则拿到的一律是 127.0.0.1;
  • 改完配置先 nginx -t 校验语法,别 reload 一个坏配置把整个站点带崩。

五、HTTPS:免费证书让站点带上小锁

# Let's Encrypt + certbot,免费且自动续期
apt install certbot python3-certbot-nginx
certbot --nginx -d example.com -d www.example.com

# certbot 会自动改 Nginx 配置并设置续期任务,可手动验证续期
certbot renew --dry-run
  • 证书有效期 90 天,自动续期务必配好(certbot 的 systemd timer / cron);
  • HTTP 请求 301 跳 HTTPS,用 certbot 插件能一键完成;
  • 定期盯一下续期是否成功,证书过期当天整站报错是常见事故。

六、平滑发布与回滚

# 发布 = 三步
cd /www/wwwroot/my-app
git pull                      # 1. 更新代码
pnpm install && pnpm build    # 2. 安装 + 构建
pm2 reload my-app             # 3. 平滑重启

# 新版本有问题?回滚也一样快
git checkout <上一个发布tag>
pnpm build
pm2 reload my-app
给每次发布打 tag(如 v1.2.0),配合 release 分支,回滚就是 git checkout v1.1.0 再 reload——比“从回收站找回 dist”靠谱一万倍。敏感配置(数据库密码、密钥)走 .env + 环境变量,绝不提交进仓库

七、上线后的健康检查清单

  • 进程pm2 status 全 online,重启次数(restarts)不持续上涨;
  • 日志:out/error 日志无持续报错,配 pm2-logrotate 防止日志撑爆磁盘;
  • 网络:本地 curl -I https://example.com 返回 200 与正确响应头;
  • 安全:防火墙只开放 80/443(与 SSH),数据库端口不对公网开放;
  • 监控:服务器厂商的基础监控 + 进程内存告警,出事能第一时间知道。

这套链路跑通一次之后,往后每次发布就是三行命令的事。部署稳定了,就可以把精力放到性能与体验上了——那也是另一个话题的起点。