LabHub
学习 学习路径 课程

网络基础

DNS — dig 能通而应用失败的原因

在 LabHub 中继续学习

一句话总结

dig 与应用从一开始就走不同的名称解析路径,因此 dig 成功并不能证明应用也会成功。

概念图: 应用实际经过的路径不同。 · dig, nslookup, host 会完全绕过这条路径。 · getent hosts 才能复现应用的解析路径。 · 解析一个外部域名会发出 10 次查询,其中 8 次注定得到 NXDOMAIN。

为什么需要它

最容易误导人的故障组合是:dig +short api.internal.example.com 正常返回地址,同一主机上的应用却因无法解析名称而退出。很多人据此认定“DNS 正常,是应用有问题”,转而排查库;这几乎总是徒劳。

工作原理

先看 DNS 自身:解析器询问根服务器得到 TLD 服务器,再询问 TLD 得到权威服务器,最后取得答案(递归与迭代查询的组合)。每个响应带 TTL,并缓存相应时间。

记录类型中,A 是 IPv4 地址,AAAA 是 IPv6 地址,CNAME 是别名,MX 是邮件服务器,NS 是权威服务器,TXT 是任意字符串,SRV 是服务主机和端口。

关键在于:应用实际经过的路径不同。 C、Python、Ruby、PHP,以及 Linux 上的 Java,大多调用 glibc 的 getaddrinfo();glibc 依次:

  1. 读取 /etc/nsswitch.conf 的 hosts 行,决定数据源及顺序。
  2. 若为 files,查看 /etc/hosts
  3. 若为 dns,应用 /etc/resolv.conf 的 nameserver、search、options 后查询。
  4. 若为 resolve,询问 systemd-resolved。

dig, nslookup, host 会完全绕过这条路径。 它们不看 nsswitch.conf 或 /etc/hosts,只从 resolv.conf 取得 nameserver 地址并直接向 UDP 53 发查询。因此 dig 找不到只存在于 /etc/hosts 的名称;反过来,遗留的旧 /etc/hosts 条目可能让只有应用连接旧 IP。

getent hosts 才能复现应用的解析路径。 getent ahosts 会按尝试顺序显示 IPv4 与 IPv6。简言之:dig 展示 DNS 服务器的答案,getent 展示应用会收到的答案;二者不同之处正是原因所在。

实际工作中的表现

Kubernetes Pod 的 resolv.conf 通常包含 options ndots:5 和四个 search 域。ndots 表示“点数少于该值时,先追加 search 列表”,因此 Pod 内可使用 apiapi.prod 等短名称。

问题在外部域名。api.stripe.com 有两个点,少于 5,会先尝试四个 search 域;同时查询 A 和 AAAA,解析一个外部域名会发出 10 次查询,其中 8 次注定得到 NXDOMAIN。 数百个 Pod 会让 CoreDNS 的大部分负载用于注定失败的查询。

“把 ndots 降到 1”会破坏集群:payments.prod 等含一个点的跨命名空间名称不再走 search,全部失败。安全下限是 2。 更可靠的方法是在外部域名末尾加点,形成绝对名称,一个点即可消除四个 search 域。

响应码也不能混为一谈。NXDOMAIN 表示名称不存在;SERVFAIL 表示解析器无法生成答案;ANSWER 为 0 的 NOERROR 表示名称存在,但没有该类型记录,典型情况是有 A 没有 AAAA。

名称无法解析时,确认走到了哪里

“像 DNS 问题”只有一半时候真是 DNS。逐层询问即可定位。

dig 查看缓存,dig +trace 查看整条链。

dig example.com A +short          # 지금 내 리졸버가 아는 답
dig example.com A +trace          # 루트부터 권한 서버까지 따라간다
dig @8.8.8.8 example.com A        # 다른 리졸버는 뭐라고 하는가
dig example.com SOA +short        # 이 이름의 주인은 어느 서버인가

+short 成功但浏览器失败,问题在 DNS 之后;@8.8.8.8 成功而默认解析器失败,问题在本地解析器或缓存

nslookup/dig 与应用可能看到不同答案。 前两者直接查询 DNS,多数程序则通过 getaddrinfo(),路径中还包括 /etc/hosts/etc/nsswitch.conf、mDNS。getent hosts example.com 更准确地反映程序所见。

TTL 不是“何时开始生效”,而是“旧值保留到何时”。 修改记录前先把 TTL 降至 300 秒,等原 TTL 过去后再修改,稳定后再调高。否则不同用户会一直访问不同位置,直到旧 TTL 全部过期。

dig example.com A          # ANSWER SECTION 의 두 번째 열이 남은 TTL 이다

名称不存在与没有答案不同。 NXDOMAIN 是名称不存在;NOERROR 但 ANSWER 为 0,则是名称存在但没有该类型记录。只有 A 而没有 AAAA 时,优先尝试 IPv6 的客户端可能因此变慢。

搜索域会悄然追加。 /etc/resolv.conf 有多个 search 域时,一个短名称会产生同样多的查询。Kubernetes Pod 因 ndots:5 导致外部域名先失败四次再成功,就是著名案例。写成带末尾点的 example.com. 可跳过搜索。

后续测验将确认什么

确认你能解释 dig 与 getent 的差异,并理解 ndots 如何制造延迟。