来源:AI炼丹踩坑 | 发布日期:2026-06-11
K8s DNS 间歇性解析超时 5s 排查
一句话复盘: Pod 访问外部域名偶发
dial tcp: lookup api.example.com: i/o timeout,超时稳定卡在 5s(glibc resolver 默认 timeout)。根因并非 CoreDNS 繁忙,而是 DNS UDP 请求经过 kube-proxy iptables 转发时,conntrack 发生竞态丢包,加上 ndots:5 放大了请求量。组合方案:NodeLocal DNSCache + ndots 调优。
问题现象
线上接口 P99 突然多了一个 5 秒 的尖刺,随机慢、重试即好。应用日志:
dial tcp: lookup api.example.com: i/o timeout
排查链路
第一步:确认是 5s 超时(glibc 特征)
DNS 超时的 5 秒不是玄学 — glibc resolver 默认 timeout 就是 5 秒。Pod 内压测验证:
while true; do date; time getent hosts api.example.com; sleep 0.2; done
输出典型:
real 0m5.012s
稳定在 5s 附近 → 沿 Linux DNS resolver 链路排查。
第二步:检查 CoreDNS 是否真的繁忙
kubectl -n kube-system top pod -l k8s-app=kube-dns
kubectl -n kube-system describe pod coredns-xxx
CoreDNS CPU 很低,无 throttling,日志无错误。DNS 超时 ≠ CoreDNS 慢 — 请求可能丢在内核路径上,根本没到 CoreDNS。
第三步:节点抓包确认丢包
找到异常 Pod 所在节点,抓 DNS 流量:
tcpdump -i any -nn udp port 53
异常时现象(中间沉默约 5 秒):
10.244.1.23.45121 > 10.96.0.10.53: A? api.example.com
10.244.1.23.45121 > 10.96.0.10.53: AAAA? api.example.com
# ~5s 沉默
10.244.1.23.45122 > 10.96.0.10.53: A? api.example.com
第一次请求无响应,5s 后换源端口重试即成功 → 指向 conntrack 竞态。
第四步:看 conntrack 统计
conntrack -S | egrep 'insert_failed|drop|invalid'
dmesg | grep nf_conntrack
insert_failed 持续增长且与 DNS 超时峰值对齐,即可确认方向。注意区分:表满(table full, dropping packet)与竞态(insert_failed 增长但未满)是两种不同问题。
根因链
ndots:5 放大 DNS 请求量
↓
glibc 同时发起 A + AAAA 查询,同源端口竞争 conntrack entry
↓
conntrack 竞态 → 失败包被内核丢弃
↓
glibc 等待 5s 超时后重试(换源端口成功)
ndots:5 是帮凶。 K8s 默认 ndots:5 会让含少于 5 个点的域名先拼上 search domain 逐条尝试(如 api.example.com.default.svc.cluster.local),外部域名越多,conntrack 压力越大。
解决方案对比
| 方案 | 效果 | 成本 | 适用场景 |
|---|---|---|---|
| NodeLocal DNSCache | 高 | 中 | 中大规模集群、DNS QPS 高 |
| 调低 ndots | 中-高 | 低 | 外部域名访问多的服务 |
| 外部域名加尾点 | 中 | 低 | 配置可控的业务 |
| DNS over TCP | 中 | 中 | 少量核心服务止血 |
| 调大 conntrack | 中 | 低 | conntrack 表满 |
| 升级内核 | 高 | 高 | 节点体系可控 |
推荐组合(实战验证):
- 核心业务配置
ndots:2 - 高频外部域名加尾点(
api.example.com.) - 集群灰度上线 NodeLocal DNSCache
- 同步监控 conntrack 指标 + CoreDNS QPS
排查 Checklist
遇到类似问题按此顺序:
- ⏱
time getent hosts <domain>— 确认是否 5s - 📋
cat /etc/resolv.conf— 看 nameserver / search / ndots - 📊
kubectl -n kube-system top pod -l k8s-app=kube-dns— CoreDNS 资源 - 🔍
tcpdump -i any -nn udp port 53— 抓包看首次请求是否有响应 - 📈
conntrack -S | egrep 'insert_failed'— conntrack 竞态 - 🗄
dmesg | grep nf_conntrack— 表是否满
核心判断: CoreDNS 无压力 + 抓包首次请求无响应 + insert_failed 增长 → 别再盲目扩 CoreDNS,问题在 conntrack/UDP 链路。
关联页面
| 页面 | 关联点 |
|---|---|
| k8s-dns-iptables-troubleshooting | iptables 封禁 53 端口引发的另一例 K8s DNS 雪崩 |
| k8s-coredns-custom-domain-resolution | CoreDNS 自定义域名解析配置 |
| dns-troubleshooting-practical-guide | 通用 DNS 全链路排查方法论 |
| k8s-service-access-troubleshooting | K8s 服务访问排障十步工作流 |
| network-troubleshooting-order | 网络排障分层顺序 |