orders 这个名字解析成功后,请求到了哪里

前两篇已经建立了本地进程链:loadgen → frontend → orders → inventory-v1 / inventory-v2,并让 orders 调用 external-stub。第 00 篇中的 orders.invalid 在解析阶段失败;本篇换一个问题:即使 orders 的 Service 名称能够解析,哪些条件决定某次 TCP 连接落到哪个 Pod?读者需能区分 DNS 查询、TCP 连接、HTTP 请求和 Pod readiness。

Service 名称、稳定入口、端点集合与实际 Pod 的关系

图左侧是调用者看到的稳定入口,右侧是会变化的就绪后端集合。两侧不能画等号:对普通 ClusterIP Service,DNS 回答的是入口地址而不是“此次请求的 Pod IP”;实际端点的选择依赖集群的数据面实现及当时的端点状态。Istiod 和 Envoy 都不在这里的无网格数据路径中。

从 DNS 到后端的四个环节

以 frontend 在同一 namespace 访问 http://orders:8080/quote 为例,以下是应该逐层检查的对象,而非在没有集群时声称实际发生的抓包结果。

  1. DNS 名称:Kubernetes 的 DNS 约定会使同一 namespace 内的短名 orders 指向相应 Service;跨 namespace 应用显式使用完整名称。查到的 ClusterIP 不等于某个 Pod IP;使用无头 Service 等不同类型时,DNS 的答案规则又不同。诊断时先保存实际查询名、搜索域与响应,而不是先猜测后端。
  2. Service 声明:spec.ports[*].port 是入口端口,targetPort 是传到后端容器的目标端口,可为数字或具名端口。Service 选择器与标签必须匹配,且后端状态须允许接收新流量;DNS 成功只说明名称层面有答案,不证明端口与后端就绪。
  3. EndpointSlice 与数据面:EndpointSlice 记录地址、端口和条件等信息,Service 的网络实现读取这些信息,将访问稳定入口的连接导向某个后端。实现可能使用 kube-proxy 的具体模式,也可能由集群中的其他 Service 数据面完成;不要把某一种 iptables 链的细节写成所有集群的必经路径。CNI 的职责主要是为 Pod 建立网络连接能力,不能把 CNI 和 Service 负载均衡、DNS 视作同一个组件。
  4. 已经建立的连接:一次连接选中了某个后端后,在 HTTP/1.1 keep-alive 或 HTTP/2 多路复用下,多次请求可能沿旧连接到相同实例。EndpointSlice 更新与数据面收敛会影响后续选择,却不会自动把所有现存连接的每个请求都切去新实例;连接被关闭、重连以及具体实现决定实际转移时机。

对应的逻辑路径是 名称 → Service 入口 → Service 数据面选择后端 → Pod 网络 → orders 应用。调用方仍要确认 X-Request-ID 是否被传给应用、实际响应里的后端版本,以及有无副作用;只在 kubectl get svc 看到正确 ClusterIP,证据链还未到请求结果。

为什么扩容后“权重”不一定立刻变

设旧版 Pod 已承接一条长连接,扩容新增 inventory-v2 并让它就绪。即使新的 EndpointSlice 已列出 v2,仍需要相关节点的数据面拿到更新;旧连接内的请求可能持续到 v1。若测试只发送大量并发请求而每次都新建连接,得到的分布与复用一条长连接的分布可能不同。这里没有承诺某个百分比或收敛秒数——应用连接池、就绪变化和网络实现都必须纳入测量条件。

反例是“Service 有 ClusterIP,EndpointSlice 却没有可用端点”:DNS 查询能成功,连接仍可能无法到达业务进程。第 00 篇的 -orders-url '' 则是应用没有配置任何上游,不是这个 Kubernetes 反例;它返回的 503 也不能冒充某个 Service 数据面的状态码。

可复跑检查与当前缺口

本仓库的 examples/service-mesh/scripts/check-02.sh 只读取资源,不创建、不修改或清理集群。它要求明确的实验 context 和专用 namespace;在 examples/service-mesh/ 目录的调用入口是:

1
SM_CONTEXT=service-mesh-lab SM_NAMESPACE=service-mesh-lab bash scripts/check-02.sh

在调用脚本前,操作者必须核对隔离集群的实际 context 名称;上面的名称是准入要求示例,不是当前机器已存在的 context。脚本发现名字不匹配即退出 2。它最多采集 Kubernetes 版本、Service、EndpointSlice 和 Pod;完整实验还要使用同一组 frontend/orders/inventory 服务部署到隔离 namespace,记录工作负载镜像 digest 与配置、从 frontend 容器实际查询 DNS 并带请求 ID 调用 orders、扩缩容前后分别记录新旧连接及后端身份,最后用非零退出码断言失败条件。当前工程尚未提供经实测的集群部署镜像,不能把这份只读检查称为端到端实验。

当前宿主没有 kubectl、kind、容器运行时或现成集群;脚本会输出 NOT_RUN: kubectl: command not found 并返回 127。**本篇集群调用、DNS 答案、CNI、EndpointSlice 更新与连接复用实验均为 NOT_RUN,没有实测结论。**第 00–01 篇进程日志只能说明合成应用的行为;建站成功同样不能填补缺失的数据面证据。所需环境及复跑清单见 writing-plans/service-mesh/CONTINUE.md。

练习与参考解答

  1. orders 的 DNS 回答指向 ClusterIP,kubectl get endpointslices 列出两个就绪 Pod,却只观察到一个版本。应补哪些信息?参考解答:确认实际请求发起者、查询名与 DNS 响应、Service 的入口/目标端口及选择器、端点在请求时刻的条件、发起者是否复用同一 TCP/HTTP2 连接、相关节点数据面是否同步,以及每个请求 ID 的后端响应。两个端点存在不意味着按 HTTP 请求逐次轮换。
  2. 构造“DNS 成功但调用失败”的对照,同时避免把进程模拟当作集群结果。参考解答:在隔离集群内先采集正确 Service 和就绪 EndpointSlice,发送带标识请求保存成功响应;随后仅在实验 namespace 把后端缩至无就绪端点,保存新 EndpointSlice、原 DNS 响应、客户端的实际错误与非零失败断言。旧连接需要单独处理,不能拿其结果代表新连接;当前环境未运行这个对照,故不得填写状态码或比例。

官方资料与系列导航

系列导航:00 一次服务调用需要哪些基础条件 → 01 SDK、网关与服务网格分别处理什么 → 02 Service 地址怎样到达 Pod(本文)→ 03 Envoy 怎样转发请求。