Kubernetes环境下Nacos生产级部署与调优实践
1. 为什么需要专门的Nacos生产部署方案
在Kubernetes环境中部署Nacos集群与传统的物理机部署存在显著差异。我经历过从虚拟机迁移到Kubernetes的完整过程,发现至少有三个方面需要特别注意:
首先是网络拓扑的变化。Kubernetes的Pod IP是动态分配的,传统的基于固定IP的集群发现机制需要调整为Service域名发现。这直接影响到nacos-cluster.conf的配置方式,我曾因此导致整个集群无法形成法定人数(quorum)。
其次是存储的持久化问题。在默认配置下,Nacos的嵌入式Derby数据库会随着Pod的重启而丢失数据。生产环境必须配置外部MySQL集群,但官方文档对Kubernetes下的MySQL连接配置描述不够详细。
最后是资源管理的差异。物理机部署时我们可以根据机器配置估算资源需求,但在Kubernetes中需要精确设置requests/limits。特别是JVM内存参数与容器内存限制的关系,配置不当会导致频繁OOMKilled。
2. 生产级部署架构设计
2.1 高可用架构拓扑
经过多次生产验证,我推荐采用下图所示架构:
[Nginx Ingress] | [Service: nacos-headless] | [Pod: nacos-0] --- [Pod: nacos-1] --- [Pod: nacos-2] | | | [PVC: nacos-0] [PVC: nacos-1] [PVC: nacos-2] | | | [MySQL MGR集群] [Redis哨兵集群] [Prometheus+Alertmanager]关键设计要点:
- 使用Headless Service实现Pod间直接通信
- 每个Pod绑定独立PVC保证日志持久化
- MySQL采用MGR集群确保数据高可用
- 监控体系需要覆盖JVM、中间件和K8s指标
2.2 资源配置计算公式
对于生产环境,我总结出以下资源配置公式:
- CPU:
- 基础需求 = 核数 × 1.5 (安全系数)
- 例如3节点集群:3 × 2核 × 1.5 = 9核总量
- 内存:
- JVM堆内存 = (容器内存限制 - 1GB) × 0.7
- 例如设置4GB限制:-Xms2457m -Xmx2457m
- 存储:
- 日志存储 = 每日日志量 × 保留天数 × 副本数
- 通常配置50Gi/节点能满足两周需求
3. 详细部署步骤
3.1 前置条件准备
# 创建专用命名空间 kubectl create ns nacos-prod # 准备MySQL配置文件 cat <<EOF > mysql-config.cnf [mysqld] server-id = \${POD_INDEX} log-bin = mysql-bin binlog_format = ROW transaction_isolation = READ-COMMITTED EOF # 创建Secret存储数据库密码 kubectl create secret generic mysql-secret \ --from-literal=password='Nacos@123' \ -n nacos-prod3.2 StatefulSet核心配置
apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos namespace: nacos-prod spec: serviceName: nacos-headless replicas: 3 template: spec: containers: - name: nacos image: nacos/nacos-server:2.4.0 env: - name: MODE value: cluster - name: SPRING_DATASOURCE_PLATFORM value: mysql - name: MYSQL_SERVICE_HOST value: "mysql-mgr.nacos-prod.svc.cluster.local" - name: MYSQL_SERVICE_DB_NAME value: "nacos_config" - name: MYSQL_SERVICE_USER value: "nacos" - name: MYSQL_SERVICE_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: password ports: - containerPort: 8848 name: client - containerPort: 9848 name: raft readinessProbe: httpGet: path: /nacos/health port: 8848 initialDelaySeconds: 30 periodSeconds: 10 resources: limits: cpu: "2" memory: 4Gi requests: cpu: "1" memory: 2Gi volumeMounts: - name: config mountPath: /home/nacos/conf - name: logs mountPath: /home/nacos/logs volumeClaimTemplates: - metadata: name: logs spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "csi-rbd-sc" resources: requests: storage: 50Gi3.3 网络与服务配置
apiVersion: v1 kind: Service metadata: name: nacos-headless namespace: nacos-prod annotations: service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" spec: clusterIP: None ports: - name: client port: 8848 targetPort: client - name: raft port: 9848 targetPort: raft selector: app: nacos publishNotReadyAddresses: true --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nacos-ingress namespace: nacos-prod annotations: nginx.ingress.kubernetes.io/upstream-hash-by: "$remote_addr" spec: ingressClassName: nginx rules: - host: nacos.company.com http: paths: - path: / pathType: Prefix backend: service: name: nacos-headless port: number: 88484. 关键调优参数
4.1 JVM参数优化
在configmap中添加以下JVM参数:
JAVA_OPT="${JAVA_OPT} -server -Xms2g -Xmx2g" JAVA_OPT="${JAVA_OPT} -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m" JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:MaxGCPauseMillis=200" JAVA_OPT="${JAVA_OPT} -XX:+HeapDumpOnOutOfMemoryError" JAVA_OPT="${JAVA_OPT} -Djava.security.egd=file:/dev/./urandom"4.2 集群通信参数
# conf/cluster.conf nacos-0.nacos-headless.nacos-prod.svc.cluster.local:8848 nacos-1.nacos-headless.nacos-prod.svc.cluster.local:8848 nacos-2.nacos-headless.nacos-prod.svc.cluster.local:8848 # conf/application.properties nacos.core.protocol.raft.data.dir=/home/nacos/data/raft nacos.naming.distro.taskDispatchThreadCount=8 nacos.naming.distro.taskDispatchPeriod=1000 nacos.naming.distro.batchSyncKeyCount=10005. 生产验证与监控
5.1 健康检查方案
# 集群状态检查 curl -X GET "http://nacos.company.com/nacos/v1/ns/operator/health?format=json" # 节点健康检查 for pod in $(kubectl get pods -n nacos-prod -l app=nacos -o name); do kubectl exec -n nacos-prod $pod -- curl -s localhost:8848/nacos/health done # 数据库连接检查 kubectl exec -n nacos-prod nacos-0 -- \ mysql -hmysql-mgr -unacos -p$MYSQL_PWD -e "SHOW DATABASES;"5.2 Prometheus监控配置
- job_name: 'nacos' metrics_path: '/nacos/actuator/prometheus' kubernetes_sd_configs: - role: pod namespaces: names: ['nacos-prod'] relabel_configs: - source_labels: [__meta_kubernetes_pod_label_app] action: keep regex: nacos - source_labels: [__address__] action: replace regex: ([^:]+)(?::\d+)? replacement: ${1}:8848 target_label: __address__6. 故障处理实录
6.1 脑裂问题处理
当网络分区发生时,可能出现以下症状:
- 控制台显示节点健康但配置修改不同步
- 客户端获取的配置出现不一致
- 日志中出现"leader timeout"警告
解决方案:
- 检查raft日志确认leader状态
kubectl exec -n nacos-prod nacos-0 -- \ tail -100 /home/nacos/logs/nacos-raft.log - 强制重置集群状态(谨慎操作):
# 停止所有Pod kubectl scale sts nacos --replicas=0 -n nacos-prod # 清理raft数据 for i in 0 1 2; do kubectl exec -n nacos-prod nacos-$i -- \ rm -rf /home/nacos/data/raft/* done # 重启集群 kubectl scale sts nacos --replicas=3 -n nacos-prod
6.2 客户端连接优化
常见客户端问题及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 长轮询超时 | 客户端未使用集群模式 | 配置所有节点地址 |
| 配置更新延迟 | 客户端缓存时间过长 | 调整cacheMillis参数 |
| 频繁重连 | 网络抖动或DNS解析问题 | 启用客户端本地缓存 |
Java客户端推荐配置:
Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "nacos.company.com:8848"); properties.put(PropertyKeyConst.NAMESPACE, "prod-env"); // 启用故障转移模式 properties.put(PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG, "true"); // 设置本地缓存 properties.put(PropertyKeyConst.CONFIG_LONG_POLL_TIMEOUT, "30000"); properties.put(PropertyKeyConst.CONFIG_RETRY_TIME, "5000");7. 升级与维护策略
7.1 滚动升级方案
采用分批次升级策略确保服务不中断:
- 先升级一个从节点并验证
- 手动触发leader切换
curl -X PUT "http://nacos-0:8848/nacos/v1/ns/operator/leader" - 升级原leader节点
- 最后升级剩余从节点
7.2 数据迁移流程
从旧版本迁移的关键步骤:
- 使用官方工具导出配置
nacos-export.sh --export --dir /backup \ --host old.nacos.com --port 8848 - 验证备份数据完整性
- 在新集群执行导入
nacos-import.sh --import --dir /backup \ --host new.nacos.com --port 8848 - 保持双集群并行运行至少24小时
8. 安全加固措施
8.1 认证与鉴权配置
# application.properties nacos.core.auth.enabled=true nacos.core.auth.system.type=nacos nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzQXZlcnlTZWNyZXRLZXlEZjM= nacos.core.auth.server.identity.key=serverIdentity nacos.core.auth.server.identity.value=security # 创建管理员账号 kubectl exec -n nacos-prod nacos-0 -- \ curl -X POST "http://localhost:8848/nacos/v1/auth/users?username=admin&password=Strong@Pass123"8.2 网络策略限制
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: nacos-access-control namespace: nacos-prod spec: podSelector: matchLabels: app: nacos policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: project: microservices ports: - protocol: TCP port: 8848 - protocol: TCP port: 9848在实际生产环境中运行Nacos集群两年多,最大的经验是一定要提前规划好容量。我们曾经因为低估了配置变更频率导致磁盘频繁告警,后来通过以下措施彻底解决:
- 配置日志自动归档脚本
- 对历史配置设置保留策略
- 定期清理不用的命名空间
- 为监控系统设置合理的告警阈值