讲解
Pod 的 IP 有三个不可靠:重建后变、多副本有多个、扩缩容时增减。直接在配置里写 Pod IP 是行不通的。Service 为一组 Pod 提供一个稳定的访问入口:一个固定的虚拟 IP(ClusterIP)和一个 DNS 名字,后端 Pod 怎么变,入口不变。可以把 Service 理解为「集群内的负载均衡器 + 服务注册表」。
Service 和后端 Pod 的关联靠的是标签选择器:Service 的 spec.selector 匹配到的所有 Pod 自动成为它的端点(Endpoints),Pod 的就绪探针失败时会被摘出端点列表,恢复后自动加回——这就是 readiness 探针「摘流量」的实现位置。访问路径是:客户端 → Service 的 ClusterIP:port → kube-proxy 维护的转发规则 → 某个健康 Pod 的 targetPort。port 是 Service 自己的端口,targetPort 是容器的端口,两者可以不同。
服务发现有两种方式,优先用 DNS:集群内的 CoreDNS 会给每个 Service 注册域名,同命名空间直接用 Service 名(如 ohmydocs-verify-web),跨命名空间用 服务名.命名空间(如 ohmydocs-verify-web.ohmydocs-verify),完整形式是 服务名.命名空间.svc.cluster.local。另一种是环境变量(kubelet 自动注入 服务名_SERVICE_HOST 等),但有「先于 Pod 创建的 Service 才注入」的顺序坑,基本只作了解。ClusterIP 是默认类型,只在集群内可达;对外暴露是后面 NodePort 和 Ingress 章节的事。
示例
一个 Deployment 加上暴露它的 ClusterIP Service(selector 匹配 Pod 的 app 标签,targetPort 指向容器的 80):
# wait: deploy/ohmydocs-verify-web
apiVersion: apps/v1
kind: Deployment
metadata:
name: ohmydocs-verify-web
spec:
replicas: 3
selector:
matchLabels:
app: ohmydocs-verify-web
template:
metadata:
labels:
app: ohmydocs-verify-web
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: ohmydocs-verify-web
spec:
type: ClusterIP
selector:
app: ohmydocs-verify-web
ports:
- port: 80
targetPort: 80
Service 拿到了稳定的 ClusterIP,Endpoints 里挂着 3 个健康 Pod 的 IP:
kubectl get svc ohmydocs-verify-web
kubectl get endpoints ohmydocs-verify-web
起一个客户端 Pod,验证集群内服务发现:DNS 解析 Service 名 + 通过 Service 名访问后端(多访问几次,响应可能来自不同副本):
kubectl run ohmydocs-verify-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl wait --for=condition=ready pod/ohmydocs-verify-client --timeout=120s
kubectl exec ohmydocs-verify-client -- nslookup ohmydocs-verify-web | tail -3
kubectl exec ohmydocs-verify-client -- wget -qO- http://ohmydocs-verify-web/ | head -2
kubectl delete pod ohmydocs-verify-client --wait
常见坑
- selector 和 Pod 标签对不上:Service 创建成功但 endpoints 是空的,访问超时;kubectl get endpoints 没地址时第一件事就是核对标签。
- targetPort 写错:流量打到容器没监听的端口,表现为连接拒绝;targetPort 必须对应容器实际监听的端口。
- 依赖 Pod IP 的环境变量注入:服务发现用 DNS,别用自动注入的环境变量——它有创建顺序依赖,且 Pod 重建后变量不更新。
- 以为 ClusterIP 能外部访问:ClusterIP 是集群内部虚拟 IP,宿主机和外部都不可达;对外用 NodePort/Ingress(下两章)。
小结
Service = 一组 Pod 的稳定入口(固定 ClusterIP + DNS 名),靠标签选择器自动维护端点,readiness 探针控制端点上下线;集群内访问直接用 Service 名。下一章把这个入口暴露到集群之外。