Kubernetes 集群内 DNS 超时导致服务偶发访问失败的排查

适用场景

本文适用于 Kubernetes 集群中应用访问 Service、数据库、Redis、外部域名时,偶发出现 DNS 解析超时、连接超时或请求延迟突然升高的场景。常见环境包括使用 CoreDNS、kube-proxy、containerd 或 Docker 运行时的生产集群,尤其是业务高峰期、节点扩容后、服务实例频繁发布后更容易暴露问题。

现象描述

业务侧通常不会直接报出 CoreDNS 故障,而是表现为以下几类现象:

  • 应用日志间歇出现 lookup xxx on 10.96.0.10:53: read udp ... i/o timeout
  • Java、Go、Python 服务访问内部 Service 偶发超时,重试后恢复
  • 同一个接口 P99 延迟突然升高,但 CPU、内存、数据库指标看起来正常
  • 部分 Pod 正常,部分 Pod 异常,异常 Pod 多集中在少数节点
  • CoreDNS Pod 没有 CrashLoopBackOff,但 QPS、延迟或错误数异常

这类问题的难点在于它经常是间歇性的。业务看到的是 HTTP 502、504、连接超时,真正的根因可能藏在 DNS 查询链路里。

可能原因

常见原因可以分为五类:

  1. CoreDNS 资源不足。CPU 被打满后,DNS 查询排队,应用侧看到解析慢或超时。
  2. CoreDNS 副本数不足。集群规模变大后仍只有两个副本,单个 Pod 承担过多请求。
  3. 节点本地网络异常。某些节点到 CoreDNS Service IP 或 CoreDNS Pod IP 不通,导致只有这些节点上的业务受影响。
  4. 上游 DNS 慢。CoreDNS 转发外部域名到上游 DNS 时,上游响应慢或丢包。
  5. 应用 DNS 查询放大。短 TTL、频繁新建连接、搜索域过多、ndots 配置不合理,导致一次访问触发多次 DNS 查询。

排查思路

排查时不要只看 CoreDNS Pod 是否 Running,Running 只能说明进程还活着,不能说明解析链路健康。建议按下面顺序缩小范围:

  1. 先确认是内部域名异常还是外部域名异常。
  2. 再确认异常是否集中在某些业务 Pod 或某些节点。
  3. 检查 CoreDNS 的 CPU、内存、日志和副本数。
  4. 从异常 Pod 内部直接测试 DNS 解析延迟。
  5. 检查 CoreDNS 到上游 DNS 的转发情况。
  6. 最后再看应用连接池、重试和 DNS 缓存配置。

常用命令

查看 CoreDNS Pod 状态:

kubectl -n kube-system get pod -l k8s-app=kube-dns -o wide

关键字段看 READYSTATUSRESTARTSNODE。如果 RESTARTS 增长,说明 CoreDNS 进程可能被 OOM、探针失败或异常退出。如果多个 CoreDNS Pod 落在同一节点,节点故障时影响面会变大。

查看 CoreDNS Service:

kubectl -n kube-system get svc kube-dns -o wide

重点看 CLUSTER-IP,这个地址通常会写入 Pod 的 /etc/resolv.conf,例如 10.96.0.10。后续在业务 Pod 中测试时,要确认请求是否真的打到了这个 DNS 地址。

查看 CoreDNS 资源使用:

kubectl -n kube-system top pod -l k8s-app=kube-dns

重点看 CPU 是否长期接近 request 或 limit。如果 CoreDNS CPU 被限制,DNS 查询延迟会明显升高。内存持续上涨则要关注是否有异常插件、日志量或查询压力。

查看 CoreDNS 日志:

kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200

重点搜索 timeoutSERVFAILplugin/errorsno such host。如果大量外部域名解析失败,优先检查上游 DNS。如果内部 Service 解析失败,重点检查 Kubernetes API、CoreDNS 配置和服务对象。

进入异常 Pod 测试内部 Service 解析:

kubectl exec -it <pod-name> -n <namespace> -- sh
nslookup kubernetes.default.svc.cluster.local
nslookup <service-name>.<namespace>.svc.cluster.local

关键看返回是否稳定、是否出现超时、解析耗时是否明显偏高。内部域名异常时,问题通常在集群 DNS 链路内。

测试外部域名解析:

kubectl exec -it <pod-name> -n <namespace> -- sh
time nslookup www.baidu.com
time nslookup www.qq.com

如果内部域名正常、外部域名慢,重点看 CoreDNS 的 forward 配置和上游 DNS 连通性。

查看 Pod DNS 配置:

kubectl exec -it <pod-name> -n <namespace> -- cat /etc/resolv.conf

