
这篇文章记录我工作过程中处理过的一个我认为比较典型的故障吧,希望对社区的朋友有用。
线上集群的业务偶发 name resolution 报错,开发同学在 Pod 里跑 nslookup 和 curl 会随机超时。典型现象和错误截图如下:

业务侧报错栈:
http: proxy error: dial tcp: lookup kubernetes.default.svc on 10.96.0.10:53: read udp 10.244.0.4:57589->10.96.0.10:53: i/o timeout。
这类报错常见于应用在访问 kubernetes.default.svc 或外网域名时由 DNS 延迟引发的超时,我使用 Google 在社区里发现了同款报错记录,链接如下:
CoreDNS 日志可见批量 i/o timeout 与 SERVFAIL:
plugin/errors: 2 elasticsearch. A: read udp 10.244.235.249:44960->132.120.200.49:53: i/o timeout,以及对集群内部 Service 的 AAAA/A 记录解析超时。
详细讨论可以在这个 Github issue 里看见。
在部分发行版中,还记录到转发上游超时后返回 SERVFAIL 的行为描述: Red Hat Customer Portal
在使用 ndots:5 默认配置时,解析外部域名出现额外延迟,nslookup/curl 间歇变慢甚至超时,社区有多处实证讨论:
这些现象混在一起时,很容易让人误以为是 CoreDNS 本身不稳定。事实上,链路里任何一个环节的小抖动,都可能在应用侧呈现为 UnknownHostException 或 i/o timeout。
为了把问题钉在板上,我在一套 3 节点的 Kubernetes 集群中,用 busybox 连续做 DNS 解析与 HTTP 探测:
kubectl run dnstest --rm -it --image=busybox:1.36 --restart=Never -- sh -c '
echo "nameserver check:"; cat /etc/resolv.conf;
i=0; while [ $i -lt 200 ]; do
date
nslookup www.google.com || echo "nslookup fail"
wget -qO- http://example.com >/dev/null 2>&1 || echo "curl fail"
i=$((i+1)); sleep 1
done
'在问题出现的时间窗口内,可以稳定复现间歇性 nslookup: write to ... connection timed out 或 wget: bad address。将相同循环脚本换到宿主机运行,失败概率显著降低,提示问题更偏向集群内部的 DNS 转发链路与配置。
resolv.conf 与 ndots 行为在 Pod 里:
cat /etc/resolv.conf
# 常见输出:
# search default.svc.cluster.local svc.cluster.local cluster.local
# nameserver 10.96.0.10
# options ndots:5ndots:5 的含义是:当查询名包含小于 5 个点时,优先将其视为非绝对域名,逐个追加 search 后缀去尝试。这会对例如 s3.amazonaws.com 之类外部域名触发多轮内部后缀尝试,带来额外 DNS 往返,延迟与超时风险被放大,这是社区里反复被证实的高频坑点。
kubectl -n kube-system logs deploy/coredns可见大量 read udp ... i/o timeout 与 SERVFAIL,表明 CoreDNS 转发至上游的链路存在丢包或上游拥塞。
CoreDNS 转发超时约 6 秒后返回 SERVFAIL,若上游 dnsmasq 等存在排队或丢包,短时间内同名查询还会持续 SERVFAIL。
conntrack 与 UDP 特性DNS 默认走 UDP。节点 conntrack 表容量不足或回收不及时,会造成间歇丢包,从而在 Pod 侧表现为超时。内核日志可见 nf_conntrack: table full, dropping packet。社区多次讨论通过调大 nf_conntrack_max、调整超时、或引入 NodeLocal DNS 缓存来缓解。
当 CNI 的 MTU 设置过大,跨节点流量被分片且上游防火墙丢弃分片,也会出现 DNS 超时的间歇性现象。此类问题通常伴随跨子网访问外部 DNS 上游更容易超时;将 MTU 调小到底层网络可承受值能显著改善。这个方向常与特定云厂商网络链路相关,可结合厂商排障文档定位。
NodeLocal DNSCache(推荐)NodeLocal DNS 将每个节点本地监听 DNS,Pod 对 DNS 的 UDP 查询会先命中本地缓存,向上游到 CoreDNS 的连接可升级为 TCP,降低 UDP 丢包带来的长尾延迟与超时。
启用步骤(适配常见 iptables 模式):
# 取得 kube-dns ClusterIP
KUBEDNS=$(kubectl -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}')
# 选一个本地监听地址,常用链路本地网段,例如 169.254.20.10
LOCALDNS=169.254.20.10
DOMAIN=cluster.local
# 下载官方示例后替换变量(伪代码示意)
# sed -i "s/__PILLAR__DNS__SERVER__/$KUBEDNS/g; s/__PILLAR__LOCAL__DNS__/$LOCALDNS/g; s/__PILLAR__DNS__DOMAIN__/$DOMAIN/g" nodelocaldns.yaml
kubectl apply -f nodelocaldns.yaml启用后,Pod 的 nameserver 会指向本地缓存 IP 或通过 iptables 重定向,缓存命中降低延迟;上游到 CoreDNS 的链路采用 TCP,conntrack 回收有保障,长尾明显收敛。
ndots 做精确控制(按需)对需要频繁访问外部域名的工作负载,可将 ndots 调整到更小值,例如 1 或依据域名结构设为 2。这能减少 search 追加带来的反复查询。配置示例:
apiVersion: v1
kind: Pod
metadata:
name: web-ndots1
spec:
dnsConfig:
options:
- name: ndots
value: "1"
containers:
- name: curl
image: curlimages/curl:8.8.0
command: ["sh","-c","while true; do curl -s https://www.google.com >/dev/null || echo DNSFAIL; sleep 1; done"]ndots 调整后可以显著降低外部域名解析时的超时概率。
conntrack 与优化 UDP 超时(集群维度)在节点层面增加连接跟踪表容量、缩短 UDP 超时,能降低突发负载下的丢包。可使用一个 DaemonSet 以声明式应用:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: sysctl-conntrack
namespace: kube-system
spec:
selector:
matchLabels: {app: sysctl-conntrack}
template:
metadata:
labels: {app: sysctl-conntrack}
spec:
hostPID: true
containers:
- name: sysctl
image: busybox:1.36
securityContext:
privileged: true
command: ["sh","-c","
sysctl -w net.netfilter.nf_conntrack_max=1048576 &&
sysctl -w net.netfilter.nf_conntrack_udp_timeout=15 &&
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=15 &&
sleep 3600000
"]
tolerations:
- operator: Exists
nodeSelector: {}确保 CoreDNS 的 Corefile 使用合理的 forward 与 cache,并关注上游可达性:
kubectl -n kube-system get configmap coredns -o yaml
# 例子(来自常见配置):
# forward . /etc/resolv.conf {
# max_concurrent 1000
# }
# cache 30i/o timeout 多见于上游不可达或丢包;当转发到云厂商内置 IP 时(如 AKS 的 168.63.129.16),需确认节点网络的放行与连通。
为了让业务侧也能快速自证,我收集了几类常见调用栈与自检脚本:
dial tcp: lookup index.docker.io on 192.168.65.1:53: read udp ... i/o timeout。这类报错往往就是 DNS 查询超时,和我们在 Pod 内看到的现象一致。io.netty.resolver.dns.DnsNameResolverTimeoutException: [/100.64.0.10:53] query timed out after 5000 milliseconds,或 SearchDomainUnknownHostException。该异常族在 Netty socket.gaierror 或 requests.exceptions.ConnectionError 包裹的 DNS 失败,可用 try/except 精确捕获。 通用自检脚本(Pod 内运行):
# DNS QPS 与错误可视化
apk add --no-cache bind-tools >/dev/null 2>&1 || true
i=0; ok=0; fail=0
while [ $i -lt 100 ]; do
if dig +time=2 +retry=1 www.google.com @$(awk '/nameserver/{print $2;exit}' /etc/resolv.conf) >/dev/null; then ok=$((ok+1)); else fail=$((fail+1)); fi
i=$((i+1)); sleep 0.3
done
echo "ok=$ok fail=$fail"在启用 NodeLocal DNSCache 与为关键工作负载设置 ndots:1 之后,nslookup 与 curl 的长尾超时完全消失,CoreDNS 的 i/o timeout 日志也从每小时几十条降为个位数。关于 NodeLocal 的官方原理图与效果说明,可以对比上游描述:本地缓存接收 Pod 的 UDP 查询,而到 CoreDNS 的链路可升级为 TCP,超时与尾延迟显著降低。
ndots:5 搭配长 search 列表会放大外部域名解析的时延与超时概率。对访问外部域名密集的工作负载下调到 1 或 2,收益立竿见影。 i/o timeout 时,先验证上游 DNS 可达、CNI/路由与安全组是否放行,再看 Corefile 配置。 conntrack 表满是 UDP 超时的隐形元凶。线上请把容量与 UDP 超时配到位,或用 NodeLocal 避免 UDP 在内核表里堆积。 168.63.129.16)若被 ACL/路由挡住,会在 CoreDNS 中集中表现为 i/o timeout。网络侧放行是硬性前置条件。 musl)在 ndots:5 环境下更容易暴露性能与超时问题,必要时更换基础镜像或调低 ndots。 prometheus 指标与日志采样,结合 kubectl exec 的循环探测脚本,能用数据定位毛刺高峰期与受影响命名空间。 NodeLocal DNSCache,按官方步骤替换变量并 kubectl apply。spec:
template:
spec:
dnsConfig:
options:
- { name: ndots, value: "1" }DaemonSet 调优 conntrack,并在 Prometheus 抓 CoreDNS 与 NodeLocal 的指标。 forward 指向稳定的企业 DNS,或检查云厂商的专用 DNS 目标是否可达。 这类 Pod 内的 nslookup 与 curl 间歇性超时,表象在应用,根因却经常在 ndots、conntrack、上游 DNS、以及 UDP 的不可靠特性上叠加共振。把请求就近落在节点本地缓存、把 ndots 调整到和业务域名结构匹配的阈值、把节点与上游的网络通路打通,这三板斧能解决绝大多数场景。余下的少量长尾,再通过指标与日志去精修——而不是盲目地重启 CoreDNS 或扩大副本。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。