# 核心心智模型
Service 只是空壳,Endpoints 才是灵魂
初学者一般会认为流量是发给 Service,然后再由 Service 发给 Pod,这是错的。
在 Linux 网络栈的底层,Service 分配的 ClusterIP 其实是一个"假 IP",它连真实的网卡都没有。
真正的流程是:
- CoreDNS 负责把域名翻译成这个虚拟的 ClusterIP。
- K8s 控制台会在后台默默维护一个叫 Endpoints 的列表,里面记录了真实 Pod 的物理 IP。
- 节点上的 kube-proxy 会读取 Endpoints 列表,并在 Linux 内核写入底层的转发规则。流量打到虚拟 IP 后,在操作系统底层就被直接 NAT 转发到了真实的 Pod IP 上。
# 实验过程
# 实验准备
我们将部署一个目标 Web 服务,以及一个专门用于网络抓包和测试的 Pod。
-
创建目标服务
kubectl create deployment web-target --image=nginx kubectl expose deployment web-target --port=80 --name=web-svc -
部署网络排查 Pod
kubectl run net-tools --image=nicolaka/netshoot -- sleep 3600(等待两个 Pod 都进入 Running 状态)
# 阶段一:DNS 解析深潜
-
进入网络排查 Pod 内部:
kubectl exec -it net-tools -- bash -
使用 nslookup 查询 Service 的域名
nslookup web-svc预期输出:会看到不仅解析出了 web-svc 的 ClusterIP,还打印出了完整限定域名(FQDN):web-svc.default.svc.cluster.local。
-
查看容器的 DNS 配置文件
为什么只敲 web-svc 这个名字就能通网络?
cat /etc/resolv.conf会看到 :
net-tools:~# cat /etc/resolv.conf search default.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5search default.svc.cluster.local svc.cluster.local表示填补的后缀,当请求web-svc时,Linux 底层会自动帮你把这些后缀补齐,然后发给nameserver(这就是 CoreDNS 的 IP)。ndots决定了 DNS 什么时候去尝试补全域名后缀。如果你查询的域名中,包含的“点号”数量 小于ndots的设定值,系统会认为这不是一个完整的域名(FQDN),会优先尝试加上search列表里的后缀去查。
退出侦察机,回到宿主机终端)
Bash
exit
# 阶段二:Endpoints 机制与 “Connection Refused”案发现场
这是 CKA 最经典的排错场景:开发人员抱怨“域名能通,但连接被拒绝”。 我们来手动制造一场惨案。
1.查看当前健康状态
kubectl get endpoints web-svc
2. 制造破坏(修改 Service 标签):
我们将 Service 寻找后端 Pod 的“规则”故意改错。
kuebctl set selector svc web-svc wrong:label
这里之所以不用patch 命令,这是因为 Kubernetes 默认的合并策略是增量更新。在 Kubernetes 的 Selector 逻辑中,多个标签之间是 “AND(且)” 的关系。
查看修改后的 web-svc Endpoints 的 selector 标签:
kubectl get svc web-svc -o jsonpath='{.spec.selector}'
3. 确认破坏结果
kubectl get endpoints web-svc
预期现象: ENDPOINTS 变成了 <none>!此时
4. 模拟客户端访问,重现经典报错
kunectl exec -it net-tools -- curl -m 3 web-svc
预期现象: curl: (7) Failed to connect to web-svc port 80: Connection refused
深度解析:为什么是 Conection refused?
当执行 curl 时,DNS 依然完美地把 web-svc 解析成 ClusterIP,因为 Service 还在。但当网络包带着这个 ClusterIP 进入 Linux 网络栈时,iptables 发现这个虚拟 IP 背后没有任何物理 Endpoints 支撑,于是内核直接向客户端返回了 TCP RST 包,导致连接被拒绝。
# 阶段三:抢救与恢复
在考场上遇到 Connection refused 或者访问不通,第一反应永远是:检查 Service 的 Selector 和 Pod 的 Label 是否完美匹配!
1. 查看 Pod 的真实标签:
kubectl get pods --show-labels grep web-target
(假设输出显示标签是 app=web-target)
2. 修复 Service
你可以用 kubectl edit svc web-svc 把 selector 改回来,或者用命令行直接打补丁:
kubectl set selector svc web-svc app=web-target
3. 见证 Endpoints 归位与网络恢复
kubectl get endpoints web-svc
kubectl exec -it net-tools -- curl -s -o /dev/null -w "%{http_code}\n" web-svc
(看到 200,说明流量重新打通!)
# 清理战场
kubectl delete deployment web-target
kubectl delete svc web-svc
kubectl delete pod net-tools