ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kubernetes DNS 解析变慢/失败排查:CoreDNS、ndots:5 与 search 域的坑

Kubernetes DNS 解析变慢/失败排查:CoreDNS、ndots:5 与 search 域的坑 Kubernetes DNS 解析变慢/失败排查:CoreDNS、ndots:5 与 search 域的坑线上服务突然报一堆dial tcp: lookup xxx: i/o timeout,但你 ping 外网 IP 又是通的。或者更诡异:同一个域名,在 Pod 里curl有时候秒回、有时候卡 5 秒才响应。这类问题十有八九不是网络断了,而是 Pod 内的 DNS 解析出了岔子。K8s 的 DNS 有几个默认行为特别反直觉,踩过一次不排查清楚,你会一直以为是「网络抖动」。这篇按「先确认现象 → 定位是哪一层 → 对症修」的顺序,把常见的几个坑一次讲透。先看清 Pod 里的 DNS 是怎么配的进任意一个业务 Pod,看/etc/resolv.conf:kubectlexec-itmy-pod --cat/etc/resolv.conf典型输出:nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5这三行每一行都可能是坑:nameserver 10.96.0.10是 CoreDNS 的 Service ClusterIP,所有解析都先发给它。search是搜索域,解析短名字时会挨个拼上去试。options ndots:5是最容易出问题的:域名里的点数少于 5 个时,先当成「非完整域名」,挨个拼 search 域去查,全失败了才把它当完整域名直接查。关键就在ndots:5。举个例子,你在 Pod 里访问api.github.com(2 个点,小于 5),DNS 客户端会先这么试:api.github.com.default.svc.cluster.local - NXDOMAIN api.github.com.svc.cluster.local - NXDOMAIN api.github.com.cluster.local - NXDOMAIN api.github.com - 命中,返回真实 IP也就是说,访问一个外网域名,要先发 3 次注定失败的查询。平时没感觉,一旦 CoreDNS 有压力或者 UDP 丢包,前面几次失败查询的超时重试会让你直观感受到「卡几秒」。坑一:外部域名解析慢,元凶是 ndots search 域放大查询复现很简单,用dig看每次查询耗时(镜像里没有 dig 就临时装一个,或用 busybox 的 nslookup):# search 让 dig 模拟 resolv.conf 的 search 行为kubectlexec-itmy-pod --sh-c\time nslookup api.github.com如果你看到解析要 2~5 秒,基本就是 search 域放大 某几次 UDP 查询丢包重试导致的。修法有三个层次,按侵入性从低到高:访问外部域名时带上末尾的点,把它变成 FQDN(完全限定域名),直接跳过 search 拼接:curlhttps://api.github.com./# 注意 com 后面那个点这个最轻量,但代码里到处改域名不现实,一般只用来快速验证「是不是 ndots 的锅」。给这个 Pod 单独调 dnsConfig,把 ndots 降到 1 或 2。这是最实用的做法,只影响这个 workload:apiVersion:v1kind:Podmetadata:name:my-podspec:dnsConfig:options:-name:ndotsvalue:2# 点数 2 就直接当完整域名查,外部域名不再走 searchcontainers:-name:appimage:my-app:latest改成ndots:2后,api.github.com(2 个点)会被直接当完整域名查,省掉 3 次无效查询。要注意:如果你的服务大量靠短名字访问集群内 Service(比如只写user-service不写全user-service.default),ndots 调太低会让这些短名字解析失败,所以别无脑设成 1,先确认你的调用方式。给集群内互访用 FQDN,彻底不依赖 search。比如把redis写全成redis.default.svc.cluster.local,配合ndots:1,解析一步到位。适合对延迟极敏感的服务。坑二:解析间歇性失败,是 CoreDNS 副本或 conntrack 的问题如果不是慢而是偶发彻底解析不到,先看 CoreDNS 本身健不健康:# CoreDNS 跑在 kube-system 里,先看副本和重启次数kubectl get pods-nkube-system-lk8s-appkube-dns-owide# 看有没有报错日志(比如上游 DNS 拿不到、限流)kubectl logs-nkube-system-lk8s-appkube-dns--tail100几个高频原因:CoreDNS 只有 1 个副本还刚好被驱逐/重启:解析在那几秒全挂。生产至少 2 副本,并加上 PodAntiAffinity 打散到不同节点。CoreDNS 所在节点资源打满:describe 看它有没有被 OOMKilled 或 CPU 饿死。经典的 UDP conntrack race:内核在做 DNAT 时,并发的 UDP DNS 请求会撞上 conntrack 插入竞争,导致约 5 秒一次的丢包。表现就是「大部分正常,偶尔卡 5 秒」。针对最后这个内核级问题,最省事的通用解法是部署NodeLocal DNSCache:在每个节点上跑一个本地 DNS 缓存,Pod 的查询先走本机的缓存(走 TCP 到上游 CoreDNS),绕开有问题的 UDP conntrack 路径:# NodeLocal DNSCache 是官方提供的 DaemonSet,部署后# 每个节点监听一个 link-local 地址(常见 169.254.20.10)做本地缓存kubectl get daemonset-nkube-system node-local-dns部署它之后,resolv.conf 的 nameserver 会指向节点本地地址,绝大多数重复查询命中本地缓存,既降延迟又躲开 conntrack 竞争。这是中大型集群几乎必上的组件。坑三:改了 Service 名却解析不到,是缓存 TTL 在作怪有时你新建了 Service 或改了它的 ClusterIP,业务 Pod 却还解析到旧值。CoreDNS 对集群内记录默认有个 30 秒的 TTL,应用侧(尤其 JVM)可能还有自己的 DNS 缓存叠加。排查时用这条命令直接问 CoreDNS,绕过应用缓存:# 指定 nameserver 直接查,看 CoreDNS 现在返回什么kubectlexec-itmy-pod --nslookupuser-service.default.svc.cluster.local10.96.0.10如果 CoreDNS 返回的是新 IP,应用还连旧的,那问题在应用层缓存(比如 Java 的networkaddress.cache.ttl),不在 K8s。这一步能帮你快速划清「是 K8s 的锅还是应用的锅」。一个能救急的排查顺序线上 DNS 出问题时别乱试,按这个顺序走最快:# 1. 现象确认:是慢还是彻底不通?kubectlexec-itmy-pod --sh-ctime nslookup 出问题的域名# 2. 看 resolv.conf,确认 ndots 和 searchkubectlexec-itmy-pod --cat/etc/resolv.conf# 3. 绕过 search 直接查完整域名,能通就是 ndots/search 的锅kubectlexec-itmy-pod --nslookup域名带末尾点或写全 FQDN# 4. 直接问 CoreDNS,能通就是应用缓存的锅kubectlexec-itmy-pod --nslookup域名10.96.0.10# 5. CoreDNS 自身健康度kubectl get pods-nkube-system-lk8s-appkube-dns kubectl logs-nkube-system-lk8s-appkube-dns--tail100小结Pod 里的 DNS 慢,先怀疑options ndots:5:访问外部域名会先拼 search 域发多次无效查询,一丢包就卡秒级。修外部解析慢:优先给 workload 单独设dnsConfig把ndots降到 2,或集群内互访直接用 FQDN,别在集群层面乱动全局值。偶发彻底失败多半是 CoreDNS 副本不够或 UDP conntrack 竞争,通用解法是上NodeLocal DNSCache。分层判断口诀:带点直查能通 ndots 的锅;直问 CoreDNS 能通 应用缓存的锅;CoreDNS 自己都返回错 才去查 CoreDNS。记忆点:K8s DNS 的 90% 玄学问题,根子都在ndots:5和那几个 search 域上——先把这行看懂,再谈网络。
返回列表