AI 协助说明: 本页由 Codex 协助核验和修复站内链接;正文的技术事实和适用范围未因本次修改而改变,仍以文中来源及当前官方文档为准。
阅读边界#
本文把 Service 当作一个 API 资源 来讲:如何用 selector、端口、类型、亲和与流量策略描述“哪些后端以什么入口被访问”,以及如何在对象层定位配置错误。
本文不逐包展开 DNS、ClusterIP、EndpointSlice、DNAT、kube-proxy 或 eBPF 的运行时实现。遇到“Service 存在但请求不通”时,请接着阅读 Service 转发链路。相关专题还有 CoreDNS 详解、kube-proxy 详解、Calico 流量走向 和 Pod 优雅退出。
结论:Service 是稳定入口的声明,不是业务代理进程#
Pod 的 IP、数量和所在节点会随发布、扩缩容、故障恢复而变化。Service 用一个稳定的名称和(多数情况下)稳定的虚拟 IP,表示一组网络后端及其访问策略:
Service spec
├─ selector:匹配哪些 Pod
├─ ports:入口端口如何映射到后端端口
├─ type / traffic policy:从哪里进入、哪些端点可以被选中
└─ EndpointSlice:控制器维护的实际后端快照Service 不会替应用做健康检查、认证、重试或七层路由;它也不是网络防火墙。应用是否接收新流量主要由 readiness 和 EndpointSlice 状态决定,访问许可则由 NetworkPolicy、CNI 与外部网络策略共同决定。
Service 资源模型#
一个常见的集群内 HTTP Service:
apiVersion: v1
kind: Service
metadata:
name: web
namespace: default
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: web
ports:
- name: http
protocol: TCP
appProtocol: http
port: 80
targetPort: http
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
internalTrafficPolicy: Cluster| 字段 | 解决的问题 | 要点 |
|---|---|---|
metadata.namespace/name | 对象作用域与 DNS 名称 | Service 只能选择同一 namespace 中的 Pod。 |
selector | 后端集合 | 标签匹配后,控制器会维护对应的 EndpointSlice。 |
ports | 入口与后端端口映射 | 见下一节;多端口 Service 应为每个端口命名。 |
type | 入口暴露方式 | 默认是 ClusterIP,其余类型在此基础上增加或改变入口语义。 |
clusterIP / clusterIPs | 虚拟入口地址 | 普通 Service 由集群分配;clusterIP: None 是 Headless Service。 |
sessionAffinity | 同一客户端是否尽量命中同一后端 | 仅是 Service 层的客户端 IP 亲和,不等同于应用会话。 |
internalTrafficPolicy / externalTrafficPolicy | 可选择端点的范围 | 是硬性范围约束,不是“尽量就近”的偏好。 |
trafficDistribution | 端点就近偏好 | 由版本和数据面支持情况决定;它表达偏好,不替代 traffic policy。 |
appProtocol 是给实现者和工具的应用协议提示,不会替代 protocol;protocol 决定 TCP、UDP 或 SCTP(集群支持时)的传输协议。
selector、Pod 与 EndpointSlice#
对带 selector 的 Service,EndpointSlice 控制器根据匹配的 Pod 自动创建、更新 EndpointSlice;正常情况下,未就绪的 Pod 不会成为常规新流量的可选后端。不要把旧的 Endpoints 对象当作完整事实来源:它已被弃用,并且在大量后端时可能被截断;排查和自动化应读取 EndpointSlice。
EndpointSlice 中最值得在对象层观察的是:
| 状态 | 含义 |
|---|---|
ready | 是否应作为常规新流量的可用端点。 |
serving | 终止中的端点是否仍在提供响应,便于连接排空。 |
terminating | 对应 Pod 已进入删除流程。 |
终止端点的状态传播与连接排空属于运行时话题;请看 Pod 优雅退出 和 Service 转发链路。
无 selector 的 Service#
Service 也可以不选择 Pod,例如后端是集群外地址或由自定义控制器维护。此时需自行创建 discovery.k8s.io/v1 的 EndpointSlice,并给它设置 kubernetes.io/service-name: <service-name> 标签。端点地址不能是另一个 Service 的 ClusterIP;Service VIP 不能再作为 Service VIP 的后端。
这是一种明确的运维接口,不应靠手改旧 Endpoints 对象实现。还要注意:无 selector 的 Service 无法通过 API Server 的 kubectl port-forward service/... 代理到任意外部端点。
port、targetPort 与 nodePort#
ports:
- name: http
protocol: TCP
port: 80
targetPort: http
nodePort: 30080| 字段 | 含义 | 常见误解 |
|---|---|---|
port | Service 自己暴露的端口 | 客户端访问的是 ServiceIP:port,不是容器监听端口。 |
targetPort | 后端端点接收流量的端口 | 可以是数字,也可以是 Pod 中命名端口;数字正确不代表应用一定在监听。 |
nodePort | NodePort / LoadBalancer 入口在节点上的端口 | 不是所有 Service 都需要它;默认范围通常为 30000-32767,但由集群配置决定。 |
name | Service 端口名 | 多端口 Service 必须可区分端口;命名 targetPort 也有利于滚动变更。 |
对于 targetPort: http,应让后端 Pod 的相应容器声明同名端口,例如:
containers:
- name: web
ports:
- name: http
containerPort: 8080这不会自动让进程监听 8080;应用仍需实际监听该端口。
Service 类型与使用决策#
| 类型或形态 | 入口语义 | 适合场景 | 关键边界 |
|---|---|---|---|
ClusterIP(默认) | 集群内虚拟 IP | 服务间调用 | 对外 HTTP/HTTPS 通常由 Ingress 或 Gateway 暴露。 |
NodePort | 每个节点 IP 上的同一端口,且通常仍有 ClusterIP | 调试、简单入口、外部 LB 后端 | 需考虑节点防火墙、安全组、端口范围和源 IP。 |
LoadBalancer | 请求基础设施实现外部负载均衡器 | 云或已部署 LB 控制器的生产入口 | 行为、费用、健康检查和是否分配 NodePort 取决于实现。 |
ExternalName | DNS 返回 externalName 的 CNAME | 给外部域名提供集群内别名 | 不创建 ClusterIP 或四层转发;HTTP Host 与 TLS SNI 可能和真实域名不匹配。 |
Headless(clusterIP: None) | DNS 直接返回后端地址,不分配普通 ClusterIP | StatefulSet、客户端自行发现/选择后端 | 它是 Service 形态而非独立 type;不要期待 Service VIP 负载均衡。 |
Headless Service 与 StatefulSet#
Headless Service 常和 StatefulSet 配合。StatefulSet 在 spec.serviceName 中引用它后,Pod 可以获得稳定的单体 DNS 名称:
<pod-name>.<service-name>.<namespace>.svc.cluster.local这适合数据库、副本集等需要识别具体成员的系统;客户端要自行处理返回的多个地址和故障切换。无状态 Deployment 的普通服务调用通常更适合使用 ClusterIP Service。
会话亲和与流量策略#
sessionAffinity#
可选值是 None(默认)和 ClientIP。后者使来自同一客户端 IP 的连接在超时时间内尽量选择同一后端:
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800它不能替代 Cookie、令牌或应用层一致性设计:经 NAT 后多个用户可能共用一个源 IP,连接重建、端点下线和数据面差异也会改变实际命中结果。
internalTrafficPolicy、externalTrafficPolicy 与 trafficDistribution#
| 配置 | 适用流量 | Cluster / 默认行为 | Local 行为与代价 |
|---|---|---|---|
internalTrafficPolicy | 集群内 Pod 发出的 Service 流量 | 可在整个集群的可用端点中选择 | 仅选择源 Pod 所在节点的端点;该节点没有端点时,对这个来源等同于没有后端。 |
externalTrafficPolicy | NodePort / LoadBalancer 的外部入口流量 | 可转发到其他节点的后端,源地址常会被改写 | 仅选择入口节点本地端点,通常有利于保留源 IP;需要让外部 LB 和健康检查只把流量送到有本地后端的节点。 |
trafficDistribution 是“优先选择拓扑上更近端点”的提示;不要把它和上表的硬限制混为一谈。使用它前应核对 Kubernetes 版本、所用 kube-proxy 或 eBPF 数据面是否支持,并观察 EndpointSlice 的拓扑信息。
Service 与常见工作负载/入口的关系#
| 对象 | 它负责什么 | 与 Service 的正确分工 |
|---|---|---|
| Deployment | 创建、更新、扩缩无状态 Pod | Deployment 管 Pod 副本,Service 用标签稳定地发现这些副本。二者没有直接从属关系。 |
| StatefulSet | 稳定序号、存储和身份 | 常用 Headless Service 做成员发现;需要统一入口时也可另建普通 Service。 |
| Ingress | HTTP/HTTPS 主机、路径与 TLS 路由 | Ingress Controller 通常把七层路由的后端指向 Service;Service 不取代 Ingress 的七层能力。 |
| NetworkPolicy | Pod 入/出站访问许可 | 策略通常作用于后端 Pod,而不是“给 Service 加防火墙”。要按 DNAT 后的真实 Pod 流量验证。 |
对象级排查:先确认声明与后端集合#
以下命令只验证 Kubernetes 对象是否表达正确;DNS、节点规则、CNI 路由和 NetworkPolicy 的逐层排查见 Service 转发链路。
1. 检查 Service 本身#
kubectl -n default get svc web -o wide
kubectl -n default describe svc web
kubectl -n default get svc web -o yaml
kubectl -n default get svc web -o jsonpath='{.spec.clusterIP}{"\n"}'确认 namespace、type、selector、port/targetPort/nodePort、clusterIP 以及两种 traffic policy 是否符合预期。LoadBalancer 还应查看 describe 中的事件和 status.loadBalancer。
2. 用同一组标签检查 Pod 与就绪状态#
kubectl -n default get pods -l app.kubernetes.io/name=web -o wide --show-labels
kubectl -n default get pods -l app.kubernetes.io/name=web最常见的错误是 Service selector 和 Deployment 模板标签不一致,或 Pod 尚未 Ready。此时应先修正标签、readinessProbe 或应用监听,不要先重启 kube-proxy。
3. 以 EndpointSlice 为准核对后端#
kubectl -n default get endpointslice -l kubernetes.io/service-name=web -o wide
kubectl -n default get endpointslice -l kubernetes.io/service-name=web -o yaml
# 仅用于兼容性观察;Endpoints API 已弃用,不应作为自动化依据
kubectl -n default get endpoints web核对地址、端口和 ready/serving/terminating。如果没有 EndpointSlice,优先检查 selector、Pod namespace、Pod labels 与 Ready 状态;如果地址存在而端口错误,检查 targetPort 和应用实际监听端口。
4. 按入口类型补充验证#
| 现象 | 对象层优先检查 |
|---|---|
| LoadBalancer 没有外部地址 | kubectl describe svc 的事件、LB 控制器/云配额、loadBalancerClass 与注解。 |
| NodePort 无法从外部进入 | nodePort 是否已分配,再转入节点防火墙、安全组和 externalTrafficPolicy 排查。 |
| Headless DNS 返回不符合预期 | clusterIP: None、selector、EndpointSlice 与 publishNotReadyAddresses。 |
| ExternalName 访问失败 | externalName 是否是真实域名,以及客户端的 DNS、HTTP Host、TLS SNI 是否匹配。 |
最小实践清单#
- 用稳定且专用的标签作为 Service selector,不要选择过宽的通用标签。
- 为多端口 Service 和 Pod 端口命名,减少
targetPort变更带来的发布风险。 - 为承接流量的应用设置 readinessProbe,并在发布/缩容时验证 EndpointSlice 变化。
- 默认从 ClusterIP 开始;只有明确需要时才选 NodePort、LoadBalancer 或 Headless。
- 把网络访问控制写在 NetworkPolicy 和基础设施安全组中,不把 Service 当作安全边界。
- 遇到“对象正确但访问失败”,停止在 Service YAML 上反复修改,按 运行时数据链路 分层定位。