6103 字
31 分钟
第 9 篇:Kubernetes Service 与 Ingress 后端流量

第 9 篇:Kubernetes Service 与 Ingress 后端流量#

0. 本篇定位#

上一篇我们讲清了 Pod、Node 与 Deployment:后端 API 如何以 Pod 为运行单元,由 Deployment 维持期望副本,并通过 rollout 完成版本更新。本篇继续往流量入口推进,聚焦 Kubernetes 里最容易被后端工程师混淆的一层:Service 与 Ingress 如何把请求送到正确的后端 Pod

这篇文章不追求记住更多 YAML 字段,而是建立一条可推理的链路:Deployment 创建带 label 的 Pod,Service 通过 selector 找到这些 Pod,EndpointSlice 记录可转发地址,集群 DNS 提供稳定服务名,kube-proxy 或数据面把 ClusterIP 流量转到 Pod,Ingress Controller 再把外部 HTTP/HTTPS 请求按域名和路径转给 Service。

读完后你应该能回答三个问题。第一,为什么后端服务不应该直接依赖 Pod IP。第二,porttargetPortnodePort、Ingress backend service port 分别处在链路的哪一段。第三,当出现 404、503、连接超时、Service 没有 endpoints、TLS 异常时,应该沿着哪些证据一步步定位,而不是靠重启和猜配置。

1. 问题背景#

后端服务在单机或 Docker Compose 阶段,调用关系往往很直接:一个容器名、一个端口、一个反向代理配置,就能跑通本地环境。但进入 Kubernetes 后,Pod 会重建,IP 会变化,副本会扩缩,发布会滚动进行,入口也可能从集群内访问扩展到公网域名和 HTTPS。此时如果仍然用“某个容器监听某个端口”的思维理解流量,就很容易在排障时走错方向。

典型现象包括:Service 已创建但没有后端地址,访问 Service DNS 能解析但连接失败,Ingress 返回 404 或 503,NodePort 在测试环境能用但生产缺少 TLS、访问日志和统一鉴权,LoadBalancer 已分配地址但后端 Pod 并没有 ready。它们表面上都是“访问不通”,根因却可能分别落在 label、selector、EndpointSlice、端口映射、readiness、IngressClass、TLS Secret、Controller 日志或云负载均衡器上。

因此,本篇的核心不是“如何暴露一个服务”,而是“如何理解一条后端流量的证据链”。只要你能把请求从用户浏览器一路追到 Pod 进程监听端口,就能把 Kubernetes 网络从黑盒变成可解释系统。

2. 核心概念#

Kubernetes 的 Service 与 Ingress 不是同一种入口。Service 解决的是一组 Pod 的稳定访问问题,Ingress 解决的是 HTTP/HTTPS 七层路由和外部入口治理问题。它们之间通过明确的职责边界协作。

2.1 Service:给一组 Pod 一个稳定身份#

Pod IP 是临时的。Pod 重建、调度到其他 Node、滚动发布或扩缩容时,后端地址都会变化。Service 的价值就是给这些变化中的 Pod 提供一个稳定的虚拟入口:调用方访问 Service 名称或 ClusterIP,而不是记住某个 Pod IP。

Service 本身不创建 Pod,也不保证应用进程健康。它只根据自己的 selector 找到符合 label 的 Pod,再把流量交给数据面转发。如果 selector 写错,Service 仍然存在,DNS 也可能正常解析,但后端地址为空,请求最终会失败。

对后端工程师来说,Service 是服务契约的一部分。调用方依赖的是 user-api.default.svc.cluster.local 这样的稳定名称,平台侧维护的是 label、selector、端口和 EndpointSlice,应用侧要保证容器真正监听 targetPort 指向的端口,并通过 readiness 表达是否可接流量。

2.2 EndpointSlice:Service 背后的真实后端清单#

EndpointSlice 是 Service 到 Pod 的关键证据。Service 不是直接“知道”每个后端进程在哪里,而是通过控制器维护的 EndpointSlice 获得一组可转发地址。里面会记录后端 IP、端口、协议、ready 状态等信息。

