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/staticpublic 目录拷贝至 standalone/public

    1.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 会话一致性保障

  1. 强制开启 ip_hash

    若不开启,用户网络波动重连时可能分配到其他后端节点,内存中的 socket 连接状态丢失,表现为频繁掉线、重新登录。

  2. ip_hash 局限

    同一公司内网、同一运营商出口的大量用户共享同一个公网IP,会集中分配到单台节点,导致负载不均。

    高并发场景替代方案:使用 Nginx sticky_cookie 模块,基于 cookie 绑定节点,粒度更细。

    3.4 多机消息互通方案

    多节点部署下,A 节点的用户无法直接给 B 节点的用户发送消息,必须引入中间件做跨机广播。

    标准方案:Redis 发布订阅

  3. 所有 Next 实例启动时订阅同一个 Redis Channel(如 ws:message

  4. 任意节点收到客户端消息,先写入业务逻辑,再 publish 到 Redis Channel

  5. 所有节点收到 Channel 消息后,遍历本机在线连接,推送给目标用户

  6. 在线用户状态统一存入 Redis,支持跨机查询用户在线状态


四、集群日常运维操作

4.1 新增节点步骤

  1. 环境准备:新机器安装同版本 Node.js、PM2

  2. 代码部署:同步 standalone 构建产物,PM2 启动服务,验证 内网IP:3000 可正常访问

  3. 更新 Nginx 配置:在 upstream 块中新增一行 server 新内网IP:3000;

  4. 平滑生效:执行 nginx -t && nginx -s reload

  5. 验证流量:查看新节点访问日志,确认有流量进入

    4.2 下线节点步骤

  6. 标记下线:在节点后添加 down 参数,nginx -s reload,停止新流量分配

  7. 等待排空:等待现有连接处理完成(建议等待 1-2 分钟)

  8. 停止服务:目标机器执行 pm2 stop next-ssr

  9. 移除配置:从 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 机器。

  1. 控制台创建负载均衡实例,分配公网 IP

  2. 后端服务器组添加所有 Next 业务机内网 IP + 3000 端口

  3. 配置监听规则:80/443 端口,开启会话保持、健康检查

  4. 域名解析到负载均衡公网 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 cookie

    6.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