首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Kubernetes Pod 里的 nslookup/curl 间歇性超时的故障排查复盘

Kubernetes Pod 里的 nslookup/curl 间歇性超时的故障排查复盘

原创
作者头像
编程小妖女
发布2025-09-20 15:10:43
发布2025-09-20 15:10:43
960
举报
文章被收录于专栏:后端开发后端开发

这篇文章记录我工作过程中处理过的一个我认为比较典型的故障吧,希望对社区的朋友有用。

故障背景与症状

线上集群的业务偶发 name resolution 报错,开发同学在 Pod 里跑 nslookupcurl 会随机超时。典型现象和错误截图如下:

业务侧报错栈:

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 timeoutSERVFAIL

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 本身不稳定。事实上,链路里任何一个环节的小抖动,都可能在应用侧呈现为 UnknownHostExceptioni/o timeout


复现方式与最小可复现环境

为了把问题钉在板上,我在一套 3 节点的 Kubernetes 集群中,用 busybox 连续做 DNS 解析与 HTTP 探测:

代码语言:bash
复制
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 outwget: bad address。将相同循环脚本换到宿主机运行,失败概率显著降低,提示问题更偏向集群内部的 DNS 转发链路与配置。


排查路径与关键证据

1) 看 Pod 的 resolv.confndots 行为

在 Pod 里:

代码语言:bash
复制
cat /etc/resolv.conf
# 常见输出:
# search default.svc.cluster.local svc.cluster.local cluster.local
# nameserver 10.96.0.10
# options ndots:5

ndots:5 的含义是:当查询名包含小于 5 个点时,优先将其视为非绝对域名,逐个追加 search 后缀去尝试。这会对例如 s3.amazonaws.com 之类外部域名触发多轮内部后缀尝试,带来额外 DNS 往返,延迟与超时风险被放大,这是社区里反复被证实的高频坑点。

2) 查 CoreDNS 日志与转发上游

代码语言:bash
复制
kubectl -n kube-system logs deploy/coredns

可见大量 read udp ... i/o timeoutSERVFAIL,表明 CoreDNS 转发至上游的链路存在丢包或上游拥塞。

CoreDNS 转发超时约 6 秒后返回 SERVFAIL,若上游 dnsmasq 等存在排队或丢包,短时间内同名查询还会持续 SERVFAIL

3) 观察 conntrack 与 UDP 特性

DNS 默认走 UDP。节点 conntrack 表容量不足或回收不及时,会造成间歇丢包,从而在 Pod 侧表现为超时。内核日志可见 nf_conntrack: table full, dropping packet。社区多次讨论通过调大 nf_conntrack_max、调整超时、或引入 NodeLocal DNS 缓存来缓解。

4) 核查 CNI 与 MTU

当 CNI 的 MTU 设置过大,跨节点流量被分片且上游防火墙丢弃分片,也会出现 DNS 超时的间歇性现象。此类问题通常伴随跨子网访问外部 DNS 上游更容易超时;将 MTU 调小到底层网络可承受值能显著改善。这个方向常与特定云厂商网络链路相关,可结合厂商排障文档定位。


解决方案与落地配置(含可运行清单)

A. 业务无侵入的 NodeLocal DNSCache(推荐)

NodeLocal DNS 将每个节点本地监听 DNS,Pod 对 DNS 的 UDP 查询会先命中本地缓存,向上游到 CoreDNS 的连接可升级为 TCP,降低 UDP 丢包带来的长尾延迟与超时。

启用步骤(适配常见 iptables 模式):

代码语言:bash
复制
# 取得 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 回收有保障,长尾明显收敛。

B. 对应用 ndots 做精确控制(按需)

对需要频繁访问外部域名的工作负载,可将 ndots 调整到更小值,例如 1 或依据域名结构设为 2。这能减少 search 追加带来的反复查询。配置示例:

代码语言:yaml
复制
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 调整后可以显著降低外部域名解析时的超时概率。

C. 调大 conntrack 与优化 UDP 超时(集群维度)

在节点层面增加连接跟踪表容量、缩短 UDP 超时,能降低突发负载下的丢包。可使用一个 DaemonSet 以声明式应用:

代码语言:yaml
复制
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: {}

D. 审核 CoreDNS 转发与缓存策略

确保 CoreDNS 的 Corefile 使用合理的 forwardcache,并关注上游可达性:

代码语言:bash
复制
kubectl -n kube-system get configmap coredns -o yaml
# 例子(来自常见配置):
# forward . /etc/resolv.conf {
#   max_concurrent 1000
# }
# cache 30

