资讯动态

Kubernetes 部署 Nacos 集群实战:StatefulSet YAML 配置与避坑指南

发布时间:2026/10/20 3:13:30 来源:尧图企业网站定制
简介这份资源面向需要在 Kubernetes 上快速落地 Nacos 的运维与后端开发人员尤其适合刚接触 K8s 编排、希望跳过繁琐配置直接跑通集群的入门者。它把 Nacos 在 K8s 中的部署拆成按执行顺序排列的极简脚本涵盖数据库配置、Headless Service、StatefulSet、Service 以及 Ingress 暴露等关键环节省去大量手工拼装 YAML 的试错成本。压缩包共 6 个文件以 5 个 yaml 清单为主分别承担数据库连接、无头服务、有状态副本集、服务暴露与外部访问入口等职责另附 1 个 txt 说明文件整体仅 3KB轻量易读。目前已有 765 人学习下载说明该方案在同类场景中具备一定参考价值。读者可据此获得一套可直接套用的部署骨架理解 Nacos 集群在 K8s 中的资源组织方式并借助顺序化文件快速完成环境搭建与排错验证。1. 从一堆散装 YAML 到能扛住重启的 Nacos 集群手里拿到一份 k8s nacos 集群部署脚本 yaml第一反应往往不是兴奋而是犯嘀咕这玩意儿直接kubectl apply就能跑起来吗我见过太多人把单机模式的 Nacos 配置原封不动塞进 StatefulSetPod 是起来了三个副本互相看不见控制台登录进去节点列表永远只有一个。这份资源解决的正是这个断层——它把 Nacos 在 Kubernetes 上做集群化部署所需的 Deployment/StatefulSet、Service、ConfigMap、持久化存储、MySQL 数据源配置打包成可直接套用的 YAML 集合。适合两类人一是手上已经有 K8s 集群、需要把注册中心和配置中心从 ECS 上迁进来的运维二是做微服务改造、本地用 docker-compose 跑 Nacos 跑得挺顺、一上集群就翻车的后端。它不教 K8s 基础也不讲 Nacos 源码就是让你少写几百行容易写错的编排文件。2. 拆开这份 YAMLNacos 集群在 K8s 里到底靠什么立住2.1 为什么是 StatefulSet 而不是 DeploymentNacos 集群节点之间需要稳定的网络标识来互相感知。Deployment 创建的 Pod 名字带随机后缀重启后 IP 和主机名全变节点间 Raft 或 Distro 协议的成员列表就对不上了。StatefulSet 给每个 Pod 固定有序的名字比如nacos-0、nacos-1、nacos-2配合 Headless Service 提供稳定的 DNS 记录。常见做法是让 Nacos 通过nacos-0.nacos-headless.namespace.svc.cluster.local这种域名互相注册而不是靠 IP。资源里的 StatefulSet 通常长这样apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos namespace: middleware spec: serviceName: nacos-headless replicas: 3 selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: containers: - name: nacos image: nacos/nacos-server:v2.2.3 ports: - containerPort: 8848 name: client - containerPort: 9848 name: grpc-client env: - name: MODE value: cluster - name: NACOS_SERVERS value: nacos-0.nacos-headless.middleware.svc.cluster.local:8848 nacos-1.nacos-headless.middleware.svc.cluster.local:8848 nacos-2.nacos-headless.middleware.nacos-headless.middleware.svc.cluster.local:8848这里有几个参数必须盯死。serviceName要和 Headless Service 的名字完全一致写错了 Pod 的 DNS 就解析不出来。NACOS_SERVERS里每个地址都要带完整域名和端口少一个节点集群就退化成单机。MODE必须是cluster默认是standalone不显式设置的话三个 Pod 各跑各的。端口方面Nacos 2.x 除了 8848 还依赖 9848 做 gRPC 通信K8s Service 里如果只暴露 8848客户端连得上但配置推送会超时这个坑后面还会细说。2.2 Headless Service 与 ClusterIP Service 的分工资源里一般会定义两个 Service。一个是 Headless ServiceclusterIP: None专门给 StatefulSet 的 Pod 提供稳定 DNS不负责负载均衡。另一个是普通 ClusterIP Service给业务应用访问 Nacos 用把请求轮询到三个 Pod 上。apiVersion: v1 kind: Service metadata: name: nacos-headless namespace: middleware spec: clusterIP: None selector: app: nacos ports: - port: 8848 name: client - port: 9848 name: grpc-client --- apiVersion: v1 kind: Service metadata: name: nacos-svc namespace: middleware spec: type: ClusterIP selector: app: nacos ports: - port: 8848 targetPort: 8848 name: client - port: 9848 targetPort: 9848 name: grpc-clientHeadless Service 的selector必须和 StatefulSet 的 Pod 标签匹配否则 DNS 记录为空。普通 Service 的targetPort要和容器containerPort对齐名字建议保持一致方便排查。如果业务在集群外访问把type改成NodePort或走 Ingress但注意 Nacos 2.x 的 gRPC 端口 9848 在 Ingress 上需要额外配置 TCP 转发很多资源包只给 HTTP 规则结果客户端一直报连接超时。2.3 数据源与持久化别让配置数据随 Pod 一起消失Nacos 默认用内嵌 Derby 数据库集群模式下必须换成外部 MySQL否则每个节点数据独立配置列表各看各的。资源里的 ConfigMap 会包含一份application.properties片段核心是这几行spring.datasource.platformmysql db.num1 db.url.0jdbc:mysql://mysql-svc.middleware.svc.cluster.local:3306/nacos_config?characterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseSSLfalse db.user.0nacos db.password.0nacosMySQL 连接串里的useSSLfalse在集群内网环境通常没问题但如果 MySQL 开了强制 SSL这里要改成true并挂载证书。db.num是数据库实例数单实例写 1主从或多节点可以调大。数据库本身要提前建好nacos_config库并执行 Nacos 发行包里的mysql-schema.sql否则启动时会报表不存在。持久化方面如果只用 MySQL 存配置Nacos 容器本身不需要挂载数据卷日志可以输出到 stdout 由 K8s 收集。但有些资源包会把nacos/data目录挂到 PVC 上这在内嵌 Derby 模式下有意义集群模式反而多余还容易因为 PVC 读写慢拖累启动。我一般会建议把日志目录单独挂一个 emptyDir 或 hostPath方便出问题时快速捞日志。3. 从零跑通部署顺序、参数覆盖与验证命令3.1 部署前的三个前置检查在kubectl apply之前先确认三件事。第一命名空间是否存在资源里如果写了namespace: middleware你得先kubectl create ns middleware否则 apply 会报错。第二MySQL 是否可达可以在集群里起一个临时 Pod 用mysql -h测试连通性别等 Nacos 起来了才发现连不上数据库。第三StorageClass 是否可用如果资源里用了 PVC 模板而集群没有默认 StorageClassPod 会一直 Pending。# 检查命名空间 kubectl get ns middleware # 测试 MySQL 连通性 kubectl run mysql-test --rm -it --imagemysql:8.0 --restartNever -- \ mysql -h mysql-svc.middleware.svc.cluster.local -u nacos -pnacos -e show databases; # 查看 StorageClass kubectl get sc这三条命令花不了一分钟但能省掉后面半小时的排查。尤其是 MySQL 连通性Nacos 启动时如果连不上数据库日志里只会报Communications link failure不会直接告诉你数据库地址写错了。3.2 应用 YAML 的正确顺序资源包里的文件通常有多个建议按依赖顺序 apply。先 ConfigMap再 Headless Service然后 StatefulSet最后普通 Service。虽然kubectl apply -f .也能一把梭但出问题时不好定位是哪一步挂了。kubectl apply -f nacos-configmap.yaml kubectl apply -f nacos-headless-svc.yaml kubectl apply -f nacos-statefulset.yaml kubectl apply -f nacos-svc.yaml # 观察 Pod 启动状态 kubectl get pods -n middleware -l appnacos -wStatefulSet 的 Pod 是按顺序启动的nacos-0先起来然后nacos-1最后nacos-2。如果nacos-0卡在 ContainerCreating 或 CrashLoopBackOff后面的不会启动。这时候先看kubectl describe pod nacos-0 -n middleware的事件再看容器日志kubectl logs nacos-0 -n middleware。常见原因是 ConfigMap 挂载路径不对或者数据库连接串里的主机名解析不了。3.3 验证集群是否真正成型三个 Pod 都 Running 不代表集群就绪。进控制台看节点列表是最直接的。默认账号密码是nacos/nacos登录后到「集群管理」→「节点列表」应该看到三个节点每个节点的状态是UP且raft或distro元数据一致。# 端口转发到本地验证 kubectl port-forward -n middleware svc/nacos-svc 8848:8848 # 另一个终端调用 OpenAPI 查节点列表 curl http://127.0.0.1:8848/nacos/v1/ns/operator/servers | jq返回的 JSON 里servers数组应该有三个元素每个元素的state是UP。如果只有一个或两个说明NACOS_SERVERS环境变量没配对或者 Headless Service 的 DNS 解析有问题。可以在 Pod 里执行nslookup nacos-headless.middleware.svc.cluster.local确认 DNS 记录是否包含三个 Pod IP。提示Nacos 2.x 的节点间通信除了 8848 还依赖 9848 和 9849如果集群里开了 NetworkPolicy记得放行这些端口否则节点列表显示 UP 但配置同步会间歇性失败。4. 避坑排查Nacos on K8s 最常见的五类翻车4.1 Pod 一直 CrashLoopBackOff日志报数据库连接失败现象是kubectl get pods显示反复重启kubectl logs里看到java.sql.SQLNonTransientConnectionException或Communications link failure。原因通常是 MySQL 地址写错、数据库没建表、或者密码里有特殊字符没转义。解决方法是先确认 MySQL Service 的 DNS 能解析再进 MySQL 手动执行source mysql-schema.sql最后检查 ConfigMap 里的密码是否被 YAML 的引号规则截断。如果密码含$或#建议用 Secret 挂载而不是明文写在 ConfigMap 里。4.2 控制台能登录但节点列表只有一个现象是三个 Pod 都 Running控制台也能打开但「节点列表」只显示当前节点。原因是NACOS_SERVERS环境变量没设置或者设置成了 IP 列表而不是域名。Nacos 集群模式下每个节点启动时会读取NACOS_SERVERS来构建成员列表如果这个变量为空它就按单机模式跑。解决方法是检查 StatefulSet 里的 env 配置确保每个地址都是pod-name.service-name.namespace.svc.cluster.local:8848的格式并且三个地址都在。4.3 客户端注册成功但配置拉取超时现象是微服务能注册到 Nacos但NacosValue或ConfigService.getConfig一直超时。原因是 Nacos 2.x 的 gRPC 端口 9848 没有在 Service 里暴露或者 Ingress 只转发了 HTTP。客户端首次连接走 8848后续长连接走 9848如果 9848 不通注册能成功但配置推送会失败。解决方法是在 Service 和 Ingress 里都加上 9848 的 TCP 转发K8s 内部访问直接用 ClusterIP Service 的 9848 端口。4.4 滚动更新时集群脑裂现象是执行kubectl rollout restart statefulset nacos后节点列表出现两个nacos-0或者部分节点状态变成SUSPICIOUS。原因是 StatefulSet 默认按逆序滚动更新nacos-2先重启此时集群还剩两个节点Raft 多数派还在但如果更新过程中有节点没来得及重新加入成员列表就会混乱。解决方法是设置podManagementPolicy: Parallel让三个 Pod 同时重启或者手动逐个删除 Pod 并等待节点重新上线后再操作下一个。生产环境建议在业务低峰期做并且提前备份 MySQL 里的配置数据。4.5 PVC 挂载导致启动缓慢或失败现象是 Pod 卡在 ContainerCreating事件里报FailedMount或Unable to attach or mount volumes。原因是资源包里给 Nacos 挂了 PVC但集群的 StorageClass 不支持动态供给或者 PVC 的访问模式是ReadWriteOnce而三个 Pod 分布在不同节点。Nacos 集群模式下配置数据存在 MySQL本地不需要持久化建议直接删掉 volumeMounts 和 volumeClaimTemplates日志用 emptyDir 或 stdout 输出。如果非要保留把访问模式改成ReadWriteMany并确认 StorageClass 支持。5. 进阶调优把 Nacos 集群从「能跑」推到「稳跑」5.1 JVM 参数与资源限制的匹配Nacos 默认 JVM 堆内存是 2G在 K8s 里如果给容器限制resources.limits.memory: 2GiJVM 会按容器内存的 1/4 自动调整实际可能只有 512M导致频繁 GC 甚至 OOM。稳妥做法是显式设置JVM_XMS和JVM_XMX环境变量让堆内存和容器 limit 保持合理比例。env: - name: JVM_XMS value: 1g - name: JVM_XMX value: 1g - name: JVM_XMN value: 512m resources: requests: memory: 1.5Gi cpu: 500m limits: memory: 2Gi cpu: 1000mJVM_XMS和JVM_XMX设成一样避免堆动态伸缩带来的抖动JVM_XMN是新生代大小一般设为堆的 1/2 到 1/3。容器 limit 要比堆最大值多留 500M 左右给元空间、线程栈和直接内存。如果节点内存紧张可以把三个 Pod 分散到不同节点用podAntiAffinity做反亲和。5.2 健康检查探针的阈值调整Nacos 启动要连数据库、初始化 Raft、加载配置首次启动可能超过 30 秒。如果livenessProbe的initialDelaySeconds设得太短Pod 会被反复杀掉重启永远起不来。建议startupProbe单独设置给足 60 到 120 秒。startupProbe: httpGet: path: /nacos/actuator/health port: 8848 initialDelaySeconds: 10 periodSeconds: 5 failureThreshold: 24 livenessProbe: httpGet: path: /nacos/actuator/health port: 8848 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /nacos/actuator/health port: 8848 initialDelaySeconds: 20 periodSeconds: 5 failureThreshold: 3startupProbe的failureThreshold乘以periodSeconds就是最大容忍启动时间24 乘 5 等于 120 秒足够覆盖大多数环境。livenessProbe的initialDelaySeconds要大于startupProbe的总时长避免启动阶段被误杀。readinessProbe控制流量接入节点没完全就绪时不要让它接收业务请求。5.3 用 PDB 和反亲和保证可用性三个副本的集群如果 K8s 节点维护时一次性驱逐两个 PodNacos 的 Raft 多数派就没了整个注册中心不可写。加一个 PodDisruptionBudget 限制自愿驱逐的数量再用反亲和把 Pod 分散到不同节点。apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: nacos-pdb namespace: middleware spec: minAvailable: 2 selector: matchLabels: app: nacos --- affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: nacos topologyKey: kubernetes.io/hostnameminAvailable: 2表示任何时刻至少有两个 Pod 可用节点 drain 时如果只剩两个 Pod驱逐会被阻塞直到新的 Pod 起来。反亲和用preferred而不是required避免节点不够时 Pod 无法调度。如果集群节点数少于三个required会导致第三个 Pod 永远 Pending。从那以后我每次拿到新的 Nacos YAML都会先跑一遍kubectl apply --dry-runserver看有没有字段拼写错误再检查NACOS_SERVERS和 9848 端口最后才真正 apply。这套流程帮我省掉了至少三次半夜爬起来处理集群脑裂的后悔药。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