适用场景
集群中的应用偶发报出 lookup xxx on 10.96.0.10:53: i/o timeout、SERVFAIL 或连接外部服务失败;重试后又恢复。故障通常集中在业务高峰,Pod 重建、扩容或切换节点后更明显。本文适用于使用 CoreDNS 作为集群 DNS,且 CoreDNS 需要转发外部域名查询的 Kubernetes 集群。
现象与影响范围
一次典型事故中,支付服务调用第三方 HTTPS 接口时约 1% 请求失败。应用日志显示:
dial tcp: lookup api.partner.example on 10.96.0.10:53: read udp 10.244.2.18:46157->10.96.0.10:53: i/o timeout
同一 Pod 立刻执行 nslookup 有时成功、有时超时;查询 kubernetes.default.svc.cluster.local 始终正常。这是重要分界:集群服务域名正常而外部域名异常,优先检查 CoreDNS 的 forward 链路、节点到上游 DNS 的网络和缓存策略,而不是先修改业务重试次数。
排查顺序
1. 在故障 Pod 中区分内部与外部解析
kubectl -n payments exec deploy/pay-api -- sh -c '
cat /etc/resolv.conf
nslookup kubernetes.default.svc.cluster.local
nslookup api.partner.example
'
nameserver 应指向集群 DNS Service IP。内部域名失败时,检查 CoreDNS Service、Endpoint 和 kube-proxy;仅外部域名失败时,继续检查转发链路。不要在业务 Pod 里临时替换 /etc/resolv.conf,这会掩盖集群问题并造成后续漂移。
2. 查看 CoreDNS 日志、负载和转发失败
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
kubectl -n kube-system logs deploy/coredns --since=15m | \
grep -E 'timeout|SERVFAIL|plugin/errors'
kubectl -n kube-system top pods -l k8s-app=kube-dns
kubectl -n kube-system get configmap coredns -o yaml
日志中的 plugin/errors: 2 ... i/o timeout 表示 CoreDNS 等待上游响应超时。观察 CoreDNS 的 CPU、内存和重启次数:CPU 持续接近限额会让 DNS 请求排队,即使上游 DNS 完全健康也会出现超时。
3. 从 CoreDNS Pod 验证上游 DNS 可达性
先读取 Corefile 中 forward . 的目标。若使用 /etc/resolv.conf,还需确认节点文件没有被错误地指向 127.0.0.53 等 Pod 网络无法访问的本地 stub resolver。
kubectl -n kube-system exec deploy/coredns -- sh -c '
cat /etc/resolv.conf
nslookup api.partner.example 10.0.0.53
'
将 10.0.0.53 替换为实际的上游 DNS。若 UDP 查询超时,再用 TCP 查询对比:
kubectl -n kube-system exec deploy/coredns -- sh -c \
'nslookup -vc api.partner.example 10.0.0.53'
UDP 失败但 TCP 成功,常见于节点防火墙、云安全组、MTU 分片或上游对 UDP 的限流;两者均失败则检查路由、网络策略和上游 DNS 健康。
修复方案
本次问题是业务流量突增时大量 Pod 同时查询同一批外部域名,上游 DNS 的 UDP 查询出现尾延迟,而 CoreDNS 缓存命中率不足。Corefile 调整为明确的上游地址、启用合理缓存,并启用健康检查:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
forward . 10.0.0.53 10.0.0.54 {
policy sequential
max_concurrent 1000
health_check 5s
}
cache 30 {
denial 5
}
loop
reload
loadbalance
}
cache 30 将成功响应最多缓存 30 秒,能吸收重复查询;denial 5 缩短 NXDOMAIN 缓存,避免刚创建的记录长时间不可见。max_concurrent 应大于峰值并发查询量,但不能无限增大,否则会把过载传递给上游 DNS。上游地址应至少两台,并按实际网络选择 sequential 或默认并发策略。
修改后执行滚动重启并观察恢复:
kubectl -n kube-system rollout restart deployment/coredns
kubectl -n kube-system rollout status deployment/coredns --timeout=120s
kubectl -n kube-system get endpoints kube-dns -o wide
不要直接删除全部 CoreDNS Pod;滚动重启能维持 DNS Endpoint 可用。若 CPU 已接近限额,优先增加副本并为每个副本设置可观测的资源请求和限额,再评估是否需要 CoreDNS autoscaler。
预防与监控
- 监控 CoreDNS 的请求速率、
SERVFAIL、响应码、P99 延迟、进程 CPU 和容器重启次数;将外部域名超时与上游 DNS 的延迟放在同一看板。 - 为关键外部域名设置合适 TTL,避免业务代码在每个请求路径中重复解析域名。
- 发布网络策略、防火墙或节点镜像前,在节点和 CoreDNS Pod 两个视角做 UDP/TCP 53 端口连通性验证。
- 压测或大规模扩容前,评估 DNS QPS 和上游 DNS 的容量;缓存命中率下降往往比平均延迟更早暴露风险。
总结
Kubernetes DNS 间歇失败不等同于 CoreDNS 服务异常。先用内部、外部域名对比确定故障边界,再从 CoreDNS 日志、资源、上游连通性和缓存命中率逐层取证。明确上游、合理缓存、滚动发布和可观测告警,能把偶发解析超时从依赖重试的隐患变成可定位、可治理的容量问题。
Discussion
评论