当 Service 没有 endpoints 时,不要先怀疑 DNS,也不要先重启业务。更直接的证据是 kubectl get endpointslice -l kubernetes.io/service-name=<svc>,看是否存在地址、端口是否正确、conditions 是否 ready。如果 EndpointSlice 为空,通常要回到 selector 与 Pod label;如果 EndpointSlice 有地址但访问失败,则继续查端口、readiness、网络策略或应用监听。

EndpointSlice 也能帮助理解滚动发布。新旧 Pod 在发布过程中会同时存在,但只有 ready 的 Pod 才应该进入可服务后端。readiness probe 不只是健康检查,它直接影响 Service 是否把流量送给某个 Pod。

2.3 Ingress:声明 HTTP/HTTPS 路由,不是转发程序本身#

Ingress 是一种路由规则声明,描述“哪个域名、哪个路径、转发到哪个 Service 的哪个端口”。真正接收外部请求、终止 TLS、读取 Ingress 规则并转发流量的是 Ingress Controller,例如 ingress-nginx、Traefik 或云厂商控制器。

这一区分非常重要。只创建 Ingress 对象但集群里没有可用 Controller,规则不会自动生效。IngressClass 不匹配时,Controller 也可能不接管这条规则。排障时既要看 kubectl describe ingress 的规则和事件,也要看 Controller Pod 的日志、Service 暴露方式以及外部负载均衡器状态。

Ingress 适合承载统一域名、路径路由、TLS、访问日志、限流、认证、灰度等入口治理能力。它不应该直接指向 Pod,而应该指向 Service,因为 Service 才负责屏蔽 Pod 副本变化。

3. 运行机制#

一次完整请求可以拆成两段:集群内的 Service 转发,以及集群外到集群内的 Ingress 转发。先把这两段拆开,再合起来看,排障会清晰很多。

3.1 从 Pod label 到 EndpointSlice#

链路起点通常是 Deployment。Deployment 模板给 Pod 打上 label,例如 app: user-api。Service 的 spec.selector 使用同样的 label 选择后端 Pod。控制器发现匹配关系后,为 Service 维护 EndpointSlice。

这里有一个常见误区:Service 的 selector 和 Deployment 的 selector 不是同一个字段。Deployment 的 selector 用来管理自己的 ReplicaSet 与 Pod,Service 的 selector 用来选择流量后端。两者通常会使用相同 label,但职责不同。

排障时可以按这个顺序看证据:kubectl get pod --show-labels 确认 Pod label,kubectl describe svc <svc> 确认 Service selector,kubectl get endpointslice 确认是否生成后端地址。只要这三步对不上,后面的 DNS、Ingress、TLS 都不是第一嫌疑。

3.2 从 ClusterIP 到 Pod 监听端口#

ClusterIP 是 Service 在集群内的虚拟 IP。客户端访问 Service DNS 后通常会得到 ClusterIP,随后数据面根据 Service 规则把流量转发到某个后端 Pod IP 和端口。不同集群的数据面实现可能是 iptables、IPVS、eBPF 或其他方案,但对应用侧暴露的语义是一致的。

这条链路里最容易写错的是端口映射。spec.ports[].port 是 Service 暴露给调用方的端口;targetPort 是后端 Pod 容器实际接收流量的端口;protocol 默认是 TCP。调用方访问的是 Service 的 port,最终应用进程必须监听 targetPort

如果 Service 能解析但访问失败,要把“解析成功”和“转发成功”分开。DNS 只说明服务名能解析,不说明 EndpointSlice 有 ready 地址,也不说明应用进程正在监听目标端口。此时应检查 EndpointSlice、Pod ready 状态、容器监听端口、应用日志和网络策略。

3.3 从 Ingress Controller 到后端 Service#

外部请求进入集群时,通常先到云负载均衡器或 Node 上的入口端口,再进入 Ingress Controller。Controller 根据 Ingress 规则匹配 Host、Path 和 PathType,找到 backend service,再访问该 Service 的端口。

Ingress backend 中写的是 Service 名称和 Service 端口,不是 Pod 端口。也就是说,Ingress 不应该关心 Pod 的 targetPort,它只关心 Service 暴露的 port。如果 Ingress backend port 写成了容器端口,而 Service 没有暴露该端口,就可能出现 503 或后端不可达。

