业务迁移中最容易误判的一类问题,是“应用日志里写着 Nacos、Redis 或 Feign,所以故障一定在 Nacos、Redis 或代码”。实际上,故障常常发生在 Service 转发或跨节点网络环节。
系列导航:Kubernetes 重建迁移教程系列。本篇可以作为迁移期和日常值班的独立排障手册。
这篇文章提供一条固定排查路径。遇到故障时从上到下执行,不要一上来就重启所有 Pod。
先按错误类型缩小范围
| 错误 | 常见含义 |
|---|---|
UnknownHostException | DNS 名称不存在或 CoreDNS 不可用 |
Connection refused | 已到达目标主机,但端口没有监听或转发到错误端口 |
Connect timed out | 路由、防火墙、NetworkPolicy 或目标无响应 |
No route to host | 跨节点路由、防火墙或 CNI 地址段不通 |
Load balancer does not have available server | 注册中心无健康实例,或客户端未正确订阅 |
| HTTP 404 | 请求已到某个 HTTP 服务,但 Host、Path 或 Gateway 路由不匹配 |
错误只是线索,不是结论。
第 1 层:确认客户端实际使用什么地址
进入报错 Pod,查看环境变量和生效配置:
kubectl -n <namespace> exec <client-pod> -- env \
| grep -Ei 'nacos|redis|gateway|service'
若配置来自 Nacos,还要回读对应的 Data ID。不要只看 Git 中的配置,因为运行时可能被环境变量、启动参数或远程配置覆盖。
第 2 层:验证 DNS
kubectl -n <namespace> exec <client-pod> -- \
getent hosts <service>.<namespace>.svc.cluster.local
若镜像没有调试工具,启动临时诊断 Pod:
kubectl -n <namespace> run net-debug --rm -it --restart=Never \
--image=<approved-network-tool-image> -- sh
DNS 失败时检查:
kubectl -n kube-system get deployment,pod -l k8s-app=kube-dns -o wide
kubectl -n kube-system logs deployment/coredns --tail=200
kubectl -n <namespace> get networkpolicy
第 3 层:检查 Service 定义
kubectl -n <target-namespace> get service <service> -o yaml
核对:
- selector 是否与目标 Pod label 一致。
port是客户端访问端口。targetPort是否指向容器真实监听端口或正确的命名端口。- 是否误用了旧 ClusterIP。
立即对比标签:
kubectl -n <target-namespace> get pods --show-labels
kubectl -n <target-namespace> get pods -l '<service-selector>' -o wide
第 4 层:检查 EndpointSlice
kubectl -n <target-namespace> get endpointslice \
-l kubernetes.io/service-name=<service> -o yaml
如果没有地址,问题通常是 selector 不匹配或 Pod 未 Ready。如果有地址但 conditions.ready: false,检查 readiness probe。
记录一个 Ready 的 Pod IP 和端口,后续分别测试“直连 Pod”和“经过 Service”。
第 5 层:直连 Pod IP
从客户端 Pod 执行:
kubectl -n <namespace> exec <client-pod> -- \
nc -vz <target-pod-ip> <target-port>
结果判断:
- Pod IP 不通:优先查 CNI、节点路由、防火墙和 NetworkPolicy。
- Pod IP 通、ClusterIP 不通:优先查 kube-proxy 或 Service 规则。
- 两者都通:继续查应用协议、认证、Gateway 或 Nacos 注册数据。
第 6 层:在不同节点重复测试
Service 故障可能只发生在某个节点。列出客户端所在节点:
kubectl -n <namespace> get pod <client-pod> -o wide
分别在故障节点和健康节点启动临时 Pod,访问同一个 ClusterIP。如果只有一个节点失败,问题范围就已经缩小到该节点的 kube-proxy、CNI、iptables/nftables 或防火墙。
第 7 层:检查 kube-proxy
kubectl -n kube-system get pods -l k8s-app=kube-proxy -o wide
kubectl -n kube-system logs <kube-proxy-pod-on-bad-node> --tail=200
比较 ConfigMap 与运行模式:
kubectl -n kube-system get configmap kube-proxy -o yaml
只重启故障节点上的 kube-proxy:
kubectl -n kube-system delete pod <kube-proxy-pod-on-bad-node>
kubectl -n kube-system get pod -l k8s-app=kube-proxy -o wide -w
删除 DaemonSet Pod 会让控制器自动重建。不要在没有证据时同时删除所有节点的 kube-proxy。
第 8 层:检查节点路由和防火墙
登录故障节点:
ip route
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=trusted --list-sources
sudo sysctl net.ipv4.ip_forward
Pod CIDR 应被正确路由或纳入可信区域,net.ipv4.ip_forward 应为 1。
Calico 环境下继续检查:
kubectl -n kube-system get pod -l k8s-app=calico-node -o wide
kubectl -n kube-system logs <calico-pod-on-bad-node> -c calico-node --tail=200
第 9 层:最后再看 Nacos 和应用
如果网络层全部正常,但客户端仍提示没有可用服务,检查 Nacos 中:
- namespace、group 和 cluster 是否一致。
- 服务名大小写和前缀是否一致。
- 实例 IP 是否仍是旧集群 Pod IP。
- 实例健康状态和 ephemeral 状态。
- 客户端是否误启用了双写或兼容模式。
从目标应用 Pod 内部访问依赖的健康检查地址,比在运维机上访问更有意义。
用对照实验避免误判
最有效的排查表是:
| 测试位置 | Pod IP | ClusterIP | Service DNS | 结果 |
|---|---|---|---|---|
| 故障节点 Pod | <pod-ip> | <service-ip> | <service-fqdn> | 记录 |
| 健康节点 Pod | <pod-ip> | <service-ip> | <service-fqdn> | 记录 |
| 目标 Pod 本机 | localhost | 不适用 | 不适用 | 记录 |
这张表能快速区分应用端口、跨节点 CNI 和 Service 转发。
修复后的验收
kubectl -n <namespace> exec <client-pod> -- \
curl -fsS http://<service>.<target-namespace>.svc.cluster.local:<port>/<health-path>
kubectl -n <namespace> logs <client-pod> --since=10m \
| grep -Ei 'refused|timed out|no route|available server'
再执行一条真实业务请求。网络命令恢复正常只是第一层证据,最终必须验证 Gateway、登录、查询或消息消费等真实链路。
最终原则
DNS → Service → EndpointSlice → Pod 端口 → 跨节点网络 → kube-proxy → 防火墙 → 注册中心 → 业务
固定顺序排查的价值,是每一步都能排除一整层。重启所有 Pod 可能让症状暂时消失,却无法定位故障原因,也无法防止下一次节点漂移后再次发生。
DISCUSSION
讨论与反馈