K8s 服务 DNS 解析偶发 5 秒超时:conntrack 与 ndots 的 3 个坑

我不会起名字322 · 后端 / 算法 / 数据库

📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL

文章目录

  • [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。

六、总结

  1. "恰好 5 秒"是内核 DNS 重试超时的标志,能直接锁定 DNS 问题;
  2. ndots:5 会把一次解析放大成最多 5 次查询,每次丢包等 5 秒 → 调小 ndots 或直接用 FQDN(末尾带点);
  3. search 域列表越长,最坏耗时越长(域数 × 5 秒);
  4. conntrack 表打满会静默丢包 ,表现为 DNS/连接超时但配置全对 ------ 判据是 nf_conntrack_count 接近 max;
  5. 治本是减少短连接,不是无脑调大 conntrack 表。

留个问题给你 :你们集群里 ndots 改过没,还是默认 5?有没有遇到过配了 ndots 之后超时变少但没根治的情况------那大概率是 conntrack 在丢包。

相关推荐
酒神dnspup4 小时前
DNS 泄露检测怎么做:从随机权威域名、递归解析器出口到 HTTP 出口一致性
网络安全·dns·网络排查·dns泄露
七夜zippoe5 小时前
把 OpenClaw Agent 搬进 K8s:容器化部署全记录
ai·云原生·容器·kubernetes·openclaw
March.s16 小时前
Kubernetes 全景导览:从部署演进到云原生底座
云原生·kubernetes·dubbo
熊出没17 小时前
云原生架构落地实践:Kubernetes 生产级配置与 Service Mesh 流量治理
云原生·架构·kubernetes·istio
东风微鸣1 天前
K8s 最佳实践已更新:请使用 Node Swap!
kubernetes
励志师code1 天前
Kubernetes 集群节点生命周期管理实战:从扩容、调度管控到证书续期
运维·docker·容器·kubernetes·gpu算力·kubelet
闲云自留地1 天前
Kubernetes 安装保姆级:关 swap、装 containerd、init 集群、calico 一篇通
容器·贪心算法·kubernetes
easy_coder1 天前
Kubernetes 三大探针:Startup、Liveness 与 Readiness
云原生·容器·kubernetes