完整链路可以概括为:Client -> LoadBalancer/NodePort -> Ingress Controller -> Ingress rule -> Service port -> EndpointSlice -> Pod targetPort -> application process。任何一段断开,用户看到的都可能只是一个简单的 404、503 或超时。

4. 最小可运行示例#

本地环境的目标不是模拟完整生产入口,而是帮助你快速理解 Service、EndpointSlice 和端口映射。越早建立证据链,越不容易在生产中把所有访问失败都归因于“网络问题”。

4.1 用最小示例跑通 Service#

本地可以用 kind、minikube 或 Docker Desktop Kubernetes 创建一个最小 Deployment 与 Service。Deployment 暴露容器端口 8080,Service 暴露 80 并通过 targetPort: 8080 转发。

apiVersion: v1
kind: Service
metadata:
name: user-api
spec:
type: ClusterIP
selector:
app: user-api
ports:
- name: http
port: 80
targetPort: 8080

这个例子的关键不是 YAML 有多短,而是字段之间的关系清晰:调用方访问 user-api:80,Service 用 app: user-api 找 Pod,最终把请求送到 Pod 的 8080 端口。只要理解这条链路,就能迁移到更复杂的服务。

4.2 用临时 Pod 验证 DNS 与连通性#

在集群内验证 Service,不建议从宿主机直接猜。更可靠的方式是启动一个临时调试 Pod,从集群内部发起请求。

Terminal window
kubectl run net-debug --rm -it --image=curlimages/curl -- sh
curl -v http://user-api.default.svc.cluster.local/health

如果 DNS 失败,重点看 CoreDNS 和 Service 是否存在;如果 DNS 成功但连接失败,重点看 EndpointSlice、Pod ready、端口监听和 NetworkPolicy;如果连接成功但返回 5xx,才进入应用日志和业务依赖排查。这样可以避免把不同层的问题混在一起。

4.3 本地 Ingress 只验证路由语义#

本地 Ingress 可以验证 Host、Path、PathType、backend service 是否写对,但不应该拿它证明生产入口已经合格。因为生产还涉及证书、负载均衡器、真实域名解析、访问日志、限流、超时、请求体大小、WebSocket、灰度和安全策略。

本地测试 Ingress 时,常见方式是安装 ingress-nginx,再把域名通过 hosts 指到本地入口地址。验证重点是:请求 Host 是否匹配,路径是否命中,IngressClass 是否被 Controller 接管,backend service port 是否存在。

如果本地返回 404,优先看 Host/Path 是否命中 Controller 规则;如果返回 503,优先看 backend Service 是否有 ready endpoints。404 和 503 的含义不同,混在一起会浪费大量时间。

5. 配置逐行拆解#

Service 与 Ingress 的字段很多,但后端排障最常用的是几组核心字段。理解它们的上下游语义,比背字段表更重要。

5.1 selector 与 labels:能不能找到后端 Pod#

Service 的 selector 是流量能否找到后端的第一道门。它会匹配 Pod 上的 labels,匹配到的 Pod 再通过 EndpointSlice 暴露为后端地址。大小写、key 名、value 值任何一个不一致,Service 都可能没有可用后端。

推荐把 label 设计成稳定契约,而不是随手命名。常见做法是使用 app.kubernetes.io/nameapp.kubernetes.io/instanceapp.kubernetes.io/component 等标准化标签,避免不同服务都写 app: backend 导致误选或难以治理。

排障时不要只看 YAML 静态文件,还要看集群实际对象。因为 Helm values、Kustomize patch、CI 注入和手工热修都可能改变最终结果。以 kubectl get pod --show-labelskubectl describe svc 的实际输出为准。

5.2 port、targetPort 与 name:端口不是一个数字#

Service 端口至少有两层含义。port 面向调用方,是 Service 对外暴露的端口;targetPort 面向后端 Pod,是流量最终进入容器的端口。它们可以相同,也可以不同。例如 Service 暴露 80,容器实际监听 8080。

targetPort 可以写数字,也可以写容器端口名称。生产中更推荐使用命名端口,例如容器声明 name: http,Service 写 targetPort: http。这样应用端口变更时,Service 与 Ingress 的语义更稳定,读者也更容易理解这是 HTTP 流量而不是任意 TCP。

