Node.js生产环境完整部署指南:PM2集群、Nginx代理与健康监控实战
Node.js单进程架构无法利用多核CPU,且进程崩溃后无法自动恢复。PM2(Process Manager 2)解决了这两个问题:集群模式利用所有CPU核心,守护进程在崩溃后自动重启。本文给出完整的生产部署方案。
一、生产环境准备
<code"># 安装Node.js LTS版本(通过nvm管理版本) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install --lts nvm use --lts nvm alias default node node --version # v20.x.x npm --version # 全局安装PM2 npm install -g pm2 # 验证PM2 pm2 --version
二、PM2 ecosystem.config.js 完整配置
<code">// ecosystem.config.js
module.exports = {
apps: [
{
// ========== 主API应用 ==========
name: 'api-server',
script: './src/server.js',
// 集群模式:充分利用所有CPU核心
instances: 'max', // 或指定数字:4
exec_mode: 'cluster',
// 自动重启配置
autorestart: true,
watch: false, // 生产环境不监听文件变化
max_restarts: 10, // 最多重启10次(防止故障循环)
restart_delay: 3000, // 重启前等待3秒
// 内存超限自动重启
max_memory_restart: '500M',
// 环境变量(生产环境)
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
// 环境变量(开发环境)
env_development: {
NODE_ENV: 'development',
PORT: 3001,
DEBUG: '*',
},
// 日志配置
error_file: '/var/log/pm2/api-error.log',
out_file: '/var/log/pm2/api-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true, // 合并所有实例的日志
// 优雅关闭(接收SIGINT后等待请求处理完毕)
kill_timeout: 5000, // 最多等待5秒
listen_timeout: 8000, // 等待8秒让应用完成启动
// 健康检查
wait_ready: true, // 等待process.send('ready')信号
},
{
// ========== Worker进程 ==========
name: 'queue-worker',
script: './src/worker.js',
instances: 2,
exec_mode: 'fork', // Worker不需要集群模式
autorestart: true,
max_memory_restart: '300M',
env_production: {
NODE_ENV: 'production',
WORKER_CONCURRENCY: 5,
},
error_file: '/var/log/pm2/worker-error.log',
out_file: '/var/log/pm2/worker-out.log',
},
],
};
三、应用代码:支持优雅关闭
<code">// src/server.js
const express = require('express');
const app = express();
// 健康检查端点(用于负载均衡和监控)
app.get('/health', (req, res) => {
res.json({
status: 'ok',
uptime: process.uptime(),
memory: process.memoryUsage(),
timestamp: new Date().toISOString(),
});
});
// 就绪检查端点(K8s readinessProbe)
app.get('/ready', async (req, res) => {
try {
// 检查数据库连接
await db.ping();
// 检查Redis连接
await redis.ping();
res.json({ status: 'ready' });
} catch (err) {
res.status(503).json({ status: 'not ready', error: err.message });
}
});
// 你的业务路由
app.use('/api', require('./routes'));
const server = app.listen(process.env.PORT || 3000, () => {
console.log(`API服务器运行在端口 ${process.env.PORT || 3000}`);
// 通知PM2应用已就绪(配合wait_ready: true)
if (process.send) {
process.send('ready');
}
});
// ============ 优雅关闭 ============
const gracefulShutdown = (signal) => {
console.log(`收到 ${signal} 信号,开始优雅关闭...`);
server.close(async () => {
console.log('HTTP服务器已关闭');
// 关闭数据库连接
await db.end();
// 关闭Redis连接
await redis.quit();
console.log('所有连接已关闭,进程退出');
process.exit(0);
});
// 强制关闭超时(PM2 kill_timeout设置的时间)
setTimeout(() => {
console.error('优雅关闭超时,强制退出');
process.exit(1);
}, 4000);
};
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));四、PM2常用管理命令
<code"># 启动(生产环境) pm2 start ecosystem.config.js --env production # 零停机重载(集群模式下,逐个重启实例) pm2 reload api-server # 零停机重启(等待ready信号) pm2 reload ecosystem.config.js --update-env # 查看所有进程状态 pm2 list # 实时监控(CPU/内存/请求数) pm2 monit # 查看日志 pm2 logs api-server pm2 logs api-server --lines 200 # 清除日志 pm2 flush api-server # 保存当前进程列表(重启服务器后自动恢复) pm2 save pm2 startup # 生成开机自启命令,按提示执行 # 删除进程 pm2 delete api-server # 查看详细信息 pm2 show api-server
五、Nginx反向代理配置
<code">upstream api_cluster {
least_conn;
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
keepalive 64; # 保持长连接池,提升性能
}
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
# 安全响应头
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://api_cluster;
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;
proxy_set_header Connection ""; # 启用keep-alive
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
# 请求缓冲(Node.js不需要,让Nginx快速转发)
proxy_buffering off;
proxy_request_buffering off;
}
# 健康检查端点不记录日志(减少日志噪声)
location = /health {
proxy_pass http://api_cluster;
access_log off;
}
}六、PM2日志管理
<code"># 安装PM2日志轮转模块 pm2 install pm2-logrotate # 配置(保留7天日志,每个文件最大50MB) pm2 set pm2-logrotate:max_size 50M pm2 set pm2-logrotate:retain 7 pm2 set pm2-logrotate:compress true pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss pm2 set pm2-logrotate:rotateInterval '0 0 * * *' # 每天轮转
七、性能调优
<code"># Node.js内存调优(针对V8引擎)
# 在package.json的scripts中:
{
"scripts": {
"start": "node --max-old-space-size=512 src/server.js"
}
}
# 或在ecosystem.config.js中:
{
node_args: '--max-old-space-size=512'
}
# 开启libuv线程池(CPU密集型任务,如加密)
# 默认4个线程,CPU核心数多时可以增加
{
env: {
UV_THREADPOOL_SIZE: 8
}
}八、总结
PM2集群模式 + 优雅关闭 + Nginx反向代理,是Node.js生产部署的标准架构。集群模式让4核服务器的Node.js吞吐量提升约4倍,零停机重载让代码更新不影响在线用户。IDC.Net的香港VPS4核8G配置配合PM2集群,是运行中高流量Node.js API服务的理想选择。
版权声明:
作者:后浪云
链接:https://idc.net/help/442843/
文章版权归作者所有,未经允许请勿转载。
THE END