i/o timeout 多见于上游不可达或丢包;当转发到云厂商内置 IP 时(如 AKS 的 168.63.129.16),需确认节点网络的放行与连通。


语言栈层面的报错与可复用定位脚本

为了让业务侧也能快速自证,我收集了几类常见调用栈与自检脚本:

  • Go/容器工具链dial tcp: lookup index.docker.io on 192.168.65.1:53: read udp ... i/o timeout。这类报错往往就是 DNS 查询超时,和我们在 Pod 内看到的现象一致。
  • Java/Nettyio.netty.resolver.dns.DnsNameResolverTimeoutException: [/100.64.0.10:53] query timed out after 5000 milliseconds,或 SearchDomainUnknownHostException。该异常族在 Netty
  • Python/requests: 典型为 socket.gaierrorrequests.exceptions.ConnectionError 包裹的 DNS 失败,可用 try/except 精确捕获。

通用自检脚本(Pod 内运行):

代码语言:bash
复制
# 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 之后,nslookupcurl 的长尾超时完全消失,CoreDNS 的 i/o timeout 日志也从每小时几十条降为个位数。关于 NodeLocal 的官方原理图与效果说明,可以对比上游描述:本地缓存接收 Pod 的 UDP 查询,而到 CoreDNS 的链路可升级为 TCP,超时与尾延迟显著降低。


我从这个故障领悟到的避坑清单(按发生频率与影响排序)

  1. ndots:5 搭配长 search 列表会放大外部域名解析的时延与超时概率。对访问外部域名密集的工作负载下调到 12,收益立竿见影。
  2. CoreDNS 并非洪水猛兽。看到 i/o timeout 时,先验证上游 DNS 可达、CNI/路由与安全组是否放行,再看 Corefile 配置。
  3. 节点 conntrack 表满是 UDP 超时的隐形元凶。线上请把容量与 UDP 超时配到位,或用 NodeLocal 避免 UDP 在内核表里堆积。
  4. 云厂商托管 DNS 专用 IP(如 AKS 的 168.63.129.16)若被 ACL/路由挡住,会在 CoreDNS 中集中表现为 i/o timeout。网络侧放行是硬性前置条件。
  5. 一些基础镜像的 DNS 库实现差异(如 Alpine 的 musl)在 ndots:5 环境下更容易暴露性能与超时问题,必要时更换基础镜像或调低 ndots
  6. 需要可观测性。打开 CoreDNS 的 prometheus 指标与日志采样,结合 kubectl exec 的循环探测脚本,能用数据定位毛刺高峰期与受影响命名空间。

附:一份可直接落地的组合修复清单

  • 集群层启用 NodeLocal DNSCache,按官方步骤替换变量并 kubectl apply
  • 对高频访问外部域名的 Deployment 添加:
代码语言:yaml
复制
spec:
  template:
    spec:
      dnsConfig:
        options:
          - { name: ndots, value: "1" }
  • 为所有节点下发 DaemonSet 调优 conntrack,并在 Prometheus 抓 CoreDNS 与 NodeLocal 的指标。
  • 审核 CoreDNS 上游可达性,必要时将 forward 指向稳定的企业 DNS,或检查云厂商的专用 DNS 目标是否可达。

总结

这类 Pod 内的 nslookupcurl 间歇性超时,表象在应用,根因却经常在 ndotsconntrack、上游 DNS、以及 UDP 的不可靠特性上叠加共振。把请求就近落在节点本地缓存、把 ndots 调整到和业务域名结构匹配的阈值、把节点与上游的网络通路打通,这三板斧能解决绝大多数场景。余下的少量长尾,再通过指标与日志去精修——而不是盲目地重启 CoreDNS 或扩大副本。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 故障背景与症状
  • 复现方式与最小可复现环境
  • 排查路径与关键证据
    • 1) 看 Pod 的 resolv.conf 与 ndots 行为
    • 2) 查 CoreDNS 日志与转发上游
    • 3) 观察 conntrack 与 UDP 特性
    • 4) 核查 CNI 与 MTU
  • 解决方案与落地配置(含可运行清单)
    • A. 业务无侵入的 NodeLocal DNSCache(推荐)
    • B. 对应用 ndots 做精确控制(按需)
    • C. 调大 conntrack 与优化 UDP 超时(集群维度)
    • D. 审核 CoreDNS 转发与缓存策略
  • 语言栈层面的报错与可复用定位脚本
  • 实战修复结果
  • 我从这个故障领悟到的避坑清单(按发生频率与影响排序)
  • 附:一份可直接落地的组合修复清单
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档