端口问题的证据链是:Service ports 是否暴露了调用方访问的端口,EndpointSlice 里记录的端口是否符合预期,Pod 容器是否声明并监听该端口,应用日志是否显示绑定成功。如果容器启动后只监听 127.0.0.1 或监听了错误端口,Service 配置再正确也无法正常转发。

5.3 type 与 externalTrafficPolicy:入口边界要说清#

Service type 决定它暴露到哪里。ClusterIP 只在集群内可访问,是默认类型;NodePort 在每个 Node 上开放一个端口,常用于测试或作为更高层入口的底座;LoadBalancer 通过云厂商或负载均衡实现分配外部地址。

不要把 NodePort 当成完整生产入口。它只是把端口暴露到 Node,不天然提供域名、TLS、访问日志、统一鉴权、WAF、限流或灰度治理。生产环境通常会在 LoadBalancer、Ingress Controller、API Gateway 或云边缘网关上补齐这些能力。

externalTrafficPolicy 会影响外部流量进入集群后的转发和源 IP 保留。Cluster 更容易均衡到所有节点后端,但可能丢失客户端源 IP;Local 有利于保留源 IP,但要求接入节点本地有可用后端,否则可能出现流量黑洞。这个字段属于入口治理决策,不能只从“能不能访问”角度选择。

6. 后端工程场景#

测试环境的价值是让流量链路接近真实协作,而不只是单人本地跑通。这里要重点验证变更、回滚、故障注入和证据可见性。

6.1 用测试环境暴露 selector 与端口错误#

Service 错误最常见的两类是 selector 不匹配和端口不匹配。前者导致 EndpointSlice 为空,后者导致有 endpoints 但访问失败。测试环境应该主动覆盖这两类场景,而不是只测 happy path。

建议把检查写进发布前验证:Deployment 的 Pod label 与 Service selector 是否匹配,Service targetPort 是否对应容器端口,EndpointSlice 是否有 ready 地址,应用 /health 是否从集群内可访问。

这些检查不一定都要阻断发布,但至少应该形成自动化证据。否则一旦发布后 Ingress 503,团队仍然需要人工从头查 label、端口、ready 状态和 Controller 日志。

6.2 验证 Ingress 的 Host、Path 与 PathType#

Ingress 路由看似简单,实际很容易因为路径规则写法不同而产生差异。Exact 要求路径完全匹配,Prefix 按路径前缀匹配,不同 Controller 对 rewrite、尾斜杠、正则注解的支持也可能不同。

测试环境至少要覆盖域名不匹配、路径不匹配、命中多个规则、默认后端、后端 Service 不存在、后端 endpoints 为空等场景。这样线上出现 404 或 503 时,团队能快速判断是规则没命中,还是命中了规则但后端不可用。

对后端 API 来说,路径前缀还涉及应用自身的 base path。Ingress 把 /api/users 转给 Service 后,是否 rewrite 成 /users,要和应用路由约定一致。否则网关日志显示转发成功,应用仍然可能返回 404。

6.3 建立入口日志与应用日志的关联#

测试环境应尽早建立请求链路的可观测性。至少要能从 Ingress Controller 访问日志看到 Host、Path、状态码、上游 Service、上游地址、响应时间和 request id,并能在后端应用日志里找到同一个 request id。

没有这层关联时,入口 502/503/504 和应用 5xx 很容易互相甩锅。入口日志能告诉你请求是否到达 Controller、是否命中规则、是否转给上游;应用日志能告诉你请求是否进入进程、是否触发业务错误或依赖超时。

测试环境不是生产的简化玩具,而是生产排障流程的演练场。每次新增 Ingress、调整 Service 端口、修改 readiness,都应该能在测试环境里留下可复查证据。

7. 常见错误与排障#

Service 与 Ingress 的故障经常表面相似,但证据位置不同。把错误按现象拆开,可以显著缩短排查时间。

8.1 Service 没有 endpoints#

现象通常是 Ingress 503、Service 访问失败,或者 kubectl describe svc 显示 Endpoints 为空。第一怀疑对象是 Service selector 与 Pod labels 不匹配,其次是 Pod 未 ready。

