系列导航:Kubernetes 重建迁移教程系列。服务名、命名空间和域名均为通用示例。
把 Nacos 配置中的固定 ClusterIP 改成 Service DNS,只是解决了“配置地址不稳定”的问题。如果 Feign、Gateway 和 RestTemplate 仍通过 Nacos 获取实例列表,注册中心故障仍可能让所有内部调用以“没有可用服务”失败。
本文讨论下一阶段:保留 Nacos 配置中心,把每条调用链的服务发现交给 Kubernetes Service。
先拆开 Nacos 的两个职责
flowchart LR
A["应用"] --> B["Nacos 配置中心"]
A --> C["Nacos 服务发现"]
C --> D["Pod 实例列表"]
E["目标架构"] --> F["Nacos 继续提供配置"]
E --> G["Service DNS 提供服务入口"]
G --> H["EndpointSlice 中的 Ready Pod"]
迁移服务发现不等于同时移除 Nacos 配置中心。把两个变更放在同一个窗口,配置加载、实例注册和调用路由会同时变化,回滚会非常困难。
什么时候适合使用 Service DNS
适合:
- 调用方和被调用方位于同一个 Kubernetes 集群。
- 服务已经有稳定的 Service 名和 namespace。
- Service selector、readiness 和 EndpointSlice 可靠。
- 不依赖 Nacos metadata 做复杂权重、区域或版本路由。
需要谨慎:
- 跨集群、跨机房或 Kubernetes 外部实例。
- 同一服务需要按租户、版本或标签选择实例。
- 客户端需要感知每个实例才能做特殊负载均衡。
- 长连接协议,或对每个实例使用固定地址,例如部分 Kafka listener。
Kubernetes Service 的目标就是让客户端不需要跟踪易变 Pod 实例,并通过 EndpointSlice 持续维护后端。Kubernetes Service。
第 1 步:盘点所有服务发现入口
在代码和配置仓库中扫描:
rg -n '@FeignClient|lb://|LoadBalancerClient|DiscoveryClient|NacosDiscovery|spring.cloud.nacos.discovery' .
整理成表:
caller,client_type,service_id,target_namespace,criticality,owner,status
gateway,Gateway,rbac-api,app-test,critical,team-a,pending
order-api,Feign,inventory-api,app-test,normal,team-b,pending
不要一扫描到多少就改多少。先选一个只读、低风险、容易验证的调用链。
第 2 步:证明目标 Service 真实可用
kubectl -n app-test get service rbac-api -o yaml
kubectl -n app-test get endpointslice \
-l kubernetes.io/service-name=rbac-api -o wide
kubectl -n app-test get pod -l app.kubernetes.io/name=rbac-api -o wide
要求:
- selector 精确匹配目标 Pod。
- EndpointSlice 只有 Ready Pod。
port和targetPort正确。- 至少有两个副本时分布在不同节点。
从调用方 Pod 测试:
kubectl -n gateway exec <gateway-pod> -- \
getent hosts rbac-api.app-test.svc.cluster.local
kubectl -n gateway exec <gateway-pod> -- \
curl -fsS http://rbac-api.app-test.svc.cluster.local:8082/<health-path>
这一步不通过时,不要修改 Feign 或 Gateway。
第 3 步:先迁移一个 Feign Client
原来只使用服务名:
@FeignClient(name = "inventory-api")
public interface InventoryClient {
@GetMapping("/v1/inventory/{id}")
InventoryDTO find(@PathVariable String id);
}
Spring Cloud OpenFeign 支持通过配置为 Feign Client 提供固定 URL;提供 URL 后,请求不再通过 LoadBalancer 按服务名发现实例。当前版本的具体配置键应以项目所用 Release Train 的官方 OpenFeign 文档为准。
spring:
cloud:
openfeign:
client:
config:
inventory-api:
url: http://inventory-api.app-test.svc.cluster.local:8090
connectTimeout: 3000
readTimeout: 5000
Kubernetes Service 本身负责把连接转发到 Ready Endpoint,因此这里不需要客户端再从 Nacos 获取 Pod IP。
第 4 步:为切换保留开关
在 Nacos 配置或 ConfigMap 中保留两套值:
clients.inventory-api.discovery-mode=nacos
clients.inventory-api.kubernetes-url=http://inventory-api.app-test.svc.cluster.local:8090
应用根据明确开关做出选择,而不是在 DNS 失败后悄悄回退到 Nacos。隐式双路径会造成:
- 同一次故障中不同实例走不同链路。
- 指标难以判断哪个入口生效。
- Nacos 问题被自动回退掩盖。
回滚只需要恢复开关并滚动调用方。
第 5 步:灰度一个调用方 Pod
创建 canary Deployment,副本为 1,并只修改它的配置:
kubectl -n <caller-namespace> apply -f caller-canary.yaml
kubectl -n <caller-namespace> rollout status deployment/<caller>-canary --timeout=10m
通过临时 Service、Header 或内部直连将测试请求发给 canary。观察:
kubectl -n <caller-namespace> logs deployment/<caller>-canary --since=15m \
| grep -Ei 'inventory-api|available server|timeout|refused'
同时验证目标 Service Endpoint 变化时 canary 能继续调用:
kubectl -n app-test rollout restart deployment/inventory-api
kubectl -n app-test rollout status deployment/inventory-api --timeout=10m
第 6 步:迁移 Gateway 路由
原路由:
uri: lb://rbac-api
候选路由:
uri: http://rbac-api.app-test.svc.cluster.local:8082
先复制一条测试 Path 或测试 Host,不直接替换全部生产路由。验证:
- Path rewrite。
- Authorization Header。
- Cookie 和 CORS。
- 超时和响应体。
- 目标 Pod 滚动时的连接连续性。
如果 Gateway 仍报 Load balancer does not have available server,说明实际生效的 route 仍是 lb://,或配置没有刷新。
第 7 步:迁移普通 HTTP Client
把基础 URL 放在配置中:
clients.file-storage.base-url=http://file-storage.app-test.svc.cluster.local:8686
客户端必须同时设置:
- connect timeout。
- read timeout。
- 连接池和并发上限。
- 只对幂等请求设置有限重试。
- 对可选依赖的明确降级策略。
Service DNS 解决的是服务发现问题,不会自动解决慢依赖和线程堆积。
第 8 步:扩大到完整 Deployment
灰度稳定后:
kubectl -n <caller-namespace> set env deployment/<caller> \
DISCOVERY_MODE=kubernetes
kubectl -n <caller-namespace> rollout status deployment/<caller> --timeout=10m
检查全部 Pod 的配置来源和日志,避免配置只在新 Pod 上生效。
第 9 步:停止这一服务的 Nacos 注册
只有所有调用方都切换后,才考虑关闭目标服务的 Nacos discovery 注册:
spring:
cloud:
nacos:
discovery:
register-enabled: false
属性名会随 Spring Cloud Alibaba 版本变化,必须按项目依赖核对。关闭前从代码仓库和 Nacos 订阅关系确认没有遗漏调用方。
Nacos Config 仍可继续工作;不要误删 spring.config.import 或配置 namespace。
第 10 步:模拟 Nacos 服务发现不可用
在隔离环境验证:
- 调用方已经完成配置加载。
- 暂时阻断其到 Nacos Naming 端口的访问。
- 持续请求已经迁移的调用链。
- 重启目标业务 Pod,观察 EndpointSlice 更新。
预期:已迁移调用链仍可通过 Service DNS 调用;需要动态配置的功能是否受影响,要单独记录。
回滚
停止扩大范围
→ 恢复 discovery-mode=nacos
→ 滚动调用方
→ 检查 Nacos 中目标服务实例
→ 验证 lb:// 调用
如果已经关闭目标服务注册,应先恢复注册并确认实例健康,再切回调用方。
完成定义
- 已迁移调用链不再创建 Nacos LoadBalancer Client。
- Feign/Gateway 生效 URL 为 Service DNS。
- 目标 Pod 滚动时请求无持续失败。
- Nacos Naming 暂时不可用时,已迁移调用链仍能工作。
- Nacos Config 加载与动态刷新仍正常。
- 所有调用方迁移完成后才关闭目标服务注册。
- 每条调用链都有独立回滚开关和验证记录。
正确的迁移单位不是“整个 Nacos”,而是一条可验证、可回滚的服务调用链。
DISCUSSION
讨论与反馈