容器里域名解析慢半拍:ndots、search 域与 conntrack 表满的三层排查
同一个容器里,ping 一个 IP 秒回,curl 一个域名却偶发 5 秒左右才响应,重试又恢复正常。这类「慢半拍」的问题十有八九卡在 DNS,而且多数不是 DNS 服务器本身故障。下面按三层由易到难排查:查询次数被放大、解析器超时被吃满、UDP 53 的包被内核丢掉。
一、先确认慢在哪一步
不要在应用日志里猜,先把解析这一跳单独测出来:
# 看解析耗时(不经过应用)
time getent hosts api.example.com
# 看实际发出去了哪些查询
tcpdump -ni any -c 20 port 53
如果 getent hosts 也慢,问题就在解析链路;如果只有应用慢,先怀疑应用自己的连接池或重试逻辑。这一步能把范围砍掉一半。
二、ndots 把一次查询放大成五六次
容器里的 /etc/resolv.conf 常见两行:
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
ndots:5 的含义是:域名字面里的点数少于 5 个时,先把每个 search 域拼上去试一遍,最后才按绝对域名查。api.example.com 只有 2 个点,解析器实际会依次查:
api.example.com.default.svc.cluster.local
api.example.com.svc.cluster.local
api.example.com.cluster.local
api.example.com
前三次注定 NXDOMAIN,却都要走一趟网络。集群 DNS 一旦有几十毫秒抖动,放大五六倍就是肉眼可见的卡顿。验证方法:
dig +search api.example.com # 能看到按 search 列表逐个查询
dig api.example.com. # 末尾加点 = 绝对域名,跳过 search
三个不改架构的修法:
- 调用外部域名时写带尾点的 FQDN(
api.example.com.),直接跳过 search 列表; - 把
ndots调到 2 左右,Kubernetes 下可用dnsConfig.options覆盖; - 部署 NodeLocal DNSCache,把解析收敛到节点本地,减少一次跨节点往返。
三、一次丢包就是一次 5 秒
glibc 解析器默认 timeout:5、attempts:2。DNS 主要走 UDP,而 UDP 没有重传,包一丢,客户端只能干等 5 秒再发第二次。所以「偶发 5 秒」几乎就是一次丢包的指纹。查两处:
cat /etc/resolv.conf # 有没有写 timeout / attempts
systemctl status systemd-resolved # 宿主机是否启用了 127.0.0.53 stub
一个常见误区:宿主机把 127.0.0.53 写进 resolv.conf,容器又整份复制过去。容器有自己的网络命名空间,127.0.0.53 指向容器自己而不是宿主机的 systemd-resolved,结果是连不上、只能等超时。容器里的解析地址应当是集群 DNS 的 Service IP 或节点地址。
四、conntrack 表满,UDP 包被静默丢弃
前两层都正常却仍然偶发超时,就要看内核有没有在丢包:
dmesg -T | grep -i nf_conntrack
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
conntrack -C
看到 nf_conntrack: table full, dropping packet 就实锤了:连接跟踪表满了,新包直接丢,DNS 的 UDP 查询首当其冲。表里堆积的多是大量短命 UDP 条目,默认 nf_conntrack_udp_timeout 为 30 秒,高并发下很快填满。处置顺序:
- 先把
nf_conntrack_max调大,同时关注nf_conntrack_buckets; - 把
nf_conntrack_udp_timeout适当调小,让条目更快回收; - 让 DNS 流量在 raw 表走
NOTRACK,从根上不占 conntrack; - 长期方案仍是 NodeLocal DNSCache,把查询压在节点内。
五、排查顺序小结
先量解析耗时锁定 DNS,再用 dig +search 判断查询次数是否被放大,然后核对 resolv.conf 的超时参数与解析地址,最后用 dmesg 和 conntrack 计数确认内核是否在丢包。不要一上来就改 /etc/hosts,它只会把问题藏起来,换个域名照样复发;也别把 5 秒延迟记到应用性能账上,那正好是一个解析超时周期的量级。
一句话记住:DNS 慢,多数时候不是「解析不出来」,而是「问得太多」或者「问了没人答」。
- 点赞
- 收藏
- 关注作者
评论(0)