证据链是:看 Service selector,看 Pod labels,看 EndpointSlice,看 Pod conditions。如果 selector 不匹配,修 Service 或 Pod 模板标签;如果 Pod 不 ready,继续查 readiness probe、容器日志、启动耗时和依赖可用性。

不要通过手工创建 Endpoints 来掩盖问题。除非你明确在接入外部服务或无 selector Service,否则 EndpointSlice 应该由控制器维护。手工修现场很容易在下一次发布时再次失效。

8.2 DNS 能解析但访问失败#

DNS 能解析只说明 Service 名称存在,并不代表后端可达。访问失败可能来自 EndpointSlice 为空、targetPort 错误、Pod 没有监听、NetworkPolicy 拦截、应用只绑定 localhost、readiness 未通过或后端进程崩溃。

排查时先从集群内临时 Pod 发起 curl -v,确认失败类型是连接拒绝、连接超时、TLS 错误还是 HTTP 状态码异常。连接拒绝更像端口未监听,超时更像网络路径或策略问题,HTTP 5xx 更可能已经到达应用或上游。

如果服务间调用使用短名称,例如 user-api,还要确认 namespace。跨 namespace 应使用 user-api.<namespace>.svc.cluster.local 或至少写明 namespace,否则可能解析到当前 namespace 下的同名 Service。

8.3 Ingress 404、503 与 TLS 异常#

Ingress 404 通常表示请求没有命中期望路由,或者被 Controller 的默认后端处理。优先检查 Host、Path、PathType、IngressClass、Controller 是否接管规则,以及是否存在 rewrite 造成的应用路由不匹配。

Ingress 503 通常表示规则命中了,但后端 Service 不可用或 endpoints 为空。此时不要继续纠结域名解析,而应检查 backend service 名称、service port、EndpointSlice、Pod ready 和 Controller 日志。

TLS 异常要分握手失败、证书不匹配和 HTTP 到 HTTPS 跳转问题。证据包括浏览器证书详情、openssl s_client 输出、Ingress TLS Secret、证书 SAN、Controller 日志和 DNS 解析。TLS 是入口边界问题,不应直接归因到后端业务代码。

8. 生产化边界#

生产环境的重点不是“暴露出去”,而是“以可治理、可观测、可回滚的方式暴露出去”。Service 与 Ingress 的配置要和安全、证书、容量、发布流程一起考虑。

8.1 ClusterIP、NodePort、LoadBalancer 的生产取舍#

后端内部服务通常使用 ClusterIP,配合集群 DNS 进行服务间调用。它简单、稳定,也避免把内部接口直接暴露到集群外。只有确实需要外部访问时,才继续叠加 Ingress、Gateway 或 LoadBalancer。

NodePort 更适合作为调试、实验或某些基础设施入口的底层机制,不适合作为直接面向用户的入口。它缺少七层治理,端口范围不友好,也容易绕开统一安全策略。

LoadBalancer 适合需要四层外部入口的场景,例如暴露 TCP 服务、Ingress Controller 的入口 Service,或特定云环境下的公网/内网负载均衡。对 HTTP/HTTPS API 来说,LoadBalancer 常常只是承载 Ingress Controller 的外部入口,而不是每个业务服务各自创建一个公网地址。

8.2 TLS、证书与域名边界#

Ingress 的 TLS 配置通常通过 Secret 引用证书和私钥。规则中的 tls.hosts 应与 rules.host、证书 SAN、DNS 解析保持一致。任何一处不一致,都可能表现为证书不匹配、握手失败或请求没有命中预期规则。

生产中建议把证书生命周期纳入平台治理,例如 cert-manager 自动签发和续期,避免人工上传证书过期。证书过期不是业务代码问题,但会直接造成全站访问失败,因此必须进入监控和告警。

还要明确 TLS 终止位置。常见做法是在 Ingress Controller 终止 TLS,然后以 HTTP 或 mTLS 转发到后端;也可以在外部网关终止,再转给集群入口。无论哪种方式,都要说清楚谁负责证书、谁记录访问日志、谁注入 X-Forwarded-* 头、后端如何识别真实协议和客户端 IP。

8.3 入口治理:超时、限流、鉴权与灰度#

