3. 服务发现与注册实战
在微服务架构中,服务实例的 IP 地址和端口会随着扩缩容、滚动发布、节点漂移而不断变化。如果调用方把目标地址写死在配置文件里,任何一次 Pod 重建都会导致调用失败。服务发现要解决的核心问题就是:让调用方通过一个稳定的逻辑名称找到当前健康的实例集合。在 AWS 上,这个能力由 AWS Cloud Map 提供命名空间与服务注册表,而 EKS 侧则通过 Service、EndpointSlice 以及 CoreDNS 完成集群内的名称解析。本节把两者串起来,讲清楚注册、健康检查与 DNS 解析的完整链路。
一、先分清两条解析路径
在 EKS 中访问一个微服务,实际存在两条并行的发现路径,理解它们的边界是实战的前提。
- 集群内路径:调用方 Pod 通过 CoreDNS 解析
order-svc.default.svc.cluster.local,得到的是 Kubernetes Service 的 ClusterIP(或 Headless Service 的 Pod IP 列表)。这条路径由 kube-proxy / EndpointSlice 维护,与 Cloud Map 无关。 - 跨 VPC / 混合路径:集群外的 EC2、Lambda、其他 VPC 中的服务,通过 Cloud Map 的私有 DNS 命名空间解析
order-svc.ns.internal,得到的是注册实例的 IP。这条路径由 Cloud Map 维护。
很多团队误以为“用了 Cloud Map 就不需要 EKS Service”,实际上两者职责不同:EKS Service 负责集群内四层负载均衡与 EndpointSlice 维护,Cloud Map 负责把实例信息暴露给集群外消费者。真正需要打通的是“Pod 就绪 → EndpointSlice 更新 → Cloud Map 注册表同步”这条链路。
二、用 Cloud Map 注册 EKS 服务实例
Cloud Map 的核心对象有三个:命名空间(Namespace)、服务(Service)、实例(Instance)。命名空间决定 DNS 后缀,服务决定逻辑名称,实例决定 IP 与端口。创建命名空间时选择“私有”,并关联 EKS 所在的 VPC,这样只有该 VPC 及其关联网络内的资源能解析。
aws servicediscovery create-private-dns-namespace \
--name ns.internal \
--vpc vpc-0a1b2c3d4e5f67890
aws servicediscovery create-service \
--name order-svc \
--namespace-id ns-xxxxxxxxxxxx \
--dns-config "NamespaceId=ns-xxxxxxxxxxxx,RoutingPolicy=WEIGHTED,DnsRecords=[{Type=A,TTL=10}]" \
--health-check-custom-config FailureThreshold=1
这里有两个关键参数值得说明。RoutingPolicy 选 WEIGHTED 时,Cloud Map 返回全部健康实例的 A 记录,由客户端自行选择;选 MULTIVALUE 时返回随机顺序的多条记录,适合做简单客户端负载均衡。TTL 设为 10 秒是一个折中值:太短会增加 DNS 查询压力,太长会导致实例下线后调用方仍拿到旧 IP。对于频繁扩缩容的服务,建议 10~30 秒。
实例注册有两种方式。第一种是手动注册,适合 EC2 等静态资源:
aws servicediscovery register-instance \
--service-id srv-xxxxxxxxxxxx \
--instance-id order-svc-10.0.3.21 \
--attributes AWS_INSTANCE_IPV4=10.0.3.21,AWS_INSTANCE_PORT=8080
第二种是自动注册,也是 EKS 场景更常用的方式。可以通过 AWS Cloud Map Controller(基于 ACK 或社区控制器)监听 EndpointSlice 变化,把 ready 的地址自动写入 Cloud Map,Pod 被删除时自动注销。这样注册表始终与集群真实状态一致,避免手工维护带来的漂移。
三、健康检查:谁来判断实例是否可用
健康检查在整条链路中有两层,必须区分清楚。
- Kubernetes 层:Pod 的
readinessProbe决定它是否进入 EndpointSlice。未就绪的 Pod 不会被 Service 转发流量,也不会被同步到 Cloud Map。 - Cloud Map 层:如果实例配置了
HealthCheckCustomConfig,Cloud Map 会维护一个 HEALTHY / UNHEALTHY 状态。使用自定义健康检查时,需要由外部进程调用UpdateInstanceCustomHealthStatus上报状态。
一个常见的配置示例是给 order-svc 的 Deployment 加上就绪探针:
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
当 /healthz 连续 3 次返回非 200,Pod 被标记为 NotReady,EndpointSlice 中该地址的 ready 变为 false,Cloud Map 控制器随即把它从注册表移除或标记为 UNHEALTHY。此时集群外消费者通过 DNS 查询就只会拿到健康实例的 IP。判断链路是否生效,可以依次检查:kubectl get endpointslice -l kubernetes.io/service-name=order-svc 看 ready 字段,再用 aws servicediscovery list-instances --service-id srv-xxx 看 Cloud Map 中的实例状态,两者应当一致。
四、DNS 解析与验证
Cloud Map 私有命名空间创建后,会在关联 VPC 中注入一条 Route 53 私有托管区记录。集群外消费者解析 order-svc.ns.internal 时,会得到当前健康实例的 A 记录集合。验证时不要只看一次解析结果,因为 TTL 和缓存会影响观察:
dig +short order-svc.ns.internal
# 10.0.3.21
# 10.0.3.22
# 缩容后再次解析,确认旧 IP 已消失
kubectl scale deploy order-svc --replicas=1
sleep 15
dig +short order-svc.ns.internal
# 10.0.3.21
如果解析结果与预期不符,按以下顺序排查:确认调用方所在 VPC 已关联该命名空间;确认安全组允许 53 端口 UDP/TCP 出站;确认 Cloud Map 服务中实例状态为 HEALTHY;确认 EndpointSlice 中目标地址 ready 为 true。多数“解析不到”的问题,根源是 Pod 未就绪导致根本没有注册,而不是 DNS 本身故障。
五、一个可直接对照的配置清单
| 配置项 | 推荐值(示例) | 作用 |
|---|---|---|
| 命名空间类型 | 私有 DNS,关联 EKS VPC | 限定解析范围,避免公网暴露 |
| RoutingPolicy | WEIGHTED 或 MULTIVALUE | 决定返回单条还是多条记录 |
| DNS TTL | 10~30 秒 | 平衡解析压力与实例变更时效 |
| readinessProbe | periodSeconds 10,failureThreshold 3 | 控制 Pod 何时进入 EndpointSlice |
| 注册方式 | Cloud Map Controller 自动同步 | 避免手工注册产生漂移 |
| 验证命令 | endpointslice + list-instances + dig | 三层交叉确认注册与解析一致 |