k8s 部署 nacos 集群这件事我前前后后在不同环境里做过五六遍第一次是照着官方给的示例 YAML 直接kubectl apply结果三个 Pod 起来了、日志也刷得挺欢但控制台上集群节点列表里始终只有一个节点另外两个像人间蒸发了一样。后来才发现是 Headless Service 少了一个publishNotReadyAddresses字段节点在变成 Ready 之前互相解析不到于是谁也发现不了谁形成死锁。这个坑官方文档里其实写了但写得很隐蔽藏在 Service 定义里的一行注释上。这篇内容就是围绕照官方那份示例走一遍这件事展开的但我不想写成一份翻译版的 YAML 注释。我更想把每个字段为什么这么写、不这么写会出什么问题讲透把你真跑起来之后半小时内大概率会撞上的那几堵墙提前拆掉。适合已经能在 k8s 里跑起 Deployment 和 Service、但对 Nacos 集群的内部协同机制不太熟的读者也适合那些已经跑起来了但心里没底的人对照着检查一遍。技术栈默认是 Nacos 2.x、Kubernetes 1.2x 时代的主流版本MySQL 走 8.0存储用集群已有的 StorageClass。1. 先定形态Nacos 在 k8s 里到底该怎么摆动手写 YAML 之前有个问题必须先回答清楚你要的是能跑起来的 Nacos还是生产可用的 Nacos 集群。这两者的资源清单差别很大而官方仓库里恰好两种都给了示例很容易拿错文件。1.1 单机、内嵌集群、外置数据库集群三条路的分岔口Nacos 的部署形态本质上是三种区别只在数据落在哪。第一种是单机模式MODEstandalone数据写进容器内嵌的 Derby 库落在/home/nacos/data目录。它的优点是零依赖、启动快、YAML 短得可以贴在一张便利贴上。缺点是数据跟 Pod 生命周期绑死Pod 重建数据就没了而且 Derby 不支持多实例共享同一个库文件——这点很关键很多人想着我挂个 NFS 上去让三个 Pod 共享 data 目录不就行了结果就是文件锁冲突、库文件损坏服务直接起不来。第二种是内嵌 Derby 多节点集群MODEcluster但不接外置数据库。这条路在 Nacos 1.x 时代官方就不太推荐2.x 更是明确要求集群模式必须外置存储。原因是集群模式下每个节点都要读写配置数据Derby 的单文件特性撑不住这种并发写。第三种是集群 外置 MySQL这才是官方示例里nacos-mysql-cluster那一份干的事也是唯一值得上生产的选择。它的结构是三个或五个Nacos Pod 各自无状态所有配置数据、命名空间、历史版本统一落在 MySQL 里节点之间只通过 Raft 和 Distro 协议同步服务实例这类临时数据。我把三者的取舍整理成一张表你对着自己的场景挑就行。形态关键环境变量数据落点适用场景明显短板单机MODEstandalone容器内 Derby本地开发、Demo 演示数据随 Pod 消失无法多副本内嵌集群MODEcluster无数据库配置各自 Derby不建议数据不一致节点间无法共享配置外置库集群MODEcluster 数据源外置 MySQL测试、预发、生产多一个 MySQL 依赖要维护选型上我的建议很直接只要这个环境里有人会依赖它做服务发现就老老实实上第三种。省下来的那点 MySQL 运维成本远小于某天配置全丢之后的重建成本。1.2 节点数为什么必须是奇数而且我建议就从 3 开始Nacos 2.x 里持久化实例和集群元数据走的是 JRaftRaft 的可用性建立在多数派上3 个节点能容忍 1 个挂掉5 个节点能容忍 2 个2 个节点反而不如 1 个——因为任何一个挂掉剩下那个凑不够多数派整个集群的写能力直接归零。所以节点数只能是 1、3、5、7 这样的奇数。实际选型时我会按下表判断3 节点绝大多数业务的起点。资源占用可控容忍单节点故障滚动升级时不会中断写入。5 节点写入压力大、或者对故障容忍度要求更高的场景。代价是节点间同步的网络往返变多选举日志也更长。7 节点及以上基本没必要。Raft 的写放大随节点数线性增长收益却很小。还有一个容易被忽略的点Nacos 集群的节点数一旦定下来扩缩容就不是kubectl scale一句话的事。因为NACOS_SERVERS这个环境变量里硬编码了所有节点的地址加节点意味着所有已有节点的这个变量都要改然后滚动重启。这个后面第 7 章我会详细讲。注意不要用 Deployment 跑 Nacos 集群。Pod 名字随机、没有稳定网络标识节点之间根本没法互相寻址。集群模式必须用 StatefulSet。2. 官方示例仓库里到底给了什么哪些假设藏在细节里很多人说照官方文档部署其实指的是那个维护在开源社区里的 k8s 部署示例仓库。它里面确实有几份能直接用的 YAML但那些文件是最小可运行示例不是生产就绪模板两者的差距需要你自己补。2.1 目录结构扫一遍别拿错文件那个仓库的deploy目录下大致是这么几类东西我用表格对一下你按需取用。文件/目录模式依赖我什么时候会用它nacos-quick-start.yamlstandalone无快速验证镜像能不能起、控制台长什么样nacos-pvc-nfs.yamlstandaloneNFS StorageClass想验证持久化但不想上 MySQLmysql/nacos-mysql-standalone.yamlstandalone外置 MySQL验证数据库连接配置是否正确mysql/nacos-mysql-cluster.yamlcluster外置 MySQL正式部署的起点我通常拿它改helm/下的 chart两者皆可视 values 而定集群已有 Helm 体系、需要参数化时我踩过的一个低级坑第一次直接 apply 了nacos-quick-start.yaml然后纳闷为什么replicas改成 3 之后三个 Pod 各玩各的。因为那份文件本来就是单机模式的MODE是standalone改副本数只会得到三个互不相干的单机实例它们各自写各自的 Derby控制台上谁也看不见谁。2.2 Headless Service 是集群寻址的地基不是可选项集群模式下 Nacos 节点之间怎么找到彼此靠的就是NACOS_SERVERS里那串地址。而这串地址里的主机名必须是每个 Pod 独立的、稳定的 DNS 名形式是nacos-序号.headless-service 名.命名空间.svc.cluster.local这就是 StatefulSet Headless Service 组合的意义。普通 ClusterIP Service 只会给你一个虚拟 IP请求打到哪个 Pod 是随机的节点之间没法建立我知道你是谁的长连接。Headless ServiceclusterIP: None则会给每个 Pod 一条独立的 A 记录nacos-0、nacos-1、nacos-2各有各的地址。这里有个非常关键的字段spec: clusterIP: None publishNotReadyAddresses: truepublishNotReadyAddresses: true的意思是即使 Pod 还没通过 readiness 探针DNS 里也要把它的地址发布出来。为什么必须要因为 Nacos 节点的就绪本身就依赖于它能看见集群里的其他节点——它得先完成集群握手才会认为自己 ready。如果 DNS 只在 ready 之后才发布那三个节点就永远等不到彼此readiness 永远不通过形成经典的启动死锁。我第一次没加这个字段现象就是三个 Pod 都在Running但READY列全是0/1日志里反复刷failed to connect to server或者干脆卡在集群初始化。加上之后就顺了。2.3 端口清单和版本差异别只暴露 8848Nacos 2.x 相比 1.x 最大的变化之一是引入了一套基于 gRPC 的长连接通信端口是对主端口做偏移端口名称用途能否省略8848主端口HTTP 控制台、OpenAPI、健康检查不能9848gRPC 客户端端口2.x 客户端与服务的长连接不能客户端必连9849gRPC 服务端端口节点之间的数据同步集群模式不能省7848Raft 端口集群选举与元数据一致性集群模式不能省偏移规则是 8848 加 1000 得到 9848加 1001 得到 9849。这个设计的好处是客户端算得出来坏处是你只对外暴露 8848 一定会失败——2.x 的客户端启动时会同时连 8848 和 98489848 连不上就报Client not connected, current status:STARTING然后重试到超时。这个报错我在好几个群里见过答案百分之百是端口没开。3. 地基要打牢命名空间、MySQL 与存储类的前置准备YAML apply 之前的那部分准备看起来琐碎但绝大多数起不来的问题都出在这一步。3.1 建库建表SQL 脚本从哪来、为什么必须手工导Nacos 不会帮你自动建表。它启动时会去读config_info、config_info_gray、his_config_info、config_tags_relation、tenant_info、users、roles、permissions这一堆表表不存在就直接抛异常退出日志里通常是Table nacos.config_info doesnt exist或者包装过的Nacos Server did not start because dumpservice bean construction failure。建表脚本的位置官方发布包的conf目录下有mysql-schema.sql容器镜像里对应路径是/home/nacos/conf/mysql-schema.sql。你可以从镜像里拷出来docker run --rm --entrypoint cat nacos/nacos-server:v2.3.2 /home/nacos/conf/mysql-schema.sql nacos-mysql.sql然后按建库、导入两步走CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;mysql -h 10.0.0.10 -u root -p nacos_config nacos-mysql.sql字符集这里我要强调utf8mb4。用utf8三字节那个在存储带 emoji 或者部分生僻字的配置内容时会截断甚至报错而配置文件里写备注带表情符号是很常见的事。这种问题上线之后才发现排查起来极其费时。3.2 连接参数里那几个不写就报错的选项数据库连接串的拼装方式取决于你用哪种配置方式。官方示例里有两套写法老写法MYSQL_SERVICE_HOST、MYSQL_SERVICE_PORT、MYSQL_SERVICE_DB_NAME、MYSQL_SERVICE_USER、MYSQL_SERVICE_PASSWORD、MYSQL_SERVICE_DB_PARAM。新写法直接给SPRING_DATASOURCE_PLATFORMmysql然后用MYSQL_SERVICE_*系列或者用NACOS_PLUGIN_DATASOURCE_*这一套数据源插件参数2.2 之后的版本支持。我更倾向用老写法因为它在示例仓库里覆盖得更全出问题时可搜到的资料多。参数串我会这么写characterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseUnicodetrueuseSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue逐个说一下为什么serverTimezoneAsia/Shanghai容器默认 UTCMySQL 8 默认也是 UTC 或系统时区两边不一致时配置的创建时间会差 8 小时做灰度发布按时间排序就会错乱。useSSLfalse集群内网通信不开 TLS 更省事如果你们的 MySQL 强制 TLS这里要改成一整套证书配置。allowPublicKeyRetrievaltrueMySQL 8 默认认证插件是caching_sha2_password在非 SSL 连接下不加这个参数会直接报Public Key Retrieval is not allowed。autoReconnecttrueMySQL 连接被中间件或防火墙掐断后能自动重连避免 Nacos 长时间空转。3.3 StorageClass 选型的真实影响虽然配置数据都在 MySQL 里但 Nacos 每个节点本地还是要有目录落盘/home/nacos/dataRaft 日志、协议状态、/home/nacos/logs、/home/nacos/plugins如果装了自定义插件。/home/nacos/data里放的是 Raft 的日志段文件写频率不低而且对顺序写比较敏感。我实测过在 NFS 上跑三个节点QPS 一上来 Raft 提交延迟明显飙高日志里能看到leader election timeout之类的告警。所以选存储类的时候优先用块存储类型的 StorageClass云上那种 ESSD、SSD 云盘延迟低、顺序写好。如果没有块存储本地盘 nodeAffinity 也行代价是 Pod 漂移会丢本地数据好在能从 MySQL 和集群其他节点恢复。NFS 类共享存储只建议给logs目录用data目录别放上面。PVC 大小给 10Gi 起步通常够用但如果你开了大量的服务实例注册、Raft 日志累积快可以给到 20Gi 并配上日志清理。4. 三个核心清单的逐字段拆解真正要写的 YAML 就三样Headless Service、一个给控制台/客户端用的普通 Service、StatefulSet。我按这个字段不加会怎样的思路来讲。4.1 Headless Service端口要全发布策略要对apiVersion: v1 kind: Service metadata: name: nacos-headless namespace: nacos labels: app: nacos spec: clusterIP: None publishNotReadyAddresses: true selector: app: nacos ports: - name: client port: 8848 targetPort: 8848 - name: raft port: 7848 targetPort: 7848 - name: grpc-client port: 9848 targetPort: 9848 - name: grpc-server port: 9849 targetPort: 9849selector必须和 StatefulSet 的 Pod 标签完全对上否则 DNS 里一个记录都不会有。publishNotReadyAddresses前面讲过了是解开启动死锁的钥匙。端口给全是为了让节点之间的 9849 通信、Raft 的 7848 通信都能通过 Service 名字走到——虽然同命名空间内 Pod 之间直接用 Pod IP 也能通但走域名更稳Pod 重建后 IP 变了域名不变。4.2 面向客户端的 ServiceClusterIP 还是 NodePort集群内部的应用访问 Nacos用一个普通的 ClusterIP Service 就够了apiVersion: v1 kind: Service metadata: name: nacos-client namespace: nacos spec: type: ClusterIP selector: app: nacos ports: - name: http port: 8848 targetPort: 8848 - name: grpc port: 9848 targetPort: 9848这里有两点经验。第一9848 一定要在这个 Service 里一起暴露很多团队只配了 8848然后 2.x 客户端在集群外怎么都连不上。第二如果你打算用 Ingress 暴露控制台别把客户端流量也走 Ingress——2.x 的 gRPC 长连接对七层代理很不友好需要额外的backend-protocol: GRPC配置而且连接会被代理层断掉重连。客户端流量走 ClusterIP 或 NodePort 更省心。4.3 StatefulSet环境变量与探针是重灾区核心片段如下我把关键项都标出来apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos namespace: nacos spec: serviceName: nacos-headless replicas: 3 podManagementPolicy: Parallel selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: nacos topologyKey: kubernetes.io/hostname containers: - name: nacos image: nacos/nacos-server:v2.3.2 env: - name: MODE value: cluster - name: NACOS_SERVERS value: nacos-0.nacos-headless.nacos.svc.cluster.local:8848 nacos-1.nacos-headless.nacos.svc.cluster.local:8848 nacos-2.nacos-headless.nacos.svc.cluster.local:8848 - name: SPRING_DATASOURCE_PLATFORM value: mysql - name: MYSQL_SERVICE_HOST value: mysql.nacos.svc.cluster.local - name: MYSQL_SERVICE_PORT value: 3306 - name: MYSQL_SERVICE_DB_NAME value: nacos_config - name: MYSQL_SERVICE_USER value: nacos - name: MYSQL_SERVICE_PASSWORD valueFrom: secretKeyRef: name: nacos-db key: password - name: MYSQL_SERVICE_DB_PARAM value: characterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseUnicodetrueuseSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue - name: JVM_XMS value: 2g - name: JVM_XMX value: 2g - name: JVM_XMN value: 1g - name: TZ value: Asia/Shanghai ports: - containerPort: 8848 - containerPort: 9848 - containerPort: 9849 - containerPort: 7848几个点值得单独说。podManagementPolicy: Parallel让三个 Pod 同时创建而不是默认的OrderedReady一个个来。因为 Nacos 节点之间是平等协商没有主从启动顺序的要求并行启动能显著缩短整体就绪时间。不过要注意并行启动时三个节点同时抢着初始化数据库表结构的情况是存在的Nacos 自己做了幂等处理一般没问题但如果你的 MySQL 权限很严格没有建表权限务必先把表导进去。NACOS_SERVERS里我用了完整的 FQDN而不是简写的nacos-0.nacos-headless。短名字在同一个命名空间里能解析跨命名空间就不行了写全了永远没错。JVM_XMS和JVM_XMX设成一样大是为了避免 JVM 堆动态伸缩带来的抖动。堆大小怎么定我的经验值是3 节点、注册实例总数在 5000 以内、配置项在 2 万条以内2g 堆足够规模再往上加先加堆到 4g再考虑加节点。同时一定要给容器配resources.requests/limits且limits.memory要比JVM_XMX大出至少 1Gi留给堆外内存、元空间和线程栈否则会被 OOMKilled 得莫名其妙。探针配置readinessProbe: httpGet: path: /nacos/v1/console/health/readiness port: 8848 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 livenessProbe: httpGet: path: /nacos/v1/console/health/liveness port: 8848 initialDelaySeconds: 60 periodSeconds: 15 failureThreshold: 5initialDelaySeconds给宽一点。Nacos 2.x 启动要连数据库、建连池、初始化 Raft、拉集群成员冷启动 40 到 60 秒很常见。liveness 设太紧会导致容器还在初始化就被判定死亡重启进入无限重启循环——这个现象很有迷惑性kubectl describe里看到的是一堆Liveness probe failed你会以为是应用挂了其实只是探针太急。4.4 数据目录的挂载以及为什么 logs 也要单独挂volumeMounts: - name: data mountPath: /home/nacos/data - name: logs mountPath: /home/nacos/logs volumeClaimTemplates: - metadata: name: data spec: accessModes: [ReadWriteOnce] storageClassName: fast-ssd resources: requests: storage: 20Gilogs目录我单独用一个emptyDir或者另一个 PVC 挂上。用 emptyDir 的话 Pod 重启日志就没了排查偶发问题很痛苦用 PVC 又要多一份存储开销。我的折中是接一套日志采集把/home/nacos/logs收走本地只留 emptyDir。这样既不占存储又能在日志平台里查历史——naming-server.log、config-server.log、nacos-cluster.log三个文件是排查问题的主战场。5. 起来之后怎么验证集群是真的成了Pod 是Running且READY 1/1只能说明容器活着不能说明集群正常。我一般按下面的顺序逐层确认。5.1 从控制台倒着查节点列表、数据源、健康状态登录控制台进集群管理里的节点列表。三个节点都在且状态是 UP这才算第一步过了。如果只有一个节点按这个顺序查kubectl exec -it nacos-0 -- env | grep NACOS_SERVERS确认环境变量真的注进去了而且三个地址格式正确、没有多余空格和换行。kubectl exec -it nacos-0 -- nslookup nacos-1.nacos-headless.nacos.svc.cluster.local确认 DNS 能解析。解析不到就是 Service 的selector对不上或者publishNotReadyAddresses没开。kubectl exec -it nacos-0 -- curl -s http://nacos-1.nacos-headless.nacos.svc.cluster.local:8848/nacos/v1/console/health/readiness确认能通。不通就是网络策略NetworkPolicy挡了或者端口在 Service 里没映射。这三步基本能定位 90% 的节点不齐问题。我自己遇到过最隐蔽的一次是环境变量里手写地址时中间用了换行符而不是空格分隔NACOS_SERVERS被解析成了一个超长的无效主机名日志里看不出所以然只能靠打印环境变量发现。5.2 数据库连接失败的几种面孔数据库问题在日志里的表现形式五花八门我总结了几种典型日志关键字真实原因处理方式No DataSource set数据源参数没生效SPRING_DATASOURCE_PLATFORM拼错或没设检查变量名2.x 用mysql全小写Access denied for user账号密码错或账号没有该库的权限用同一个账号在容器里mysql -h手连一次Public Key Retrieval is not allowedMySQL 8 认证插件 无 SSL连接串加allowPublicKeyRetrievaltrueTable nacos.xxx doesnt exist建表脚本没导或导到了别的库检查MYSQL_SERVICE_DB_NAME与导入的库是否一致Communications link failure网络不通、Service 名错、端口错先在 Pod 里nc -zv host 3306其中No DataSource set这个最有欺骗性因为它看起来像是 Nacos 内部逻辑错误实际上就是环境变量没配对。2.2 之后的版本数据源插件化改造过一次有些版本对MYSQL_SERVICE_*系列变量的支持有细微差别遇到死活不认的情况可以退一步用SPRING_DATASOURCE_URL/SPRING_DATASOURCE_USERNAME/SPRING_DATASOURCE_PASSWORD这套标准 Spring 变量直接注入成功率更高。5.3 启动成功但客户端连不上把 9848 这个坎跨过去控制台能进、节点也齐了但业务服务启动报Client not connected, current status:STARTING或者一直server is DOWN八成是 gRPC 端口的问题。排查链路是这样的先看客户端配置的serverAddr是什么。如果写的是某个 NodePort 或者 Ingress 域名那么客户端会自动用8848 1000 9848去连 gRPC而这个端口你可能根本没映射出去。解决办法有两个把 9848 也映射出来或者在客户端显式指定nacos.remote.client.grpc.port之类的参数不同版本参数名有差异以你用的客户端版本为准。还有一种情况是客户端和服务端版本跨了大版本。2.x 服务端对 1.x 客户端是兼容的走 HTTP 长轮询但 1.x 服务端对 2.x 客户端就不行了。混部环境里最容易出这种事服务端升到了 2.3某个老服务还在用 1.4 的客户端那个服务就会一直报连接异常。6. 暴露与接入控制台给人用客户端给机器用这两种流量的特性完全不同混在一起设计方案后面一定会返工。6.1 控制台走 Ingress但要注意路径前缀控制台的 Web 路径默认带/nacos前缀。用 Ingress 转发时有两种做法一是保留前缀path: /nacospathType: Prefix二是用 rewrite 把前缀剥掉。我推荐保留前缀因为前端资源里的绝对路径是按/nacos生成的剥掉之后静态资源会 404。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nacos-console namespace: nacos spec: ingressClassName: nginx rules: - host: nacos.internal.example http: paths: - path: /nacos pathType: Prefix backend: service: name: nacos-client port: number: 8848顺手提一句鉴权。2.2 之后的版本默认要求配置nacos.core.auth.plugin.nacos.token.secret.key长度至少 32 字节且要 Base64 编码否则启动会直接失败并提示密钥不合法。这个值建议用 Secret 注入不要明文写在 ConfigMap 里也不要全公司所有环境共用同一个。6.2 客户端侧的三行配置与两个常见误配Spring Cloud 体系里的典型配置nacos: discovery: server-addr: nacos-client.nacos.svc.cluster.local:8848 config: server-addr: nacos-client.nacos.svc.cluster.local:8848 file-extension: yaml我见过两个高频误配。第一个是server-addr里写了http://前缀某些版本的客户端解析会出问题一律不带协议头更稳妥。第二个是命名空间服务端建了dev、test、prod三个命名空间客户端只配了server-addr没配namespace结果永远读的是 public 下的配置改了半天配置不生效其实是读错了地方。命名空间要用ID一串 UUID而不是名字这个坑我踩过填名字不报错但就是不生效。7. 运营期扩容、升级、备份与监控部署完成只是起点。真正消耗精力的是后面这些日常动作。7.1 扩容为什么不能直接改 replicas前面提过NACOS_SERVERS是硬编码的。你把replicas从 3 改成 5新起来的nacos-3、nacos-4会带着旧的NACOS_SERVERS只有三个地址而已有的三个节点根本不知道有新成员存在。结果是控制台上看着有 5 个 Pod节点列表还是 3 个新节点成了孤岛。正确顺序是把NACOS_SERVERS环境变量改成 5 个地址。先把replicas调到 5让新 Pod 起来此时它们能连上老节点但老节点不认它们属于过渡状态。对 StatefulSet 做一次滚动重启可以用kubectl rollout restart statefulset/nacos让所有节点都读到新的NACOS_SERVERS。在控制台确认 5 个节点全部 UP。缩容反过来先改NACOS_SERVERS去掉要删的节点滚动重启最后再降replicas。顺序错了会出现一边已经下线、另一边还在往它发请求的情况短暂的数据不一致。7.2 升级 2.x 小版本时的滚动策略Raft 集群的升级有个原则先升 follower最后升 leader。StatefulSet 默认从序号大的开始删nacos-2、nacos-1、nacos-0而 leader 通常是先启动的那个序号 0 的概率最大所以默认顺序恰好是友好的不用特别调整。升级过程中集群会短暂失去多数派吗3 节点滚动升级时任何时刻只有一个节点不可用剩余两个仍构成多数派写入不会中断。但如果升级的版本跨了较大范围比如从 2.0 直接跳到 2.3中间的数据结构可能有变更务必先在测试环境完整演练一遍并做好 MySQL 全量备份。7.3 备份的三层数据库、配置导出、PVC 快照很多人只做数据库备份这在多数场景下够用因为配置数据全在 MySQL 里。备份策略我一般这么定MySQL 每日全量 binlog恢复粒度可以到秒。这是主力手段。控制台配置导出Nacos 支持按命名空间导出配置压缩包定期导一份存到对象存储作为跨版本的兜底。它的好处是可读、可人工比对。PVC 快照只针对 Raft 数据万一整个集群状态错乱可以用它快速回滚但要三个节点一起回滚单独回滚一个节点的快照可能引入不一致。7.4 监控指标怎么抓Nacos 暴露了 Prometheus 格式的指标但默认可能没开全。需要在配置里打开端点暴露management.endpoints.web.exposure.include*然后curl http://pod:8848/nacos/metrics就能拿到。我关注的几个指标指标含义告警阈值参考nacos_monitor{nameconfigCount}配置总数突增突降都值得看nacos_monitor{nameserviceCount}服务数与预期注册量对不上就查nacos_timer_seconds系列各类请求耗时P99 明显升高时查数据库JVM 堆使用率内存压力持续高于 80% 考虑扩堆Raft 提交延迟集群一致性健康度持续偏高多半是存储慢配置中心最典型的故障是配置推送延迟表现是改了配置但应用迟迟收不到。这种时候先看 Nacos 的推空比例和长轮询连接数再看下游应用的监听是否正常注册了。8. 我踩过的几个不那么常见的坑前面讲的都是主流问题最后补几个偏门但确实会让人卡半天的。容器时区。即便加了TZAsia/Shanghai某些基础镜像里的 JVM 仍然读的是/etc/localtime。如果日志时间还是差 8 小时可以在启动参数里加-Duser.timezoneAsia/Shanghai或者直接把宿主机的时区文件挂进容器。日志时间和实际时间对不上排查时序问题时会非常痛苦两个事件的先后顺序都可能判反。ConfigMap 热更新不生效。如果你把application.properties通过 ConfigMap 挂载进去改 ConfigMap 之后文件确实会更新有几分钟延迟但 Nacos 不会自动重新加载必须重启 Pod。所以能用环境变量配的尽量用环境变量Nacos 官方镜像对MYSQL_SERVICE_*、JVM_*这些变量都有专门处理比挂配置文件省心。多个命名空间共用一套 MySQL。测试环境和生产环境共用一个 MySQL 实例、不同的库看起来隔离了但 Nacos 的连接池、慢查询、磁盘 IO 是全实例共享的。测试环境跑个压测生产环境的配置推送就可能超时。有条件的话实例级别隔离更稳。health 接口被安全策略拦掉。有些团队会在 Service 前面加一层网络策略只放行 8848 和特定来源。这时候 kubelet 的探针请求来自节点 IP可能被挡表现为 Pod 反复重启但手动curl是通的。遇到这种手动通、探针不通第一时间怀疑 NetworkPolicy。StatefulSet 的 PVC 不会自动清理。删掉 StatefulSet 之后再重建同名 PVC 还在旧的 Raft 数据会被新集群读到可能出现集群 ID 冲突、节点状态异常。彻底重来的时候记得把 PVC 一起删干净或者干脆换个命名空间重建。这套东西跑顺之后其实很省心一年到头出不了几次问题。真正花时间的从来都是前面那几次起来了但不对的排查。我个人的习惯是每套新环境部署完都按第 5 章那个顺序过一遍检查清单把每一步的验证结果记在部署文档里。下次出问题时至少知道自己是从哪个正常状态偏出去的。
阅读完成 · 觉得有帮助?