生产入口不仅要转发请求,还要承载治理策略。超时、最大请求体、连接数、重试、限流、鉴权、CORS、WebSocket、灰度发布和访问日志,都可能由 Ingress Controller、API Gateway、Service Mesh 或云网关负责。

这些能力不应该隐含在零散注解里无人维护。团队需要明确哪些能力放在 Ingress,哪些放在 API Gateway,哪些由应用自己处理。否则同一个问题可能在多个层面重复配置,最终出现难以解释的行为。

灰度发布尤其要谨慎。Service 本身按后端 endpoints 负载均衡,不理解业务版本语义;Ingress 某些 Controller 可以通过权重、Header 或 Canary 注解做灰度,但具体语义依赖实现。生产灰度要配合指标、日志、回滚和版本标识,而不是只改一条路由规则。

9. 什么时候用,什么时候不用#

真正可维护的 Kubernetes 流量体系,靠的是约定、检查和证据链,而不是靠少数人记得命令。

9.1 建立从入口到 Pod 的排障顺序#

建议团队固定一条排障顺序:先确认用户请求的域名和路径,再看 DNS 和外部负载均衡器,再看 Ingress 是否命中规则,再看 backend Service,再看 EndpointSlice,再看 Pod ready 和容器端口,最后进入应用日志与依赖。

这条顺序的价值是避免跳层。比如 Ingress 503 时先查应用数据库,很可能浪费时间;Service endpoints 为空时重启 Controller,也通常不会解决问题。证据链能让团队在压力下仍然按层推进。

每次事故复盘时,也应该把根因标注到链路上的具体位置:Host/Path、IngressClass、Service port、selector、EndpointSlice、readiness、targetPort、应用监听、网络策略或外部负载均衡。分类稳定后,后续才能自动化。

9.2 用模板和 CI 固化字段约定#

Service 与 Ingress 的字段约定应沉淀进 Helm chart、Kustomize base、平台模板或 CI 检查中。不要依赖每个项目手写 selector、端口名、IngressClass、TLS Secret 名称和注解。

可以优先固化几类规则:Service selector 必须与 Deployment pod labels 一致;端口必须使用命名端口;Ingress backend 只能指向 Service port;公网入口必须配置 TLS;生产环境禁止业务服务直接使用 NodePort 暴露。

这些规则不一定都要一开始强制阻断,但至少要能在评审或流水线里提示。随着团队规模变大,手工约定会逐渐失效,模板和检查才是可复制能力。

9.3 把观测指标对齐到链路层级#

入口层应关注请求量、状态码、延迟、TLS 错误、上游连接失败和路由命中;Service/Endpoint 层应关注 endpoints 数量、ready 比例、Pod churn;应用层应关注业务错误、依赖超时、线程池、连接池和 JVM 指标。

告警也要按层设计。Ingress 5xx 升高不等于应用一定故障,Service endpoints 归零不等于 Controller 一定故障,应用健康检查失败不等于入口路由错误。告警信息最好直接带上 namespace、Ingress、Service、backend endpoint 和版本标识。

当指标、日志、事件和配置能按同一条链路关联起来时,Service 与 Ingress 就不再只是 YAML,而是后端流量治理的一套工程能力。

10. 下一篇衔接#

本篇解决的是后端服务“如何被访问”的问题:Service 提供稳定集群内入口,EndpointSlice 记录真实后端,Ingress 与 Controller 承载 HTTP/HTTPS 外部路由和入口治理。到这里,一个后端 API 已经能被副本化部署,也能被集群内外访问。

下一篇会进入 Kubernetes ConfigMap 与 Secret 配置治理。流量链路跑通之后,后端服务还需要解决配置如何注入、密钥如何管理、配置变更如何发布、敏感信息如何避免进入镜像和日志。Service 与 Ingress 关注请求怎么到达服务,ConfigMap 与 Secret 关注服务启动和运行时依赖什么配置。

11. 官方参考#

第 9 篇:Kubernetes Service 与 Ingress 后端流量
https://jupiter-ws.cn/posts/backend/container/09_kubernetes_service_ingress_backend_traffic/
作者
Jupiter
发布于
2026-07-05
许可协议
CC BY-NC-SA 4.0