igozhang

——

    Nacos 启动失败问题

    ENV

    类别 信息
    环境 基地A · Kubernetes 预发集群
    节点 OS Ubuntu 20.04.6 LTS,Kernel 5.4.0-216-generic
    容器运行时 Docker 25.0.5
    Kubernetes v1.24.6(Client/Server)
    节点 7 台(3 Master + 4 Worker),均 Ready
    Nacos Helm 部署,nacos/nacos-server:v2.2.1,StatefulSet 3 副本
    MySQL Bitnami MySQL Cluster(mysql 命名空间),主库 mysql-cluster-primary
    网络 Calico,CoreDNS

    1. 问题现象

    对象 状态
    nacos-0/1/2 CrashLoopBackOff,重启约 1.4 万次
    StatefulSet nacos 1/3 Ready(巡检时),后自行恢复为 3/3
    异常事件 BackOff restarting failed container

    关键日志:

    Nacos Server did not start because dumpservice bean construction failure : No DataSource set
    Caused by: java.lang.IllegalStateException: No DataSource set
    

    2. 问题原因

    2.1 定位推导过程

    Step 1 — 确认容器退出原因

    timeout 10 kubectl describe pod nacos-0 -n nacos | tail -n 60
    

    关键输出:Exit Code: 1,环境变量 SPRING_DATASOURCE_PLATFORM=mysql,MySQL 参数来自 ConfigMap nacos-cm

    Step 2 — 查看崩溃日志

    timeout 10 kubectl logs nacos-0 -n nacos --previous --tail=60
    

    关键输出:No DataSource setExternalDumpService.init 失败。

    Step 3 — 核对 MySQL 配置

    timeout 10 kubectl get configmap nacos-cm -n nacos -o yaml | tail -n 60
    

    关键输出:host/port/user/password/db 均存在,配置完整。

    Step 4 — 验证 MySQL 服务与连通性

    timeout 10 kubectl get pod,svc,endpoints -n mysql -o wide 2>&1 | head -n 60
    
    timeout 10 kubectl exec -n mysql mysql-cluster-primary-0 -- \
      mysql -unacos -p'password' -Dnacos_config -e "SELECT 1 AS connectivity_ok;" 2>&1
    

    关键输出:MySQL Pod/Endpoints 正常;connectivity_ok = 1

    Step 5 — 分析 nacos.log 时序

    timeout 10 kubectl exec -n nacos nacos-0 -c nacos -- \
      sh -c "grep -iE 'datasource|Hikari|No DataSource' /home/nacos/logs/nacos.log | tail -n 60" 2>&1
    

    关键输出:

    09:53:28  findMapper dataSource: mysql  →  No DataSource set   # HikariPool 尚未启动
    09:59:07  HikariPool-1 - Starting...
    09:59:07  HikariPool-1 - Start completed.                        # MySQL 连接实际正常
    

    Step 6 — 集群互连日志

    Fail to connect server ... Connection refused: nacos-X.nacos-hs.nacos.svc.cluster.local:9849
    

    2.2 根因结论

    层级 原因
    直接原因 Spring Bean 初始化竞态:ExternalDumpServiceHikariPool 就绪前访问 DB → No DataSource set → 退出码 1
    放大因素 mysql.paramconnectTimeout=1000(1 秒)过短,加剧数据源初始化失败概率
    连带问题 3 节点同时 CrashLoop,8848/9848 gRPC 互连 Connection refused,集群无法形成,形成死锁

    MySQL 服务、账号、网络均正常,非 MySQL 故障

    2.3 形成原因推测

    运维在集群网络抖动或 CoreDNS 短暂异常期间,未做单节点恢复而任由 3 副本同时 CrashLoop;叠加 开发/运维 部署 Nacos 时 MySQL 连接超时仅 1 秒,启动窗口过窄。多次异常重启后 3 节点互相不可达,集群死锁,重启次数累积至 1.4 万+。后期节点偶发启动顺序正确时自行恢复为 3/3 Ready,但配置缺陷未消除,仍有复发风险。


    3. 解决方案

    3.1 预防性加固 ConfigMap(当前已 Ready,勿主动滚动重启)

    kubectl patch configmap nacos-cm -n nacos --type merge -p \
      '{"data":{"mysql.param":"characterEncoding=utf8&connectTimeout=30000&socketTimeout=30000&autoReconnect=true&useSSL=false"}}'
    

    验证:

    timeout 10 kubectl get configmap nacos-cm -n nacos -o yaml | grep -A1 'mysql.param'
    

    3.2 若再次全员 CrashLoop — 打破集群死锁

    kubectl scale statefulset nacos -n nacos --replicas=1
    

    首节点稳定后:

    kubectl scale statefulset nacos -n nacos --replicas=3
    

    3.3 验证服务正常

    timeout 10 kubectl get pod -n nacos -o custom-columns=\
    NAME:.metadata.name,READY:.status.containerStatuses[0].ready,RESTARTS:.status.containerStatuses[0].restartCount
    
    timeout 10 kubectl exec -n nacos nacos-0 -- curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8848/nacos/
    

    期望:3 个 Pod READY=true,HTTP 200/302;RESTARTS 不再持续增长。


    4. 后续预防措施

    开发

    • Nacos 集群模式启动脚本中,确保 ExternalDumpService 依赖 DataSource 就绪后再初始化(或升级至已修复竞态的版本)。
    • Helm Chart 默认值中 connectTimeout 不低于 10 秒。
    • 集群模式部署文档中明确:首次启动建议单节点 bootstrap 再扩至 3 副本。

    运维

    • 监控 Nacos Pod RESTARTS 增量,连续增长时优先 scale replicas=1 而非反复 delete Pod。
    • 维护 MySQL 前先确认 Nacos 连接参数与 Endpoints 可用。
    • ConfigMap 变更后择低峰滚动重启,并逐 Pod 观察日志。
    • nacos 命名空间配置 Pod 重启告警(如 1 小时内重启 > 3 次)。

    5. 配置参数建议

    参数 当前值 建议值 原因/后果
    mysql.paramconnectTimeout 1000(1s) 30000(30s) 过短易在 DNS/网络抖动时连接失败,触发 DataSource 竞态崩溃
    mysql.paramsocketTimeout 3000(3s) 30000(30s) 读超时过短导致连接中断,加剧启动失败
    StatefulSet replicas(故障时) 3 临时 1 3 节点同时崩溃时集群 gRPC 死锁,单节点可打破循环
    Pod 内存 request 2Gi 保持或升至 4Gi 集群模式 + JVM 1g 堆,节点内存紧张时 OOM 风险

    附录:关键 ConfigMap 参考

    mysql.db.host: mysql-cluster-primary.mysql.svc.cluster.local
    mysql.db.name: nacos_config
    mysql.user: nacos
    mysql.password: password
    mysql.port: "3306"
    mysql.param: characterEncoding=utf8&connectTimeout=30000&socketTimeout=30000&autoReconnect=true&useSSL=false
    

    MP3