DNS — dig 能通而应用失败的原因
一句话总结
dig 与应用从一开始就走不同的名称解析路径,因此 dig 成功并不能证明应用也会成功。
为什么需要它
最容易误导人的故障组合是: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 依次:
- 读取
/etc/nsswitch.conf的 hosts 行,决定数据源及顺序。 - 若为
files,查看/etc/hosts。 - 若为
dns,应用/etc/resolv.conf的 nameserver、search、options 后查询。 - 若为
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 内可使用 api 或 api.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 如何制造延迟。