返回博客

systemd-resolved 把 NXDOMAIN 当终局答案:DNS 兜底列表为何永远不生效

在一台 Ubuntu 22.04 服务器上配 Docker registry mirror,docker pull 死活报 "no such host"。nslookup 指向 127.0.0.53 返回 NXDOMAIN,但拿同一个域名直接 dig @223.5.5.5 秒出 A 记录。netplan 里明明已经填了公共 DNS 作为兜底——却像从来没写一样。

折腾了很久才发现,根子不在域名、不在镜像源,而在 systemd-resolved 一个让人误解的核心行为。

现象

# 失败
nslookup docker.m.daocloud.io
# ** server can't find docker.m.daocloud.io: NXDOMAIN
# server: 127.0.0.53
 
# 成功(直接绕过 systemd-resolved)
dig +short @223.5.5.5 docker.m.daocloud.io
# 返回真实 IP
 
# netplan 配置
nameservers:
  addresses: [192.168.1.1, 223.5.5.5]

resolvectl status enp2s0 显示两个 DNS 都在列表里,看起来没问题。但只要把 192.168.1.1 删掉,resolution 立即恢复正常。

根因

很多人对"多个 nameserver"的直觉来自传统 glibc resolver:上一个 timeout 才试下一个,是严格的顺序兜底链。

systemd-resolved 完全不同:

NXDOMAIN is treated as an authoritative negative response.

它对多个 upstream DNS 的策略是并发竞速——同时发出查询,谁先回来就用谁的结果。路由器 DNS 在内网,延迟最低,通常最先响应。一旦它返回 NXDOMAIN(被 DNS 污染、运营商封锁或路由器策略),systemd-resolved 把这个结果当权威终局答案,立即结束查询,不会等公共 DNS 的响应。

加再多公共 DNS 也无济于事——它们已经没有参赛资格了。

坑:NXDOMAIN 不是超时,是终止信号

传统 resolver 的"超时失败才换下一个"逻辑在 systemd-resolved 里完全失效。NXDOMAIN 被视为权威负回答,一票否决,竞速就此终止。把公共 DNS 写在路由器 DNS 后面,等于白写。

正解

把可靠的公共 DNS 放列表第一位,让它赢得竞速;路由器 DNS 退为兜底(仅用于内网 hostname 解析)或直接删掉。

/etc/netplan/01-static.yaml

network:
  version: 2
  ethernets:
    enp2s0:
      addresses: [192.168.100.x/24]
      routes:
        - to: default
          via: 192.168.100.1
      nameservers:
        addresses:
          - 223.5.5.5        # 阿里 DNS — 优先,延迟低且不污染
          - 119.29.29.29     # DNSPod — 备用
          - 192.168.1.1      # 路由器 — 仅内网 host,兜底
sudo netplan apply
resolvectl status enp2s0 | grep "DNS Servers"
# 确认 223.5.5.5 排第一
nslookup docker.m.daocloud.io
# 应该返回真实 IP
正解:把不污染的 DNS 放第一,让它先赢

systemd-resolved 是竞速模型,不是兜底链。第一个返回权威响应的 DNS 说了算。把延迟低且干净的公共 DNS 排第一,路由器 DNS 的 NXDOMAIN 就没机会先到。

几个容易踩的附坑

改 /etc/resolv.conf 没用

Ubuntu 22.04 的 /etc/resolv.conf 是一个指向 127.0.0.53 的 stub,实际上游由 systemd-resolved 管理。直接编辑 resolv.conf 会被 NetworkManager 或 cloud-init 覆盖,也绕不过 systemd-resolved 的竞速逻辑。DNS 配置必须走 netplan。

如果根本不需要内网 hostname 解析,最干净的方案是直接只填公共 DNS:

nameservers:
  addresses: [223.5.5.5, 119.29.29.29]

零污染机会,省去排查路由器策略的麻烦。

一句话外卖

systemd-resolved 多 DNS 是竞速而非兜底;NXDOMAIN 是终止信号,把干净的 DNS 放第一才有意义。