业务迁移中最容易误判的一类问题,是“应用日志里写着 Nacos、Redis 或 Feign,所以故障一定在 Nacos、Redis 或代码”。实际上,故障常常发生在 Service 转发或跨节点网络环节。

系列导航:Kubernetes 重建迁移教程系列。本篇可以作为迁移期和日常值班的独立排障手册。

这篇文章提供一条固定排查路径。遇到故障时从上到下执行,不要一上来就重启所有 Pod。

先按错误类型缩小范围

错误常见含义
UnknownHostExceptionDNS 名称不存在或 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 IPClusterIPService 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 可能让症状暂时消失,却无法定位故障原因,也无法防止下一次节点漂移后再次发生。