Nacos 集群部署方案

0 阅读7分钟

Nacos 集群部署方案


一、集群标准部署架构

1.1 架构图

                              ┌─────────────────┐
                                  Nginx / SLB   
                                 (VIP: 10.x.x.x)│
                                 Port: 8848     
                              └────────┬─────────┘
                                       
              ┌────────────────────────┼────────────────────────┐
                                                              
    ┌─────────▼─────────┐   ┌─────────▼─────────┐   ┌─────────▼─────────┐
       Nacos Node 1          Nacos Node 2          Nacos Node 3    
       IP: 10.1.1.11         IP: 10.1.1.12         IP: 10.1.1.13   
       Port: 8848            Port: 8848            Port: 8848      
       gRPC: 9848            gRPC: 9848            gRPC: 9848      
       gRPC srv: 9849        gRPC srv: 9849        gRPC srv: 9849  
    └────────┬──────────┘   └────────┬──────────┘   └────────┬──────────┘
                                                            
             └───────────────────────┼────────────────────────┘
                                     
                        ┌────────────▼────────────┐
                                  MySQL          
                          (主从 / MGR 集群)       
                          IP: 10.1.1.20:3306     
                          DB: nacos_config       
                        └─────────────────────────┘

1.2 架构说明

组件说明
Nacos Node3 节点集群,无中心化设计,任意节点可提供完整服务
负载均衡Nginx / SLB 统一入口,将请求分发到后端各 Nacos 节点
MySQL集中式元数据存储,所有 Nacos 节点共享同一数据库
协议端口8848(HTTP)、9848(gRPC客户端)、9849(gRPC服务端,用于Raft)

1.3 关键端口说明

端口用途偏移公式
8848HTTP 管理端 / 客户端 API主端口
9848gRPC 客户端通信主端口 + 1000
9849gRPC Raft 节点间通信主端口 + 1001

注意:Nacos 2.x 起,节点间 Raft 通信默认使用 (server.port + 1001) 端口,集群间必须互通。


二、集群完整配置

2.1 环境要求

项目要求
JDK1.8+ (推荐 JDK 17)
内存JVM 堆内存 ≥ 2G (生产建议 4G)
磁盘≥ 10G 可用空间
MySQL5.7+ / 8.0

2.2 Nacos 下载与目录

# 下载
wget https://github.com/alibaba/nacos/releases/download/2.4.3/nacos-server-2.4.3.tar.gz

# 解压
tar -zxvf nacos-server-2.4.3.tar.gz -C /usr/local/
cd /usr/local/nacos

2.3 MySQL 初始化

-- 创建数据库
CREATE DATABASE IF NOT EXISTS nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 授权
GRANT ALL PRIVILEGES ON nacos_config.* TO 'nacos'@'%' IDENTIFIED BY 'Nacos@123456';
FLUSH PRIVILEGES;

执行 Nacos 自带 SQL 脚本(在 conf/mysql-schema.sql):

mysql -h 10.1.1.20 -u nacos -p nacos_config < /usr/local/nacos/conf/mysql-schema.sql

2.4 conf/application.properties(三节点通用配置)

# ============ 集群模式 ============
spring.sql.init.platform=mysql

# ============ 节点数量(用于选举多数派)============
nacos.naming.election.majority=true
nacos.naming.quorum.majority=2

# ============ MySQL 数据源 ============
db.num=1
db.url.0=jdbc:mysql://10.1.1.20:3306/nacos_config?characterEncoding=utf8&connectTimeout=3000&socketTimeout=6000&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
db.user.0=nacos
db.password.0=Nacos@123456
db.pool.config.driver-class-name=com.mysql.cj.jdbc.Driver

# ============ 连接池 ============
db.pool.config.connectionTimeout=5000
db.pool.config.maximumPoolSize=20
db.pool.config.minimumIdle=5

2.5 conf/cluster.conf(集群节点列表)

每台节点使用相同的 cluster.conf,内容为各节点 IP:PORT:

10.1.1.11:8848
10.1.1.12:8848
10.1.1.13:8848

注意:IP 必须配置本机实际网络 IP,不要写 127.0.0.1localhost

2.6 JVM 参数优化 conf/nacos-jvm.env(或 bin/startup.sh 中调整)

# 生产环境建议
JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -Xmn2g"
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/nacos/logs/heapdump.hprof"
JAVA_OPT="${JAVA_OPT} -Dnacos.home=/usr/local/nacos"
JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote"
JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote.port=9999"
JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote.authenticate=false"
JAVA_OPT="${JAVA_OPT} -Dcom.sun.management.jmxremote.ssl=false"

2.7 Nginx 负载均衡配置

upstream nacos_cluster {
    server 10.1.1.11:8848 weight=1 max_fails=2 fail_timeout=10s;
    server 10.1.1.12:8848 weight=1 max_fails=2 fail_timeout=10s;
    server 10.1.1.13:8848 weight=1 max_fails=2 fail_timeout=10s;
}

# HTTP 长连接配置(gRPC 必须)
upstream nacos_grpc {
    server 10.1.1.11:9848 weight=1;
    server 10.1.1.12:9848 weight=1;
    server 10.1.1.13:9848 weight=1;
}

server {
    listen 8848;
    server_name nacos.example.com;

    # 日志
    access_log /var/log/nginx/nacos_access.log;
    error_log  /var/log/nginx/nacos_error.log;

    # 代理 HTTP API
    location / {
        proxy_pass http://nacos_cluster;
        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;
    }
}

# gRPC 端口(四层代理)
stream {
    upstream nacos_grpc_stream {
        server 10.1.1.11:9848;
        server 10.1.1.12:9848;
        server 10.1.1.13:9848;
    }
    server {
        listen 9848;
        proxy_pass nacos_grpc_stream;
        proxy_timeout 60s;
    }
}

三、启停脚本

3.1 Nacos 启动脚本 start_nacos.sh

#!/bin/bash
# ============================================
# Nacos 集群启动脚本
# ============================================

NACOS_HOME=/usr/local/nacos
NACOS_USER=nacos
JAVA_HOME=/usr/local/jdk17
LOG_DIR=${NACOS_HOME}/logs
STARTUP_LOG=${LOG_DIR}/startup.log

# 环境检查
check_env() {
    if [ ! -d "${JAVA_HOME}" ]; then
        echo "[ERROR] JAVA_HOME ${JAVA_HOME} not found"
        exit 1
    fi
    if [ ! -f "${NACOS_HOME}/bin/startup.sh" ]; then
        echo "[ERROR] startup.sh not found in ${NACOS_HOME}/bin/"
        exit 1
    fi
    if [ ! -f "${NACOS_HOME}/conf/cluster.conf" ]; then
        echo "[ERROR] cluster.conf not found"
        exit 1
    fi
}

# 检查是否已运行
check_running() {
    PIDS=$(ps -ef | grep "nacos.nacos" | grep -v grep | awk '{print $2}')
    if [ -n "${PIDS}" ]; then
        echo "[WARN] Nacos is already running. PIDS: ${PIDS}"
        return 1
    fi
    return 0
}

# 启动
start_nacos() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting Nacos ..."
    cd ${NACOS_HOME}/bin
    # 单机模式: startup.sh -m standalone
    # 集群模式: startup.sh (默认)
    bash startup.sh >> ${STARTUP_LOG} 2>&1 &

    # 等待启动完成
    echo "Waiting for Nacos to start ..."
    for i in {1..60}; do
        sleep 2
        if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8848/nacos/v1/console/health/readiness | grep -q "200"; then
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] Nacos started successfully!"
            return 0
        fi
        echo -n "."
    done
    echo ""
    echo "[ERROR] Nacos start timeout, please check log: ${STARTUP_LOG}"
    return 1
}

# 主流程
main() {
    check_env
    if check_running; then
        start_nacos
    fi
}

main

3.2 Nacos 停止脚本 stop_nacos.sh

#!/bin/bash
# ============================================
# Nacos 集群停止脚本
# ============================================

NACOS_HOME=/usr/local/nacos
SHUTDOWN_API="http://127.0.0.1:8848/nacos/v1/console/server/shutdown"
TIMEOUT=30

# 优雅下线
graceful_shutdown() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Graceful shutdown via API ..."
    curl -X POST "${SHUTDOWN_API}" 2>/dev/null
    return $?
}

# 强制停止
force_kill() {
    echo "[WARN] Force killing Nacos ..."
    PIDS=$(ps -ef | grep "nacos.nacos" | grep -v grep | awk '{print $2}')
    for pid in ${PIDS}; do
        echo "Killing PID: ${pid}"
        kill -9 ${pid}
    done
}

# 主流程
main() {
    # 先尝试优雅下线
    if ! graceful_shutdown; then
        force_kill
        exit 0
    fi

    # 等待进程退出
    echo "Waiting for Nacos to stop ..."
    for i in $(seq 1 ${TIMEOUT}); do
        sleep 1
        PCOUNT=$(ps -ef | grep "nacos.nacos" | grep -v grep | wc -l)
        if [ ${PCOUNT} -eq 0 ]; then
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] Nacos stopped gracefully."
            exit 0
        fi
    done

    # 超时则强制停止
    force_kill
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Nacos stopped."
}

main

3.3 Nacos 重启脚本 restart_nacos.sh

#!/bin/bash
# ============================================
# Nacos 集群重启脚本
# ============================================

NACOS_HOME=/usr/local/nacos
SCRIPT_DIR=$(cd $(dirname $0) && pwd)

echo "=========================================="
echo "  Nacos Cluster Restart"
echo "  Time: $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="

echo "[STEP 1/2] Stopping Nacos ..."
bash ${SCRIPT_DIR}/stop_nacos.sh

# 确保完全停止
sleep 5
PIDS=$(ps -ef | grep "nacos.nacos" | grep -v grep | awk '{print $2}')
if [ -n "${PIDS}" ]; then
    echo "[ERROR] Failed to stop Nacos completely. PIDs: ${PIDS}"
    exit 1
fi

echo "[STEP 2/2] Starting Nacos ..."
bash ${SCRIPT_DIR}/start_nacos.sh

if [ $? -eq 0 ]; then
    echo "=========================================="
    echo "  Nacos restarted successfully!"
    echo "=========================================="
else
    echo "[ERROR] Nacos start failed after restart!"
    exit 1
fi

3.4 集群滚动重启脚本 rolling_restart.sh

#!/bin/bash
# ============================================
# 集群滚动重启(逐台重启,保证服务不中断)
# ============================================

NODES=("10.1.1.11" "10.1.1.12" "10.1.1.13")
SSH_USER="root"
REMOTE_SCRIPT_DIR="/usr/local/nacos/scripts"

for node in "${NODES[@]}"; do
    echo "=========================================="
    echo "  Restarting Nacos on ${node} ..."
    echo "=========================================="

    ssh ${SSH_USER}@${node} "bash ${REMOTE_SCRIPT_DIR}/stop_nacos.sh"
    sleep 10

    # 确认停止
    if ssh ${SSH_USER}@${node} "ps -ef | grep nacos.nacos | grep -v grep | wc -l"; then
        echo "[WARN] ${node} may still be running, force kill ..."
        ssh ${SSH_USER}@${node} "pkill -9 -f nacos.nacos"
        sleep 3
    fi

    ssh ${SSH_USER}@${node} "bash ${REMOTE_SCRIPT_DIR}/start_nacos.sh"

    # 等待节点就绪
    echo "Waiting for ${node} to be ready ..."
    for i in {1..30}; do
        sleep 2
        if curl -s -o /dev/null -w "%{http_code}" http://${node}:8848/nacos/v1/console/health/readiness | grep -q "200"; then
            echo "${node} is ready!"
            break
        fi
        echo -n "."
    done
    echo ""

    # 等待集群重新同步
    echo "Waiting for cluster sync (30s) ..."
    sleep 30
done

echo "=========================================="
echo "  Rolling restart completed!"
echo "=========================================="

3.5 集群状态检查脚本 check_cluster.sh

#!/bin/bash
# ============================================
# Nacos 集群状态检查
# ============================================

NODES=("10.1.1.11" "10.1.1.12" "10.1.1.13")
ALERT_THRESHOLD=2   # 少于 N 个存活节点时告警

alive_count=0

echo "=========================================="
echo "  Nacos Cluster Health Check"
echo "  Time: $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="

for node in "${NODES[@]}"; do
    status_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://${node}:8848/nacos/v1/console/health/readiness 2>/dev/null)

    if [ "${status_code}" = "200" ]; then
        echo "[OK]  ${node}:8848 - UP"
        alive_count=$((alive_count + 1))
    else
        echo "[DOWN] ${node}:8848 - DOWN (HTTP ${status_code})"
    fi
done

echo ""
echo "Alive nodes: ${alive_count}/${#NODES[@]}"

if [ ${alive_count} -lt ${ALERT_THRESHOLD} ]; then
    echo "[ALERT] Cluster is degraded! Only ${alive_count} nodes alive."
    exit 1
else
    echo "[OK] Cluster is healthy."
    exit 0
fi

四、运维注意事项

4.1 日常维护要点

检查项频率说明
集群节点健康检查每天运行 check_cluster.sh 确认 3 节点均在线
MySQL 连接检查每天确认 Nacos 到 MySQL 的连接数正常(<50)
磁盘空间每周日志目录 /usr/local/nacos/logs 清理,防止写满
配置备份每周导出 Nacos 配置做异地备份
证书有效期每月如启用了 TLS,检查证书到期时间
JVM 堆内存监控持续接入 Prometheus/Grafana 监控 JVM 指标

4.2 日志管理

# Nacos 日志目录结构
/usr/local/nacos/logs/
├── nacos.log           # 主日志
├── naming-server.log   # 命名服务日志
├── config-server.log   # 配置服务日志
├── raft.log            # Raft 协议日志
├── jraft.log           # JRaft 日志
├── access_log.log      # 访问日志
├── alipay-jraft.log    # JRaft 详细日志
└── startup.log         # 启动日志

日志清理策略

# 保留最近 7 天日志
find /usr/local/nacos/logs/ -name "*.log" -mtime +7 -exec rm -f {} \;

# 或配置 logback 滚动策略:conf/nacos-logback.xml 中调整 maxHistory

4.3 配置备份与恢复

# 备份(通过 API 导出)
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs/export" -o nacos_backup_$(date +%Y%m%d).zip

# 恢复(通过 API 导入)
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs/import" \
  -F "file=@nacos_backup_20250101.zip" \
  -F "policy=OVERWRITE"

4.4 故障排查

4.4.1 节点启动失败
# 1. 检查启动日志
tail -200 /usr/local/nacos/logs/startup.log

# 2. 常见原因排查
# - 端口占用
netstat -tlnp | grep -E "8848|9848|9849"

# - MySQL 连接不通
telnet 10.1.1.20 3306
mysql -h 10.1.1.20 -u nacos -p -e "SELECT 1"

# - cluster.conf 中 IP 配置错误
cat /usr/local/nacos/conf/cluster.conf

# - JVM 内存不足
free -h
4.4.2 集群脑裂
症状处理方式
部分节点间互相不可见检查网络连通性和防火墙规则(端口 9848/9849)
多节点自认为 Leader先全部停止 → 确认 MySQL 正常 → 逐个启动
4.4.3 配置无法生效
# 检查配置推送是否成功(查 Nacos 日志)
grep "config change" /usr/local/nacos/logs/nacos.log

# 确认客户端长轮询连接正常
curl http://node:8848/nacos/v1/cs/configs/listener
4.4.4 常见问题速查
问题原因解决
Connect refusedNacos 未启动执行 start_nacos.sh
Service Unavailable集群节点不足多数派恢复至少 2 个节点
DB connection pool full数据库连接数不够调大 maximumPoolSize
日志刷满磁盘未配置日志滚动配置 logback maxHistory

4.5 高可用要点

┌─────────────────────────────────────────────────────────────────┐
│                     高可用设计核心原则                              │
├─────────────────────────────────────────────────────────────────┤
│  1. 集群节点数 = 2n+1 (奇数),生产环境至少 3 节点                     │
│  2. 多数派 (quorum) = n+1,即 3 节点集群至少需 2 节点存活              │
│  3. MySQL 使用主从 / MGR / Galera 保证数据库高可用                    │
│  4. 负载均衡层(Nginx/SLB)自身也需要主备 HA                           │
│  5. 节点分布在不同的物理机 / 机架 / 可用区                              │
│  6. 客户端必须配置所有节点地址,不能只配 VIP                            │
└─────────────────────────────────────────────────────────────────┘
客户端连接最佳实践
// Spring Cloud 应用 - bootstrap.yml
spring:
  cloud:
    nacos:
      discovery:
        server-addr: nacos.example.com:8848   # VIP / 域名
      config:
        server-addr: nacos.example.com:8848

# 客户端也应配置节点列表用于直连兜底
# 通过 JVM 参数: -Dnacos.server-addr=10.1.1.11:8848,10.1.1.12:8848,10.1.1.13:8848
防火墙规则
# 集群内部互通 (所有节点之间)
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.1.1.0/24" port port="8848" protocol="tcp" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.1.1.0/24" port port="9848" protocol="tcp" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.1.1.0/24" port port="9849" protocol="tcp" accept'
firewall-cmd --reload

4.6 升级操作流程

升级前:
  1. 备份数据库  → mysqldump nacos_config > backup.sql
  2. 导出配置    → curl 导出配置 zip
  3. 通知业务方  → 发布升级公告

升级步骤:
  1. 从 Nacos 负载均衡摘除目标节点
  2. 停止 Nacos → 替换 jar 包 / 解压新版本
  3. 启动 Nacos → 验证节点恢复正常
  4. 观察 10 分钟 → 确认无异常
  5. 依次滚动升级其他节点
  6. 升级完成后整体验证

回滚方案:
  1. 恢复旧版本 jar 包
  2. 恢复数据库备份(如有 schema 变更)
  3. 按相同滚动方式回滚

4.7 监控指标建议

指标类别具体指标告警阈值
可用性节点存活数< 3 告警
可用性健康检查失败连续 3 次告警
性能HTTP 请求延迟 p99> 500ms
性能gRPC 连接数突降 > 30%
JVM堆内存使用率> 80%
JVMFull GC 频率> 5 次/小时
数据库连接池活跃数> 80%
数据库慢查询数> 10/min

附录:快速部署 Checklist

  • 3 台 Linux 服务器(JDK 17 已安装)
  • MySQL 实例已创建并初始化 nacos_config
  • application.properties 已配置 MySQL 连接
  • cluster.conf 已填写 3 节点 IP:8848
  • Nginx / SLB 已配置 8848 和 9848 代理
  • 防火墙已开放 8848、9848、9849 端口
  • 各节点 start_nacos.sh 启动成功
  • check_cluster.sh 确认 3 节点健康
  • Nacos 控制台 http://{VIP}:8848/nacos 可正常访问