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 Node | 3 节点集群,无中心化设计,任意节点可提供完整服务 |
| 负载均衡 | Nginx / SLB 统一入口,将请求分发到后端各 Nacos 节点 |
| MySQL | 集中式元数据存储,所有 Nacos 节点共享同一数据库 |
| 协议端口 | 8848(HTTP)、9848(gRPC客户端)、9849(gRPC服务端,用于Raft) |
1.3 关键端口说明
| 端口 | 用途 | 偏移公式 |
|---|
| 8848 | HTTP 管理端 / 客户端 API | 主端口 |
| 9848 | gRPC 客户端通信 | 主端口 + 1000 |
| 9849 | gRPC Raft 节点间通信 | 主端口 + 1001 |
注意:Nacos 2.x 起,节点间 Raft 通信默认使用 (server.port + 1001) 端口,集群间必须互通。
二、集群完整配置
2.1 环境要求
| 项目 | 要求 |
|---|
| JDK | 1.8+ (推荐 JDK 17) |
| 内存 | JVM 堆内存 ≥ 2G (生产建议 4G) |
| 磁盘 | ≥ 10G 可用空间 |
| MySQL | 5.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.1 或 localhost。
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_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
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_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_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
NODES=("10.1.1.11" "10.1.1.12" "10.1.1.13")
ALERT_THRESHOLD=2
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 日志管理
/usr/local/nacos/logs/
├── nacos.log
├── naming-server.log
├── config-server.log
├── raft.log
├── jraft.log
├── access_log.log
├── alipay-jraft.log
└── startup.log
日志清理策略:
find /usr/local/nacos/logs/ -name "*.log" -mtime +7 -exec rm -f {} \;
4.3 配置备份与恢复
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs/export" -o nacos_backup_$(date +%Y%m%d).zip
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 节点启动失败
tail -200 /usr/local/nacos/logs/startup.log
netstat -tlnp | grep -E "8848|9848|9849"
telnet 10.1.1.20 3306
mysql -h 10.1.1.20 -u nacos -p -e "SELECT 1"
cat /usr/local/nacos/conf/cluster.conf
free -h
4.4.2 集群脑裂
| 症状 | 处理方式 |
|---|
| 部分节点间互相不可见 | 检查网络连通性和防火墙规则(端口 9848/9849) |
| 多节点自认为 Leader | 先全部停止 → 确认 MySQL 正常 → 逐个启动 |
4.4.3 配置无法生效
grep "config change" /usr/local/nacos/logs/nacos.log
curl http://node:8848/nacos/v1/cs/configs/listener
4.4.4 常见问题速查
| 问题 | 原因 | 解决 |
|---|
Connect refused | Nacos 未启动 | 执行 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:
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% |
| JVM | Full GC 频率 | > 5 次/小时 |
| 数据库 | 连接池活跃数 | > 80% |
| 数据库 | 慢查询数 | > 10/min |
附录:快速部署 Checklist