Next.js 集群部署与 Nginx 负载均衡生产手册
Next.js 集群部署与 Nginx 负载均衡生产手册
一、Next.js 单机 PM2 集群部署(垂直扩容)
1.1 前置:standalone 生产构建
Next.js 默认构建产物包含完整 node_modules,体积大、不利于多机分发,生产集群强制开启独立打包模式。
配置方式
// package.json
{
"scripts": {
"build": "next build",
"start": "node .next/standalone/server.js"
}
}
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone'
}
module.exports = nextConfig
构建产物说明
-
执行
npm run build后,生成.next/standalone目录 -
目录内包含最小化运行时依赖 +
server.js入口文件,无需全局安装 node_modules 即可运行 -
需手动将
.next/static拷贝至standalone/.next/static,public目录拷贝至standalone/public1.2 PM2 集群模式完整配置
利用 Node.js cluster 模式,充分利用 CPU 多核,解决单进程只能使用单核的问题。
ecosystem.config.js完整配置module.exports = { apps: [{ name: "next-ssr", // 启动入口,指向 standalone 产物 script: ".next/standalone/server.js", cwd: "./",
// 集群模式核心配置
exec_mode: "cluster",
// 最优值:自动匹配 CPU 核心数,多开无收益且会增加上下文切换开销
instances: "max",
// 环境变量
env_production: {
NODE_ENV: "production",
PORT: 3000,
// 监听所有网卡,允许内网 Nginx 访问
HOSTNAME: "0.0.0.0"
},
// 进程守护与重启策略
watch: false,
// 内存超过 1.5G 自动重启,防止 OOM
max_memory_restart: "1500M",
// 异常自动重启,最大重启间隔 3 秒
autorestart: true,
min_uptime: "10s",
restart_delay: 3000,
// 日志配置
log_date_format: "YYYY-MM-DD HH:mm:ss",
error_file: "./logs/next-error.log",
out_file: "./logs/next-out.log",
merge_logs: true
}]
}
### 1.3 常用运维命令
```bash
# 启动生产集群
pm2 start ecosystem.config.js --env production
# 零停机热更新(代码更新后执行,不中断请求)
pm2 reload next-ssr
# 查看集群状态、CPU/内存占用
pm2 status
pm2 monit
# 查看日志
pm2 logs next-ssr --lines 200
# 停止/删除服务
pm2 stop next-ssr
pm2 delete next-ssr
# 设置开机自启
pm2 save
pm2 startup
1.4 验证集群生效
# 查看 Node 进程数,应等于 CPU 核心数
ps aux | grep server.js | grep -v grep | wc -l
# 验证端口监听
netstat -tlnp | grep 3000
二、多机水平集群 + Nginx 负载均衡(核心)
2.1 标准架构与角色分工
用户公网请求 → 域名 → Nginx负载均衡机(公网IP) → 内网转发 → 多台Next业务机(仅内网IP)
-
Nginx 节点:1台,独立机器,仅负责流量入口、SSL卸载、限流、分流、静态缓存
-
Next 业务节点:N台,只运行 Next + PM2,不对外暴露公网端口,仅内网互通
-
通信原则:Nginx 到 Next 全部使用内网 IP,不占用公网带宽,延迟更低、安全性更高
2.2 upstream 后端集群完整配置
upstream块定义后端节点池,是负载均衡的核心配置。http { # 后端 Next 服务池 upstream next_cluster { # 会话保持:同一客户端 IP 始终分配到同一台后端 # 解决登录态、WebSocket 跨机丢失问题 ip_hash; # 后端节点格式:内网IP:端口 + 参数 # weight:权重,数值越大分配流量越多,配置相同则均分 server 172.16.0.10:3000 weight=1 max_fails=2 fail_timeout=30s; server 172.16.0.11:3000 weight=1 max_fails=2 fail_timeout=3000;
备用节点:所有主节点故障时自动启用
server 172.16.0.12:3000 backup;
下线标记:维护时标记为 down,流量不再分配
server 172.16.0.13:3000 down;
}
}
**核心参数详解**
| 参数 | 作用 |
|------|------|
| `ip_hash` | 基于客户端 IP 哈希分配节点,保证会话一致性;缺点是同一出口IP下大量用户会集中到单节点 |
| `weight` | 权重,硬件配置不同的机器可按比例分配流量 |
| `max_fails` | 允许请求失败的次数,超过后标记节点不可用 |
| `fail_timeout` | 节点被标记不可用后,多久后重新尝试探测 |
| `backup` | 备用节点,所有主节点挂掉才启用 |
| `down` | 手动标记节点下线,用于平滑维护 |
### 2.3 Nginx 代理完整配置
```nginx
http {
# 限流规则:单 IP 每秒 20 次请求
# zone:共享内存区,10M 可存储约 16 万 IP 状态
limit_req_zone $binary_remote_addr zone=ssr_limit:10m rate=20r/s;
# gzip 压缩,减少传输体积,降低带宽占用
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss;
server {
listen 80;
server_name your-domain.com;
# 限流生效:突发允许额外 5 个请求,nodelay 表示不排队直接处理
limit_req zone=ssr_limit burst=5 nodelay;
# 动静分离:静态资源直接返回,不转发到 Next
location /_next/static/ {
alias /path/to/your/static/.next/static/;
# 缓存 30 天,静态资源带哈希值,可强缓存
expires 30d;
add_header Cache-Control "public, immutable";
}
location /public/ {
alias /path/to/your/public/;
expires 7d;
}
# SSR 页面与接口请求转发到集群
location / {
proxy_pass http://next_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_connect_timeout 10s; # 与后端建立连接超时
proxy_send_timeout 30s; # 向后端发送请求超时
proxy_read_timeout 30s; # 读取后端响应超时
# 禁用后端缓冲,提升实时性
proxy_buffering off;
}
}
}
2.4 限流配置深度说明
-
rate=20r/s:每秒最多 20 个请求,超过则进入队列或直接拒绝 -
burst=5:允许瞬时突发 5 个请求,配合nodelay直接处理,不排队 -
若需更宽松的限流,可调整为
rate=50r/s burst=10 nodelay -
针对爬虫/恶意IP,可配合
deny指令直接封禁2.5 配置生效与校验
# 校验配置语法是否正确 nginx -t # 平滑重载配置,不中断现有连接 nginx -s reload # 查看 Nginx 连接状态 netstat -an | grep :80 | wc -l
三、WebSocket 长连接专项配置
3.1 链路原理
WebSocket 基于 HTTP 协议握手升级,握手成功后转为长连接,连接生命周期内固定绑定同一台后端节点,不会重新负载分配。
3.2 完整配置模板
在原有 location / 中补充以下配置,无需拆分路径:
location / {
proxy_pass http://next_cluster;
proxy_http_version 1.1;
# ===== WebSocket 协议升级核心配置 =====
# 透传客户端 Upgrade 头,告知后端升级协议
proxy_set_header Upgrade $http_upgrade;
# 固定为 upgrade,表示连接升级
proxy_set_header Connection "upgrade";
# ===== 长连接超时配置 =====
# 后端 1 小时无数据发送则断开连接,避免死连接占用资源
proxy_read_timeout 3600s;
# 客户端 1 小时无数据发送则断开
proxy_send_timeout 3600s;
# 常规透传配置
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
3.3 会话一致性保障
-
强制开启
ip_hash若不开启,用户网络波动重连时可能分配到其他后端节点,内存中的 socket 连接状态丢失,表现为频繁掉线、重新登录。
-
ip_hash局限同一公司内网、同一运营商出口的大量用户共享同一个公网IP,会集中分配到单台节点,导致负载不均。
高并发场景替代方案:使用 Nginx
sticky_cookie模块,基于 cookie 绑定节点,粒度更细。3.4 多机消息互通方案
多节点部署下,A 节点的用户无法直接给 B 节点的用户发送消息,必须引入中间件做跨机广播。
标准方案:Redis 发布订阅
-
所有 Next 实例启动时订阅同一个 Redis Channel(如
ws:message) -
任意节点收到客户端消息,先写入业务逻辑,再 publish 到 Redis Channel
-
所有节点收到 Channel 消息后,遍历本机在线连接,推送给目标用户
-
在线用户状态统一存入 Redis,支持跨机查询用户在线状态
四、集群日常运维操作
4.1 新增节点步骤
-
环境准备:新机器安装同版本 Node.js、PM2
-
代码部署:同步 standalone 构建产物,PM2 启动服务,验证
内网IP:3000可正常访问 -
更新 Nginx 配置:在
upstream块中新增一行server 新内网IP:3000; -
平滑生效:执行
nginx -t && nginx -s reload -
验证流量:查看新节点访问日志,确认有流量进入
4.2 下线节点步骤
-
标记下线:在节点后添加
down参数,nginx -s reload,停止新流量分配 -
等待排空:等待现有连接处理完成(建议等待 1-2 分钟)
-
停止服务:目标机器执行
pm2 stop next-ssr -
移除配置:从 upstream 中删除该行,再次重载 Nginx
4.3 常用排查命令
# 查看后端节点健康状态 nginx -T | grep upstream # 查看 Nginx 实时连接数 watch -n 1 'netstat -an | grep :80 | wc -l' # 查看 Nginx 错误日志,定位 502/504 原因 tail -f /var/log/nginx/error.log # 查看单台 Next 节点负载 pm2 monit
五、高可用进阶方案
5.1 方案选型对比
| 方案 | 运维成本 | 自动故障切换 | 适用场景 |
| -------------------- | ---- | ------ | ----------------- |
| 单台 Nginx | 极低 | 否 | 测试环境、中小站点、可接受短暂故障 |
| 云厂商 SLB/CLB | 极低 | 是 | 云上项目,生产首选 |
| Keepalived + 双 Nginx | 高 | 是 | 自建机房、无云负载均衡 |
| DNS 多 A 记录 | 低 | 否 | 简易容灾,故障仍有部分用户不可用 |
5.2 方案一:云厂商负载均衡(推荐)
阿里云 SLB、腾讯云 CLB、华为云 ELB 为托管服务,无需自建 Nginx 机器。
-
控制台创建负载均衡实例,分配公网 IP
-
后端服务器组添加所有 Next 业务机内网 IP + 3000 端口
-
配置监听规则:80/443 端口,开启会话保持、健康检查
-
域名解析到负载均衡公网 IP 即可
优势:天然多机高可用、自带健康检查、SSL 证书托管、带宽监控,无需维护入口机器。
5.3 方案二:Keepalived 双 Nginx 高可用
机器规划
-
主节点:
172.16.0.2(公网110.x.x.2) -
备节点:
172.16.0.3(公网110.x.x.3) -
虚拟 VIP:
110.x.x.100(域名只解析此 IP)主节点 Keepalived 配置
global_defs { router_id nginx_master } # Nginx 存活检测脚本 vrrp_script check_nginx { script "/etc/keepalived/check_nginx.sh" interval 2 weight -20 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 123456 } virtual_ipaddress { 110.x.x.100/24 dev eth0 } track_script { check_nginx } }检测脚本
/etc/keepalived/check_nginx.sh#!/bin/bash if ! pgrep nginx > /dev/null; then exit 1 fi exit 0备节点配置:仅修改两处
state BACKUP priority 90生效操作
# 两台机器均执行 chmod +x /etc/keepalived/check_nginx.sh systemctl start keepalived systemctl enable keepalived主节点 Nginx 故障或机器宕机时,备节点自动接管 VIP,用户无感知。
六、常见问题排障
6.1 登录态跨机丢失
-
原因:未开启
ip_hash,请求分发到不同节点,session 存在本机内存 -
解决:开启
ip_hash;根治方案是 session 存入 Redis,实现全节点共享6.2 大量 502 Bad Gateway
-
常见原因:后端节点 3000 端口未监听、防火墙拦截内网端口、Next 服务崩溃
-
排查:在 Nginx 机器上
curl 内网IP:3000验证连通性;查看 Next 错误日志是否有报错6.3 WebSocket 频繁断开
-
原因1:
proxy_read_timeout太短,默认 60s 无数据自动断开 -
解决:调大超时时间,或前端加心跳包(30s 一次)保活
-
原因2:未开启
ip_hash,重连后节点切换 -
解决:开启
ip_hash或 sticky cookie6.4 后端节点负载不均
-
原因:
ip_hash导致大出口IP用户集中;或 weight 配置不一致 -
解决:流量均匀场景可关闭
ip_hash改用轮询;或调整 weight 权重6.5 静态资源 404
-
原因:standalone 构建后未手动拷贝 static、public 目录
-
解决:部署时将
.next/static放入 standalone 对应目录,或由 Nginx 直接托管静态资源
本文由萧兮的博客原创发布,欢迎转载,转载务必保留原文链接。
萧兮的博客:https://www.20010515.xyz · 原文:https://www.20010515.xyz/posts/019ef2a9-44f4-7a23-b514-50c2ed0b33bc