现代 Linux 真的还挺烦的,systmed-resolved 整出来这一堆“新行为”,我们还只能跟着改。。。

遇到的问题:Ubuntu 设置 static IP 的时候,不是给进去多个 DNS 吗。按照我之前的理解, 这几个DNS是有顺序的,只有当前面DNS确实down 了(不仅仅是 resolve 不了某一个 domain),才会用到后面的。

但是实际情况是,Ubuntu (24.04 但是可能 20.04 就是这样了。)上面,并不会遵守你给定的这个顺序。 而是系统会自己按照自己测算出来的结果,选择一个使用,而且,这个选择的还是可能经常变动的。

某种意义上来说,算是提高可靠性的一个改动。但是这个 DNS 的顺序在很多地方都应该是有用的,现在居然 直接就把行为改了,总感觉不少系统可能升级的时候,都有不少麻烦。

问题和解决方法

真正的问题:systemd-resolved 默认选择最快的 DNS 服务器(如 1.1.1.1),但无法解析本地域名(如 nas.lan)。

解决方案:为特定域名指定 DNS 服务器

注意事项:

  • /etc/systemd/resolved.conf.d 文件夹可能不存在,需要手动创建。
  • Domains=~. 的含义是:将 ~. 作为域名后缀来解析本地域名,如 nas
  • systemd-resolved 的行为模式:对于静态 IP 和 DHCP 给的 DNS,都会进行性能测试,选择最快的服务器作为当前服务器。

步骤:

  1. 确定本地域名(或域后缀)
  2. 创建 resolved 配置文件:sudo vim /etc/systemd/resolved.conf.d/99-local-dns.conf(可能需要创建文件夹)
  3. 添加配置内容:
    • [Resolve]
    • DNS=192.168.1.252
    • Domains=~.(或 Domains=lan)(或者如果多个域名的话,用空格隔开 Domains=~. lan local mycorp.internal home.arpa
  4. 保存并退出
  5. 重启 systemd-resolved 服务:sudo systemctl restart systemd-resolved
  6. 验证结果:resolvectl statusresolvectl query nas(或 resolvectl query google.com

NOTE

~. 的意思是:**所有不带’.‘的域名,比如就一个单词:nas 或者 gamebox 这种。

systemd-resolved 的行为模式:

  • 静态 IP 和 DHCP 给的 DNS 都会进行性能测试,选择最快的服务器作为当前服务器。
  • 如果主 DC 因为负载高而响应慢,systemd-resolved 会自动切换到辅 DC。
  • 故障快速恢复:systemd-resolved 会在发现第一个服务器性能下降时就已经主动切换到了更好的服务器上。

Best practice:

  • 使用 systemd-resolved 的分离 DNS 功能,为特定域名(如 .corp.local)强制指定解析服务器。
  • 不要强制 systemd-resolved 改变它的性能优先逻辑,而是使用它的分离 DNS 功能来保证内部解析的正确性和外部解析的性能。

History (from Gemini):

没错,systemd-resolved 这种智能的、基于性能的 DNS 解析行为是 Linux(尤其是 Ubuntu)在网络管理方面一个重要的演进。在此之前,行为确实是您所描述的那样——严格遵守顺序。

转折点:systemd-resolved 的引入

这个行为的转变始于 systemd-resolved 服务成为系统默认 DNS 解析器的那个版本

  • 首次默认启用: Ubuntu 16.10 (Yakkety Yak) 是第一个默认使用 systemd-resolved 的 Ubuntu 版本。
  • LTS 版本确立: 这一变化在更为重要的长期支持版 Ubuntu 18.04 LTS (Bionic Beaver) 中得到了全面确立和普及。

因此,可以认为从 Ubuntu 18.04 LTS 开始,绝大多数用户都开始体验到这种新的 DNS 解析行为。在此之前的 Ubuntu 版本,特别是广为使用的 Ubuntu 16.04 LTS (Xenial Xerus) 及更早版本,都采用的是传统的、严格遵守顺序的方式。


在此之前:严格遵守顺序的传统方式 (/etc/resolv.conf)

systemd-resolved 时代之前,Linux 系统的 DNS 解析主要依赖于一个非常简单直接的机制,其核心是 /etc/resolv.conf 文件和 GNU C 库(glibc)的解析器。

工作方式如下:

  1. 核心配置文件: 系统的 DNS 服务器地址被直接写入 /etc/resolv.conf 文件中,格式为 nameserver <IP 地址>
  2. 严格的顺序: 当一个应用程序需要解析域名时,glibc 解析器会读取这个文件,并严格按照从上到下的顺序尝试 nameserver 列表。
  3. 基于超时的故障切换:
    • 解析器向第一个 DNS 服务器发送查询。
    • 如果服务器在设定的超时时间(通常是 5 秒)内没有响应,解析器会放弃它。
    • 然后,它会向第二个 DNS 服务器发送相同的查询。
    • 这个过程会一直持续下去,直到有一个服务器成功响应,或者列表中的所有服务器都尝试失败。

这种传统方式的缺点非常明显:

  • 故障切换缓慢: 如果列表中的第一个 DNS 服务器宕机或网络不通,那么系统上每一个新的 DNS 查询都会先经历一个长达数秒的超时等待,然后才能轮到第二个服务器。这会导致用户感受到明显的网络卡顿。
  • 无法利用性能: 即使第二个 DNS 服务器的响应速度比第一个快得多(例如 10ms vs 100ms),系统也毫无办法,只能“死板地”每次都先去问那个慢的服务器。
  • 网络切换时管理复杂: 对于频繁切换网络环境的笔记本电脑,管理 /etc/resolv.conf 很容易出错,常常需要手动干预或依赖 resolvconf 等辅助工具。

行为对比总结

特性 (Feature)传统方式 (Ubuntu 16.04 及更早)systemd-resolved 方式 (Ubuntu 18.04 及之后)
核心机制glibc 解析器直接读取 /etc/resolv.confsystemd-resolved 作为中心化的解析服务
选择逻辑严格按顺序,从上到下基于性能,动态选择响应最快的服务器
故障切换被动、基于超时 (非常慢)主动、基于健康和性能 (非常快)
优点逻辑简单,行为可预测解析速度快,故障无感知切换,功能强大 (支持 Split DNS, DNS-over-TLS 等)
缺点故障切换慢,无性能优化,管理不便行为初看可能不符合直觉(如本文的讨论),配置方式与传统不同

总结来说:是的,在 systemd-resolved 成为主流之前,整个 Linux 世界的 DNS 解析行为就是严格遵守 /etc/resolv.conf 里的 nameserver 顺序的。这个转变是为了解决传统方式在现代网络环境下(尤其是移动设备和复杂网络中)暴露出的严重性能和体验问题而进行的一次重要进化。


Other closely related notes: