我不会起名字322 · 后端 / 算法 / 数据库
文章目录
- [K8s 服务 DNS 解析偶发 5 秒超时:conntrack 与 ndots 的 3 个坑](#K8s 服务 DNS 解析偶发 5 秒超时:conntrack 与 ndots 的 3 个坑)
-
- [一、先搞清 5 秒从哪来](#一、先搞清 5 秒从哪来)
- [二、第 1 个坑:ndots:5 把一次解析放大成 5 次](#二、第 1 个坑:ndots:5 把一次解析放大成 5 次)
- [三、第 2 个坑:search 域列表过长](#三、第 2 个坑:search 域列表过长)
- [四、第 3 个坑:conntrack 表被打满](#四、第 3 个坑:conntrack 表被打满)
- 五、把三个坑串成排查链
- 六、总结
K8s 服务 DNS 解析偶发 5 秒超时:conntrack 与 ndots 的 3 个坑
"为什么偏偏是 5 秒?"
这是我第一次看到这个故障时的疑问。业务方反馈:偶发请求要 5 秒才返回,日志里是 lookup user-svc.default.svc.cluster.local: i/o timeout。不是 1 秒,不是 30 秒,雷打不动的 5 秒。
这个 5 秒数字本身就是线索。这篇拆 3 个坑:ndots:5 的放大效应、search 域列表过长、conntrack 表打满。
一、先搞清 5 秒从哪来
DNS 解析超时的"5 秒"不是随便来的,它是内核 DNS 重试机制的结果:
- glibc/musl 的 resolver 默认
timeout:5(每次查询等 5 秒)、attempts:2; - 一次查询发 UDP 包没响应 → 等 5 秒 → 重试;
- 如果
ndots导致串行查询多个域名,每个都可能等满 5 秒。
所以看到"恰好 5 秒",几乎可以锁定是 DNS 查询超时,而不是应用自己慢。
bash
# 进容器实测解析耗时
kubectl exec -it <pod> -- cat /etc/resolv.conf
kubectl exec -it <pod> -- nslookup user-svc.default.svc.cluster.local
kubectl exec -it <pod> -- sh -c "time getent hosts user-svc"
二、第 1 个坑:ndots:5 把一次解析放大成 5 次
先看你容器里的 /etc/resolv.conf,长这样:
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
ndots:5 的规则是 :如果查询的域名里点的数量 < 5 ,就先把它当相对域名 ,依次拼接 search 列表里的每个后缀去查;全都失败,才查原名。
我们来算一下 user-svc 的解析过程(点数为 0 < 5):
| 次序 | 实际查询的域名 | 是否命中 |
|---|---|---|
| 1 | user-svc.default.svc.cluster.local |
✅ 命中(这就是它的真实全名) |
| --- | 以下都不会发生(第 1 次就中了) | --- |
但如果查询的是 user-svc.default.svc.cluster.local(点数 = 4 < 5),就会先拼 search 后缀:
| 次序 | 实际查询 | 结果 |
|---|---|---|
| 1 | user-svc.default.svc.cluster.local.default.svc.cluster.local |
❌ NXDOMAIN |
| 2 | user-svc.default.svc.cluster.local.svc.cluster.local |
❌ NXDOMAIN |
| 3 | user-svc.default.svc.cluster.local.cluster.local |
❌ NXDOMAIN |
| 4 | user-svc.default.svc.cluster.local |
✅ 命中 |
一次解析变成 4 次查询。每次 NXDOMAIN 虽然快,但一旦某次丢包要等 5 秒超时,整体就变成 5 秒。

修法 :把 ndots 调小(通常设 2),或直接用 FQDN(以 . 结尾)。
yaml
spec:
dnsConfig:
options:
- name: ndots
value: "2" # 点数 >= 2 就直接查原名
- name: single-request-reopen # 规避 A/AAAA 并发导致的 5 秒
single-request-reopen 也值得记住:glibc 默认把 A 和 AAAA 查询发到同一个 socket,某些内核/网络栈下会导致其中一条丢包等 5 秒。这个选项让两条查询用不同 socket。
三、第 2 个坑:search 域列表过长
search 列表里每多一个域,最坏情况下就多一轮查询。默认的 default.svc.cluster.local svc.cluster.local cluster.local 已经是 3 个;如果应用或平台又追加了自定义域,查询次数会线性放大。
bash
# 看 search 列表
kubectl exec -it <pod> -- cat /etc/resolv.conf | grep search
判据 :search 列表长度 × 每次超时 5 秒 = 最坏耗时。3 个域 + 丢包 = 15 秒。
修法 :精简 search 列表(dnsConfig.searches),或改用 FQDN。
四、第 3 个坑:conntrack 表被打满
这是最隐蔽的一个。即使 DNS 配置完全正确,DNS 查询也会失败 ------因为 UDP DNS 包要经过 conntrack(连接跟踪)记录,而 conntrack 表有大小上限。
bash
# 看 conntrack 表使用情况(在节点上执行)
cat /proc/sys/net/netfilter/nf_conntrack_count # 当前条目数
cat /proc/sys/net/netfilter/nf_conntrack_max # 上限
# 看是否有丢包
nstat -az | grep -i conntrack
# 或
cat /proc/net/stat/nf_conntrack | head -1
为什么短连接会打满 conntrack :每个 DNS 查询、每个短 TCP 连接都产生一条 conntrack 记录,默认 nf_conntrack_udp_timeout 是 30 秒。QPS 高 + 大量短连接 → 表被占满 → 新包直接被丢(表现为 DNS 超时、连接超时)。
判据 :nf_conntrack_count 接近 nf_conntrack_max(比如超过 80%),且 insert_failed 在涨。
bash
# 确认真的是 conntrack 丢弃
nstat -az | grep -E "TcpAttemptFails|IpExt|conntrack"
dmesg | grep -i "nf_conntrack: table full"
修法:
bash
# ① 调大表(注意:也要调大 hashsize,否则只是拖慢哈希)
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_buckets=262144
# ② 缩短空闲超时
sysctl -w net.netfilter.nf_conntrack_udp_timeout=10
# ③ 应用侧:用长连接替代短连接,减少 conntrack 压力
第 ③ 条才是治本------减少连接数永远比调大表更有效。
五、把三个坑串成排查链
| 顺序 | 排查点 | 命令 | 命中则 |
|---|---|---|---|
| 1 | resolv.conf 的 ndots | cat /etc/resolv.conf |
默认 5→调小或改 FQDN |
| 2 | search 域数量 | 同上 | >3 个→精简 |
| 3 | 解析实测耗时 | time getent hosts <svc> |
恰 5 秒→DNS 超时确认 |
| 4 | conntrack 使用率 | nf_conntrack_count / max |
接近满→调表+减连接 |
| 5 | 内核丢弃计数 | `nstat -az | grep conntrack` |
一个可带走物 :dnsConfig 调优片段(ndots:2 + single-request-reopen)可以直接复制进 Deployment。
六、总结
- "恰好 5 秒"是内核 DNS 重试超时的标志,能直接锁定 DNS 问题;
ndots:5会把一次解析放大成最多 5 次查询,每次丢包等 5 秒 → 调小 ndots 或直接用 FQDN(末尾带点);search域列表越长,最坏耗时越长(域数 × 5 秒);- conntrack 表打满会静默丢包 ,表现为 DNS/连接超时但配置全对 ------ 判据是
nf_conntrack_count接近max; - 治本是减少短连接,不是无脑调大 conntrack 表。
留个问题给你 :你们集群里 ndots 改过没,还是默认 5?有没有遇到过配了 ndots 之后超时变少但没根治的情况------那大概率是 conntrack 在丢包。