重点看 nameserversearchoptions ndotsndots:5 是 Kubernetes 常见默认值,访问短域名时可能先拼接多个搜索域再查询,放大 DNS 请求量。

查看 CoreDNS 配置:

kubectl -n kube-system get configmap coredns -o yaml

重点看 Corefile 中的 forwardcacheloopreadyprometheus 配置。cache 可以降低重复查询压力,forward 指向的上游 DNS 决定外部域名解析质量。

定位示例

某业务在高峰期偶发访问 Redis 超时,应用日志出现:

lookup redis.default.svc.cluster.local on 10.96.0.10:53: read udp 10.244.18.32:42531->10.96.0.10:53: i/o timeout

第一步,确认 CoreDNS 状态:

kubectl -n kube-system get pod -l k8s-app=kube-dns -o wide

发现两个 CoreDNS Pod 都 Running,没有重启,但都运行在同一个节点上。

第二步,查看资源:

kubectl -n kube-system top pod -l k8s-app=kube-dns

发现 CoreDNS CPU 在高峰期接近 limit,说明解析请求可能在排队。

第三步,从异常业务 Pod 内测试:

kubectl exec -it order-api-6d7c8f9d9b-abcde -n prod -- sh
time nslookup redis.default.svc.cluster.local
cat /etc/resolv.conf

结果显示多次解析耗时超过 2 秒,并且 /etc/resolv.conf 中有较长的 search 列表和 options ndots:5。应用访问 redis 这种短域名时,会先尝试多个搜索域组合,再查最终域名,造成额外查询。

第四步,检查 CoreDNS 日志:

kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200 | grep -E "timeout|SERVFAIL|plugin/errors"

日志中没有大量内部域名失败,但高峰期延迟明显,最终判断为 CoreDNS 副本不足叠加应用短域名查询放大。

修复方案

可以按风险从低到高逐步处理。

首先扩容 CoreDNS:

kubectl -n kube-system scale deployment coredns --replicas=4

扩容后观察 CoreDNS CPU 和业务超时是否下降。生产环境建议结合集群节点数和 DNS QPS 调整副本数,并使用反亲和性避免副本集中在同一节点。

其次调整 CoreDNS 资源限制:

kubectl -n kube-system edit deployment coredns

适当提高 CPU request 和 limit。CoreDNS 对 CPU 比较敏感,CPU 被 throttling 后,业务侧会直接感知到解析延迟。

再次,优化应用访问地址。内部服务尽量使用完整域名:

redis.default.svc.cluster.local

不要在高频请求路径里频繁解析短域名。对 Java、Go、Node.js 等应用,还要检查连接池复用和 DNS 缓存策略,避免每次请求都新建连接并触发解析。

如果外部域名解析慢,要检查 CoreDNS forward 的上游 DNS:

kubectl -n kube-system get configmap coredns -o yaml

可以将不稳定的上游 DNS 替换为企业内可用的递归 DNS,并保证节点到上游 DNS 的 UDP/TCP 53 端口通畅。

如果异常集中在某些节点,继续检查节点网络:

kubectl get pod -A -o wide | grep <node-name>
kubectl describe node <node-name>

重点看节点是否有网络插件异常、conntrack 表满、丢包、DNS Service IP 访问异常等问题。

预防措施

  • 为 CoreDNS 配置合理的 request、limit 和副本数,不要长期使用默认值跑大集群。
  • 为 CoreDNS 配置 Pod 反亲和性,避免多个副本落在同一节点。
  • 在监控中加入 CoreDNS QPS、错误数、P99 延迟、CPU throttling 指标。
  • 业务应用尽量复用连接池,避免短连接放大 DNS 压力。
  • 高频访问内部服务时使用完整 Service 域名,减少 search 域查询次数。
  • 对上游 DNS 做可用性监控,避免外部域名解析慢拖垮业务请求。
  • 发布新服务或扩容节点后,观察 CoreDNS 负载是否同步增长。

总结

Kubernetes DNS 问题经常被误判为应用超时、Nginx 502、数据库慢或网络抖动。实际排查时,要从业务日志中的 lookupi/o timeoutSERVFAIL 等关键字入手,再结合 CoreDNS 状态、资源、日志、Pod 内解析测试和节点分布逐步定位。

处理这类问题的核心不是只把 CoreDNS 重启一遍,而是确认 DNS 查询链路哪里慢、为什么慢、是否被应用查询模式放大。稳定的 CoreDNS 副本、合理的资源配置、可观测的 DNS 指标和规范的服务访问方式,才是减少线上 DNS 超时的关键。