外观
第八节 Service、服务发现与外部访问
约 1412 字大约 5 分钟
KubernetesServiceEndpointSliceCoreDNS
2026-07-28
本课目标
为 yunfan-web 建立稳定的 ClusterIP 与 DNS 名称,核对 selector、EndpointSlice 和就绪 Pod;通过 NodePort 与 kind 端口映射从本机访问,并比较 Headless、ExternalName、会话保持和 port-forward 的边界。
8.1 接单:Pod IP 会变,客户端应该连接谁
Deployment 替换 Pod 后,Pod 名称与 IP 都可能变化。把客户端直接绑定到 Pod IP,会让每次扩缩容和自愈都变成地址变更。
Service 为一组后端提供稳定入口;控制平面根据 selector 生成 EndpointSlice,集群 DNS 再为 Service 建立名称。
Service 如何找到后端
固定排错顺序:Service → selector → EndpointSlice → Pod。
1 / 5
固定排错顺序:
Service 字段 → selector → EndpointSlice → Ready Pod → 网络与 DNS新版 Kubernetes 应优先检查 EndpointSlice;旧的 Endpoints API 从 Kubernetes v1.33 起已弃用,不作为本课主要证据。
8.2 ClusterIP:先建立集群内稳定入口
Service selector 必须匹配第 6 课 Deployment 的 app.kubernetes.io/name: yunfan-web。EndpointSlice 是保存后端地址和就绪条件的当前 API。
执行:
kubectl apply -f manifests/08-service/yunfan-web-services.yaml
kubectl get service -n yunfan-shop
kubectl get endpointslice -n yunfan-shop \
-l kubernetes.io/service-name=yunfan-web \
-o wide
kubectl get pod -n yunfan-shop \
-l app.kubernetes.io/name=yunfan-web \
-o wideEndpointSlice 中就绪地址数量应与可用 Web Pod 对应。sessionAffinity: ClientIP 让同一客户端 IP 的连接尽量保持同一后端,不等于用户登录会话已可靠持久。
8.3 DNS:用名称而不是虚拟 IP
创建一次性诊断 Pod:
kubectl run netcheck \
-n yunfan-shop \
--image=busybox:1.36.1 \
--restart=Never \
--command -- sleep 3600
kubectl wait -n yunfan-shop --for=condition=Ready pod/netcheck --timeout=120s
kubectl exec -n yunfan-shop netcheck -- nslookup yunfan-web
kubectl exec -n yunfan-shop netcheck -- wget -qO- http://yunfan-web同命名空间可使用短名称 yunfan-web;跨命名空间应至少写 yunfan-web.yunfan-shop,完整名称是 yunfan-web.yunfan-shop.svc.cluster.local。
DNS 通过能证明什么
它证明诊断 Pod 能访问 CoreDNS,CoreDNS 能生成 Service 记录,并且 Service 可把 HTTP 请求送到后端。它不能证明外部访问或公网 DNS。
8.4 NodePort 与 kind 端口映射
kind 中的外部访问链路
区分宿主机端口、NodePort、Service 与 Pod 端口。
1 / 5
第 2 课已经把宿主机 127.0.0.1:18080 映射到节点容器 30080;本课 NodePort 也固定为 30080,完整链路才一致。
从个人电脑执行:
curl -i http://127.0.0.1:18080若没有保留该端口映射,使用临时 port-forward:
kubectl port-forward -n yunfan-shop service/yunfan-web 18080:80另开终端访问 http://127.0.0.1:18080,完成后按 Ctrl+C 停止。port-forward 依赖当前终端连接,适合诊断,不是生产入口。
外部访问边界
NodePort 加 kind 映射只适合本机实验。LoadBalancer 需要云提供商或本地控制器。externalIPs 从 Kubernetes v1.36 起已弃用,本课只说明原理,不创建依赖该字段的新方案。
8.5 Headless 与 ExternalName
Headless Service 把 clusterIP 设为完整值 None,不分配虚拟 IP,也不由 kube-proxy 做负载均衡;DNS 直接返回后端地址。ExternalName 只返回 CNAME,不创建代理和 EndpointSlice。
验证 DNS 记录:
kubectl apply -f manifests/08-service/discovery-variants.yaml
kubectl exec -n yunfan-shop netcheck -- nslookup yunfan-web-headless
kubectl exec -n yunfan-shop netcheck -- nslookup public-docsExternalName 对 HTTP Host 与 TLS 证书可能产生不匹配,本课不把 DNS CNAME 成功解释为应用协议一定可用。
8.6 故障演练:selector 不匹配
临时把 ClusterIP Service selector 改成不存在的值:
kubectl patch service yunfan-web \
-n yunfan-shop \
--type=merge \
-p '{"spec":{"selector":{"app.kubernetes.io/name":"wrong-web"}}}'
kubectl get service yunfan-web -n yunfan-shop -o yaml
kubectl get endpointslice -n yunfan-shop \
-l kubernetes.io/service-name=yunfan-webDNS 仍可解析 Service 名称,但 EndpointSlice 没有可用后端,HTTP 请求失败。恢复方式:
kubectl apply -f manifests/08-service/yunfan-web-services.yaml
kubectl get endpointslice -n yunfan-shop \
-l kubernetes.io/service-name=yunfan-web这证明“Service 存在”和“Service 有后端”是两个验收点。
8.7 排错矩阵
| 现象 | 首查 | 常见根因 |
|---|---|---|
| Service 名称无法解析 | CoreDNS、Pod resolv.conf | DNS 组件或命名空间错误 |
| 能解析但连接拒绝 | EndpointSlice、targetPort | 后端为空或端口错误 |
| EndpointSlice 为空 | Service selector、Pod labels | 标签不匹配 |
| 集群内可达、本机不可达 | NodePort、extraPortMappings | 端口链不一致 |
| port-forward 成功、NodePort 失败 | 节点映射 | kind 配置未预留端口 |
| ExternalName 解析但 HTTPS 失败 | CNAME、Host、证书名 | 应用协议主机名不匹配 |
8.8 交付
- 两份 Service YAML。
- 一张内部访问截图:Service、EndpointSlice、DNS 和 Pod 内 wget。
- 一张外部访问截图:本机 curl 与端口链说明。
- 一段 selector 故障的“空端点—恢复”记录。
清理诊断 Pod,保留 Service 与 Web Deployment:
kubectl delete pod netcheck -n yunfan-shop本章小测
本章小结
- Service 用 selector 关联后端,EndpointSlice 保存当前地址与就绪条件。
- DNS 提供稳定名称,ClusterIP 提供集群内虚拟入口。
- kind 外部访问需要宿主机映射、NodePort 与 targetPort 逐跳一致。
- Headless 通过 DNS 返回后端地址,ExternalName 只创建 CNAME。
- 排错顺序固定为 Service、selector、EndpointSlice、Pod、网络/DNS。
