5月31日 16:34

Kubernetes Service 有什么作用?ClusterIP、NodePort 和 LoadBalancer 怎么选?

Kubernetes Service 的作用,是给一组会变化的 Pod 提供一个稳定访问入口。Pod IP 会随着重建、扩缩容、滚动发布而变化,客户端如果直接访问 Pod,很快就会遇到地址失效和负载不均的问题。Service 用 selector 找到后端 Pod,通过 EndpointSlice 记录真实后端,再由 kube-proxy 或 CNI 数据面把流量转发过去。理解 Service 的关键不是背类型,而是知道不同类型解决的是“集群内访问、节点端口暴露、云负载均衡、外部域名映射”这几类问题。

Service 如何找到后端 Pod

最常见的 Service 通过 selector 匹配 Pod 标签。只要 Pod 上有 app: web,它就会被加入 Service 对应的 EndpointSlice。Deployment 滚动更新时,新旧 Pod 会动态进出后端列表,Service 的 ClusterIP 和 DNS 名称保持不变。踩坑点是 selector 写错时 Service 仍然存在,但没有后端,访问表现通常是连接失败或超时。

yaml
apiVersion: v1 kind: Service metadata: name: web spec: type: ClusterIP selector: app: web ports: - name: http port: 80 targetPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: web image: nginx ports: - containerPort: 8080
bash
kubectl get svc web kubectl get endpointslice -l kubernetes.io/service-name=web kubectl describe svc web

四种 Service 类型怎么选

ClusterIP 是默认类型,只能在集群内部访问,适合微服务之间互调、内部数据库代理、队列服务等。它安全、简单,也最常用。边界是集群外不能直接访问,除非配合 Ingress、Gateway、端口转发或 VPN。

NodePort 会在每个节点上打开一个端口,外部可以通过 NodeIP:NodePort 访问。它适合临时测试、裸金属环境接入外部负载均衡器,默认端口范围通常是 30000-32767。取舍是暴露面更大,而且节点 IP 变化、端口冲突、安全组放行都要自己管,生产 HTTP 服务一般不直接把 NodePort 当最终入口。

LoadBalancer 会请求云厂商创建外部负载均衡器,再把流量转到 Service 后端。它适合云上对外暴露 TCP/UDP 服务,使用体验最好。边界是依赖 cloud-controller-manager 和云平台权限,裸金属集群默认不会自动得到外部 LB,通常要配 MetalLB 或云厂商插件。

ExternalName 不代理流量,只返回一个 CNAME,把集群内服务名映射到外部 DNS 名称。它适合把外部数据库、第三方 API 用统一的集群内域名表达出来。踩坑点是它没有 ClusterIP,也没有端口转发和健康检查能力,排障时不要去找 Endpoint。

yaml
apiVersion: v1 kind: Service metadata: name: external-db spec: type: ExternalName externalName: db.example.com

kube-proxy 在中间做什么

kube-proxy 监听 Service 和 EndpointSlice 变化,在节点上维护转发规则。iptables 模式简单稳定,但规则很多时更新成本会上升;IPVS 模式适合更大规模,支持更丰富的负载均衡算法。现在不少 CNI 也会用 eBPF 接管 Service 数据面,这时 kube-proxy 可能被替代。边界要看集群实现,不能只凭“Service 不通”就断定 kube-proxy 有问题。

Headless Service 适合什么场景

Headless Service 设置 clusterIP: None,不会分配虚拟 IP,而是让 DNS 直接返回后端 Pod 地址。它常用于 StatefulSet,比如数据库、消息队列这类需要稳定 Pod 身份的应用。取舍是客户端要能处理多个后端地址和连接策略,不能再完全依赖 Service 做统一负载均衡。

yaml
apiVersion: v1 kind: Service metadata: name: mysql spec: clusterIP: None selector: app: mysql ports: - port: 3306

追问

ClusterIP、NodePort、LoadBalancer 的关系是什么?

LoadBalancer 通常会包含 NodePort,NodePort 又建立在 ClusterIP 之上,所以它们不是完全割裂的三套机制。ClusterIP 解决集群内稳定访问,NodePort 把入口扩到节点端口,LoadBalancer 再借助云厂商或外部 LB 提供公网或内网入口。取舍是越往外暴露,运维边界越大,安全组、证书、源地址保留和费用都要考虑。内部服务不要为了“方便”直接用 LoadBalancer。

Service selector 写错会发生什么?

Service 对象会创建成功,DNS 也可能正常解析,但 EndpointSlice 为空。客户端访问时通常表现为连接被拒绝、超时或没有可用后端。排查时先执行 kubectl get endpointslice -l kubernetes.io/service-name=<svc>,再核对 Pod 标签和 Service selector。这个坑很常见,因为 YAML 校验不会知道你的业务标签是否写对。

什么时候用 Ingress,而不是直接用 LoadBalancer?

如果是 HTTP/HTTPS,多服务共享域名、路径路由、TLS 证书和灰度规则,用 Ingress 或 Gateway API 通常更合适。每个 Service 都建一个 LoadBalancer 简单直接,但成本高、入口分散,也不好统一做证书和访问控制。边界是非 HTTP 协议不一定适合 Ingress,需要看控制器是否支持 TCP/UDP 转发。生产里常见做法是一个 Ingress Controller 前面挂一个 LoadBalancer。

sessionAffinity 能解决所有会话保持问题吗?

不能。sessionAffinity: ClientIP 只能按客户端 IP 做相对简单的粘性会话,NAT、代理和移动网络会让多个用户看起来来自同一个 IP。它也不理解应用层登录态,Pod 重启后会话仍可能丢失。更稳的做法是把会话放到 Redis、数据库或外部状态存储里。Service 层会话保持可以作为补充,不应该成为唯一依赖。

Service 不通时优先排查哪几步?

先查 Service 是否有 ClusterIP 和端口,再查 EndpointSlice 是否有后端地址。然后进同命名空间 Pod 里用 curlnc 测 Service DNS、ClusterIP 和 Pod IP,区分是 DNS、Service 转发还是应用端口问题。NodePort 或 LoadBalancer 不通时,还要检查节点安全组、云负载均衡器健康检查和 externalTrafficPolicy。不要只看 Service YAML,真正的线索通常在 EndpointSlice、事件和后端 Pod readiness。

标签:Kubernetes