Nacos 3.x 配置中心、服务注册与命名空间治理工具手册
配置改了,为什么服务仍然读到旧值
联调现场常出现两种看似无关的故障:开发者在控制台发布了 order-service.yaml,应用重启后仍读到旧配置;服务明明注册成功,调用方却查不到健康实例。真正的分岔通常不在“网络通不通”,而在连接到了哪个 Nacos、使用了哪个 namespace / group / dataId,以及客户端是否仍在维持实例租约。
Nacos 把两类能力放在同一平台中:配置中心保存并推送配置,命名服务维护“哪些实例可以被发现”的运行视图。它适合微服务配置分发与注册发现,不是业务数据库、密钥保险库或完整流量治理平台。若团队只需要静态配置,版本化文件和部署系统更简单;若需要强事务、多键查询或业务状态持久化,应使用数据库;若需要 L7 流量策略、零信任身份和代理数据面,应评估服务网格。
Nacos 3.x 的资源关系先记住两条:
配置:namespaceId -> groupName -> dataId
命名:namespaceId -> groupName -> serviceName -> clusterName -> instancenamespace 通常隔离环境或租户,group 聚合同一项目或发布域,dataId 标识一份配置。命名模型中的 clusterName 是服务下的实例分组,不是 Nacos Server 集群。服务类型决定实例采用临时还是持久语义;同一个 service identity 不能混用两种实例类型。
先把 3.2 单节点安全地跑起来
Nacos 下载页列出稳定的 3.x 与 2.x 发行线。下面固定 3.2.3,便于团队复现;升级时先查看目标小版本说明并验证客户端兼容。Nacos 3.x Server / Console 运行包要求 Java 17,Java 客户端仍可运行在 Java 8。容器方式不要求宿主机安装 JDK。
首次操作需要 Docker Compose、curl、空闲的 8080、8848、9848 端口,以及两个不会提交到仓库的随机值:节点间 identity 和 JWT 签名密钥。签名密钥必须是至少 32 个原始字符经 Base64 编码后的字符串。
$raw = New-Object byte[] 48
[Security.Cryptography.RandomNumberGenerator]::Fill($raw)
[Convert]::ToBase64String($raw)将输出放进本机 .env 的 NACOS_AUTH_TOKEN,另生成随机的 NACOS_AUTH_IDENTITY_KEY 和 NACOS_AUTH_IDENTITY_VALUE。仓库只能提交 .env.example 占位符。
services:
nacos:
image: nacos/nacos-server:v3.2.3
container_name: te-nacos
environment:
MODE: standalone
NACOS_AUTH_ENABLE: "true"
NACOS_AUTH_ADMIN_ENABLE: "true"
NACOS_AUTH_CONSOLE_ENABLE: "true"
NACOS_AUTH_SYSTEM_TYPE: nacos
NACOS_AUTH_TOKEN: ${NACOS_AUTH_TOKEN:?set NACOS_AUTH_TOKEN}
NACOS_AUTH_IDENTITY_KEY: ${NACOS_AUTH_IDENTITY_KEY:?set NACOS_AUTH_IDENTITY_KEY}
NACOS_AUTH_IDENTITY_VALUE: ${NACOS_AUTH_IDENTITY_VALUE:?set NACOS_AUTH_IDENTITY_VALUE}
ports:
- "127.0.0.1:8080:8080"
- "127.0.0.1:8848:8848"
- "127.0.0.1:9848:9848"
volumes:
- nacos-data:/home/nacos/data
- nacos-logs:/home/nacos/logs
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8848/nacos/v3/admin/core/state/readiness | grep -q '\"data\":\"ok\"'"]
interval: 10s
timeout: 5s
retries: 30
volumes:
nacos-data:
nacos-logs:docker compose up -d nacos
docker compose ps nacos
docker compose logs --tail=100 nacosdocker compose ps 应从 starting 变为 healthy。这里检查的是公开的 readiness API;只验证进程可读,并不替代一次带认证的配置发布、读取和服务注册实验。控制台入口是 http://127.0.0.1:8080/,不是旧教程里的 8848/nacos。首次启用默认认证时,Nacos 不再附带管理员默认密码;可在控制台初始化,也可调用一次初始化接口:
curl -sS -X POST 'http://127.0.0.1:8848/nacos/v3/auth/user/admin' \
-d 'password=<strong-admin-password>'
curl -sS -X POST 'http://127.0.0.1:8848/nacos/v3/auth/user/login' \
-d 'username=nacos&password=<strong-admin-password>'第二个响应应包含 accessToken、tokenTtl、globalAdmin 和 username。把 token 只放在当前终端,例如 export NACOS_ACCESS_TOKEN='<access-token>';管理员密码与 token 都不能写入 Compose、脚本参数日志或 Git。
用一份配置走通管理面和客户端面
Nacos 3.x 把调用者分成管理面与客户端面。发布、删除、导入、历史查询使用 /nacos/v3/admin/*;业务应用读取已知配置、注册实例、续约和查询已知下游使用 /nacos/v3/client/*。3.x 已移除 v1/v2 Admin API,旧脚本不能只换镜像而不迁移路径。
先在控制台创建 namespace te-order-dev,复制它的 namespace ID 到 NAMESPACE_ID。ID 与显示名称不是一回事,SDK 和 API 通常需要 ID。
export NACOS_ADDR='http://127.0.0.1:8848'
export NAMESPACE_ID='<namespace-id>'
export NACOS_GROUP='TE_ORDER_DEV'
export NACOS_DATA_ID='order-service.yaml'
curl -sS -X POST "$NACOS_ADDR/nacos/v3/admin/cs/config" \
--data-urlencode "accessToken=$NACOS_ACCESS_TOKEN" \
--data-urlencode "namespaceId=$NAMESPACE_ID" \
--data-urlencode "groupName=$NACOS_GROUP" \
--data-urlencode "dataId=$NACOS_DATA_ID" \
--data-urlencode $'content=feature:\n checkout: true\nlimits:\n pendingOrders: 100' \
--data-urlencode 'type=yaml'成功响应的统一外层应含 code: 0,data 为 true。随后用客户端接口读取同一个三元组:
curl -sS -G "$NACOS_ADDR/nacos/v3/client/cs/config" \
--data-urlencode "accessToken=$NACOS_ACCESS_TOKEN" \
--data-urlencode "namespaceId=$NAMESPACE_ID" \
--data-urlencode "groupName=$NACOS_GROUP" \
--data-urlencode "dataId=$NACOS_DATA_ID"响应的 data.content 应包含 checkout: true,并带 md5、contentType 等字段。发布成功但读取不到时,逐项比较 URL、context path、namespace ID、group 和 dataId;不要先清缓存,因为错空间造成的空结果不会被清缓存修好。
反向实验:故意读错命名空间
将读取命令中的 namespaceId 改为 public。预期是拿不到刚发布的内容,或得到明确的不存在响应;控制台中的原配置不会消失。这证明配置身份由 namespace、group、dataId 共同决定,也解释了为什么团队不能把 public 和 DEFAULT_GROUP 当长期默认值。
若错误空间仍能读到同名内容,说明另一个空间已经存在同名 dataId。此时应比较内容 MD5、创建者和发布历史,而不是把“读到了值”误判为接入正确。
注册实例,并观察租约如何失效
下面注册一个临时实例。ip 必须是消费者网络中可达的地址;示例地址只用于观察注册模型,不代表真实业务端口可调用。
export NACOS_SERVICE='te-order-api'
curl -sS -X POST "$NACOS_ADDR/nacos/v3/client/ns/instance" \
--data-urlencode "accessToken=$NACOS_ACCESS_TOKEN" \
--data-urlencode "namespaceId=$NAMESPACE_ID" \
--data-urlencode "groupName=$NACOS_GROUP" \
--data-urlencode "serviceName=$NACOS_SERVICE" \
--data-urlencode 'clusterName=DEV' \
--data-urlencode 'ip=127.0.0.1' \
--data-urlencode 'port=18080' \
--data-urlencode 'ephemeral=true' \
--data-urlencode 'heartBeat=false'
curl -sS -G "$NACOS_ADDR/nacos/v3/client/ns/instance/list" \
--data-urlencode "accessToken=$NACOS_ACCESS_TOKEN" \
--data-urlencode "namespaceId=$NAMESPACE_ID" \
--data-urlencode "groupName=$NACOS_GROUP" \
--data-urlencode "serviceName=$NACOS_SERVICE" \
--data-urlencode 'healthyOnly=false'注册响应的 data 应为 ok;查询结果应出现 ip、port、healthy、enabled、ephemeral、clusterName。HTTP OpenAPI 注册的临时实例必须续约:
curl -sS -X POST "$NACOS_ADDR/nacos/v3/client/ns/instance" \
--data-urlencode "accessToken=$NACOS_ACCESS_TOKEN" \
--data-urlencode "namespaceId=$NAMESPACE_ID" \
--data-urlencode "groupName=$NACOS_GROUP" \
--data-urlencode "serviceName=$NACOS_SERVICE" \
--data-urlencode 'clusterName=DEV' \
--data-urlencode 'ip=127.0.0.1' \
--data-urlencode 'port=18080' \
--data-urlencode 'ephemeral=true' \
--data-urlencode 'heartBeat=true'续约请求会忽略 healthy、weight、enabled 和 metadata。若返回错误码 21003,实例已过期被移除,客户端应携带完整字段并以 heartBeat=false 重新注册。生产项目优先使用官方 SDK 的长连接、订阅、本地缓存和自动续约能力,不要用临时 shell 循环充当注册客户端。
反向实验:停止续约
注册成功后不再发送续约,按查询响应中的 instanceHeartBeatTimeOut 和 ipDeleteTimeout 观察状态变化。预期先失去健康资格,随后从临时实例视图中移除;具体耗时以服务端返回字段和当前配置为准。若长期不消失,检查注册的 ephemeral、service 类型和实际查询空间。
这个实验说明临时实例是“由活客户端持有”的 AP 运行状态,主要通过 Distro 同步;持久实例是 CP 语义的持久资源,可从服务端状态恢复。二者故障语义不同,不能在同一 service identity 下混用。
接进真实项目时固定四个坐标
项目配置应把地址与资源身份显式外置,启动日志打印非敏感坐标:
nacos:
server-addr: 127.0.0.1:8848
namespace: ${NACOS_NAMESPACE_ID}
group: TE_ORDER_DEV
data-id: order-service.yaml
service-name: te-order-api
cluster-name: DEV
username: ${NACOS_USERNAME}
password: ${NACOS_PASSWORD}nacos.serverAddr=127.0.0.1:8848
nacos.namespace=<namespace-id>
nacos.group=TE_ORDER_DEV
nacos.dataId=order-service.yaml
nacos.service=te-order-api
nacos.cluster=DEV密码、access token 和签名密钥不得打印。客户端只应获得目标 namespace 的配置读取、已知服务查询和自身服务注册权限;发布平台使用独立身份调用 Admin API。这样可以把“应用运行”与“修改全局配置”分开审计。
配置监听器收到新值后先解析到不可变对象,完成格式与范围校验,再原子替换当前快照。错误配置应保留上一份可用值并上报告警,不能因为一次解析失败把运行值置空。发布流程至少保留变更人、内容哈希、关联工单、灰度对象和回滚版本。
五个端口对应五条不同链路
| 端口 | 协议与职责 | 网络策略 |
|---|---|---|
8080 | 独立控制台和 Console API | 只给管理网络或 SSO 入口 |
8848 | 主 HTTP 端口,Admin/OpenAPI 及部分认证请求 | 按需给发布平台和客户端;不暴露公网 |
9848 | 客户端 gRPC,由主端口按 +1000 推导 | 客户端到 Server 的 TCP 转发 |
9849 | Server 间 gRPC,由主端口按 +1001 推导 | 仅集群节点互通 |
7848 | JRaft 请求,由主端口按 -1000 推导 | 仅集群节点互通 |
配置客户端地址仍通常写 8848,3.x 客户端会按偏移计算 9848。负载均衡器必须对 gRPC 使用 TCP 转发,不能把 9848 当普通 HTTP/2 反向代理。只开放 8848 时,登录或简单 HTTP 可能成功,而 SDK 长连接持续失败;这类“半通”比完全不通更容易误诊。
端口改变时要整体检查偏移冲突。例如主端口改为 18848 后,客户端与服务端 gRPC 默认随之变为 19848、19849,JRaft 变为 17848;控制台 8080 独立配置。
单机、共享环境和生产集群怎样选
单机模式可用内置 Derby,启动快但没有节点故障容忍,适合个人开发和短期测试。共享开发环境至少需要持久卷、认证、资源 owner、备份与变更记录;多人依赖它时,单节点停机会同时中断配置读取和注册更新。
生产常见形态是奇数个 Nacos Server 节点、稳定的内部负载入口、受保护的 7848/9849 节点网络,以及外部数据库。官方部署架构同时使用 Distro 与 Raft:临时服务实例偏向可用性,通过 Distro 在节点间同步;配置、持久命名状态和集群元数据依赖更强的一致性路径。增加 Server 节点提高并发和故障容忍,但不会自动提高外部数据库可用性,也不能替代跨故障域放置。
三节点通常是高可用起点,五节点提高故障容忍也增加 Raft 通信、发布确认与运维成本。节点数应由故障域、客户端连接数、配置监听数、实例数、注册与推送速率、数据库延迟和恢复目标共同决定。多集群路由在官方部署说明中仍标为待完成能力,不能把它当现成的跨地域容灾承诺。
平台选型时比较这些事实:
已经使用 Spring Cloud Alibaba、Dubbo 等 Nacos 生态,并同时需要配置与注册发现,Nacos 的接入成本通常较低。只需要服务发现且重视 DNS、节点 agent、跨数据中心 gossip,可比较 Consul。已全面运行 Kubernetes,服务发现可先评估原生 Service / EndpointSlice;额外引入 Nacos 会增加一套身份、存储和故障面。
团队没有能力维护数据库、高可用、升级与安全基线时,托管服务的费用可能低于自建的人力与事故成本。
Nacos 开源发行版采用 Apache License 2.0。二次分发或修改仍需保留许可与声明;商业支持、托管服务和插件应分别核对各自条款,不能从核心仓库许可推导全部外围能力。
从现象反推故障层
| 现象 | 第一证据 | 常见原因 | 恢复动作 |
|---|---|---|---|
| 控制台打不开但 API 可用 | docker compose ps、8080 监听 | 仍访问 8848/nacos,或漏映射独立控制台端口 | 改用 8080,检查 Console 进程与网络 |
| 登录成功但 SDK 超时 | 客户端 gRPC 日志、9848 连通性 | 防火墙只放行 8848,或代理协议错误 | 放行 TCP 9848,按端口偏移修负载均衡 |
| 发布成功但应用不更新 | 应用打印的四个坐标、监听日志、MD5 | namespace ID / group / dataId 错配,监听器解析失败 | 比较坐标与内容哈希,保留上一快照并修配置 |
| 实例存在但调用失败 | 查询结果中的 IP/端口,从消费者网络直连 | 注册了容器内地址、localhost 或错误 cluster | 修正广告地址,注销旧实例后重新注册 |
| 权限刚修改仍旧生效 | 角色、权限与服务端缓存配置 | 用户/角色/权限缓存存在短暂延迟 | 等待缓存收敛后重试;紧急撤权同时撤销凭证 |
| 节点反复选举或发布变慢 | JRaft 状态、节点间 7848、数据库延迟 | 节点网络抖动、磁盘或外部数据库瓶颈 | 先恢复节点与存储稳定性,再处理业务发布 |
| 改密码后仍是旧行为 | 实际环境变量、volume、启动日志 | 旧数据目录保留账号和初始化状态 | 个人环境可清卷重建;共享环境按变更流程迁移 |
排障时区分管理视图和发现视图。消费者拿到的是经过 cluster、enabled、healthy、保护阈值和选择器过滤后的视图,不一定等同于控制台中的完整实例列表。
清理、回滚与退出
先注销实验实例,再删除实验配置:
curl -sS -X DELETE "$NACOS_ADDR/nacos/v3/client/ns/instance" \
--data-urlencode "accessToken=$NACOS_ACCESS_TOKEN" \
--data-urlencode "namespaceId=$NAMESPACE_ID" \
--data-urlencode "groupName=$NACOS_GROUP" \
--data-urlencode "serviceName=$NACOS_SERVICE" \
--data-urlencode 'clusterName=DEV' \
--data-urlencode 'ip=127.0.0.1' \
--data-urlencode 'port=18080' \
--data-urlencode 'ephemeral=true'
curl -sS -X DELETE "$NACOS_ADDR/nacos/v3/admin/cs/config" \
--data-urlencode "accessToken=$NACOS_ACCESS_TOKEN" \
--data-urlencode "namespaceId=$NAMESPACE_ID" \
--data-urlencode "groupName=$NACOS_GROUP" \
--data-urlencode "dataId=$NACOS_DATA_ID"再次查询应不再出现该实例与配置。个人本机确认没有其他数据后才能执行 docker compose down -v;共享环境禁止用删卷代替资源级清理。
配置回滚应从历史版本恢复到同一 namespace / group / dataId,并验证应用监听器已经切换到目标 MD5。版本升级回滚比镜像降级复杂:新版本可能已经改变数据库结构或持久状态,必须先验证官方升级路径、备份恢复和客户端兼容,不能直接把镜像标签改回旧版。
退出 Nacos 前先冻结新资源,导出配置及元数据,迁移消费者,再迁移注册者,观察旧集群连接数、监听数和实例数归零,最后撤销账号、网络入口和存储。只迁移配置文件而遗漏动态监听、实例生命周期和权限模型,会留下隐蔽依赖。
长期治理看趋势,不看万能阈值
容量基线至少记录客户端连接数、配置数与平均大小、监听关系数、服务与实例数、注册/注销速率、推送延迟、失败重试、JRaft 提交延迟、节点 CPU/堆/GC、磁盘与外部数据库延迟。阈值应来自压测、SLO 和版本基线,不能照抄脱离负载的固定数字。
每次升级先在隔离环境回放配置发布、客户端读取、注册、续约、节点滚动和数据库恢复。持续多个观测周期后,连接数、实例数和监听数应回到稳定区间,错误率不应随轮次单调增长。团队资源登记至少包含 owner、namespace、group/dataId 或 service、发布与回滚入口、数据敏感级别、凭证轮换、容量预算和下线日期的语义化生命周期状态。
上线评审可以用以下问题收尾:
镜像与客户端版本已固定,并查过目标版本兼容与升级说明。控制台、HTTP、客户端 gRPC、Server gRPC 和 JRaft 端口的网络方向明确。namespace ID、group、dataId、service、clusterName 在日志和资源登记中可追踪。
运行身份不能发布全局配置,发布身份不交给业务进程。管理员密码、token secret、节点 identity 和数据库凭证由密钥系统托管。正向读取、错空间读取、实例续约停止和清理动作都有预期证据。
单节点故障、数据库故障、网络分区、配置回滚和凭证撤销有演练入口。容量、版本、许可、成本、owner 与退出路径进入长期台账。
