在Ubuntu系统运维中,DNS解析故障是最常见的网络问题之一,而通过resolv.conf配置DNS故障转移(DNS Failover)是解决这一问题最直接、最有效的手段。核心思路就是在resolv.conf中配置多个DNS服务器地址,当主DNS服务器不可用时,系统自动切换到备用DNS服务器,保证域名解析不中断。Ubuntu默认使用systemd-resolved或者NetworkManager来管理DNS,直接修改resolv.conf可能会被覆盖,所以需要掌握正确的配置方法,包括传统方式和systemd-resolved方式两种路径。

一、Ubuntu DNS解析机制的基本认识

Ubuntu系统的DNS解析配置文件位于/etc/resolv.conf,这个文件通常是一个符号链接,指向systemd-resolved生成的stub文件(/run/systemd/resolve/stub-resolv.conf)或者NetworkManager管理的文件。文件内容一般包含nameserver指令,每行一个DNS服务器IP地址。系统按照从上到下的顺序依次尝试DNS服务器,第一个能响应的就被使用,这就是天然的故障转移机制。但问题在于,如果你直接编辑/etc/resolv.conf,重启后配置可能丢失,所以必须从源头去配置。

二、传统方式:直接修改resolv.conf并锁定配置

如果你的Ubuntu版本较老(18.04之前),或者你不使用systemd-resolved,可以直接编辑resolv.conf。打开文件:

sudo nano /etc/resolv.conf

写入多个DNS服务器地址,例如:

nameserver 114.114.114.114
nameserver 8.8.8.8
nameserver 223.5.5.5

这里114.114.114.114是主DNS,8.8.8.8和223.5.5.5是备用。系统会优先使用第一个,如果第一个超时(默认5秒),就尝试第二个。为了防止配置被覆盖,需要安装resolvconf包并锁定文件:

sudo apt install resolvconf
sudo chattr +i /etc/resolv.conf

chattr +i命令给文件加了不可修改属性,即使systemd-resolved尝试重写也会失败。但这种方式在新版Ubuntu上并不推荐,因为它和systemd体系存在冲突。

三、systemd-resolved方式:正确的现代配置方法

Ubuntu 18.04及之后的版本默认启用systemd-resolved作为DNS解析服务。正确的做法是修改systemd-resolved的配置文件,而不是直接动resolv.conf。

编辑/etc/systemd/resolved.conf文件:

sudo nano /etc/systemd/resolved.conf

找到或添加以下配置项:

[Resolve]
DNS=114.114.114.114 8.8.8.8 223.5.5.5
FallbackDNS=1.1.1.1 9.9.9.9
DNSStubListener=yes
DNS=127.0.0.53

这里需要特别说明:DNS指令中多个IP用空格分隔,系统会按顺序尝试。FallbackDNS是当所有配置的DNS都不可用时的兜底方案。DNSStubListener=yes表示systemd-resolved在本地127.0.0.53提供DNS服务。配置完成后重启服务:

sudo systemctl restart systemd-resolved

然后验证配置是否生效:

resolvectl status

输出中会显示当前使用的DNS服务器列表,确认多个DNS都已生效。此时/etc/resolv.conf会自动更新为指向127.0.0.53,这是正常的,不需要手动修改它。

四、NetworkManager方式:适用于桌面版和无线网络环境

如果你的Ubuntu是桌面版,网络由NetworkManager管理,那么需要通过nmcli或者nmtui来配置DNS。使用nmcli命令:

nmcli con mod "你的连接名称" ipv4.dns "114.114.114.114 8.8.8.8 223.5.5.5"
nmcli con mod "你的连接名称" ipv4.dns-search "yourdomain.com"
nmcli con mod "你的连接名称" ipv4.ignore-auto-dns yes
nmcli con up "你的连接名称"

关键参数ipv4.ignore-auto-dns yes的作用是告诉NetworkManager忽略DHCP分配的DNS,使用我们手动指定的。如果不加这一行,DHCP可能会覆盖你的配置。对于无线网络,同样适用这套命令。可以用nmcli con show查看所有连接名称。

五、DNS故障转移的超时和重试策略

默认情况下,系统对每个DNS服务器的超时时间是5秒。如果主DNS挂了,等5秒后才会尝试备用DNS,这在某些场景下可能太慢。可以通过修改systemd-resolved的超时参数来优化。编辑/etc/systemd/resolved.conf:

[Resolve]
DNS=114.114.114.114 8.8.8.8 223.5.5.5
Timeout=2
Attempts=3

Timeout=2表示每个DNS服务器等待2秒,Attempts=3表示每个DNS尝试3次。这样总的故障检测时间可以控制在6秒以内,比默认的5秒单次超时更灵活。但要注意,Timeout值不能设得太小(比如低于1秒),否则正常网络波动也会触发不必要的切换。

六、使用resolv.conf的options参数优化解析行为

在resolv.conf中可以添加options指令来控制解析行为,虽然在systemd-resolved体系下这些参数通常放在resolved.conf中,但了解它们很有必要:

options timeout:2 attempts:3 rotate

rotate参数表示轮询使用多个DNS服务器,而不是总是从第一个开始。这在负载均衡场景下很有用,避免所有请求都打到同一个DNS上。但对于故障转移场景,一般不建议加rotate,因为你希望主DNS优先,只有它挂了才用备用。如果你确实需要在resolved.conf中设置,写法是:

[Resolve]
DNS=114.114.114.114 8.8.8.8 223.5.5.5
Options=timeout:2 attempts:3

七、验证DNS故障转移是否真正生效

配置完成后必须验证。最直接的方法是模拟DNS故障:

# 先测试主DNS是否正常
dig @114.114.114.114 example.com

# 然后用iptables模拟主DNS不可达(测试环境中)
sudo iptables -A OUTPUT -d 114.114.114.114 -p udp --dport 53 -j DROP

# 再次测试解析
dig example.com

# 恢复规则
sudo iptables -D OUTPUT -d 114.114.114.114 -p udp --dport 53 -j DROP

如果dig命令在主DNS被阻断后仍然能返回结果,说明故障转移生效了。另外可以用resolvectl query example.com查看当前使用的是哪个DNS服务器,确认切换逻辑正确。

八、生产环境中的注意事项和最佳实践

第一,不要把所有DNS都配置成同一个运营商的,比如全用电信的DNS,万一该运营商出口故障,所有DNS都不可用。建议主备DNS选择不同运营商或不同服务商,比如114(国内)、阿里DNS(223.5.5.5)、腾讯DNS(119.29.29.29)搭配使用。

第二,DNS服务器数量建议2到3个,不要超过4个。太多DNS会增加解析延迟,因为系统要逐个尝试。三个足够覆盖大多数故障场景。

第三,定期检查DNS配置是否被意外覆盖。可以写一个简单的监控脚本:

#!/bin/bash
EXPECTED_DNS="114.114.114.114 8.8.8.8 223.5.5.5"
ACTUAL_DNS=$(resolvectl status | grep "DNS Server" | awk '{print $3}' | tr '\n' ' ')
if [ "$ACTUAL_DNS" != "$EXPECTED_DNS" ]; then
    echo "DNS配置异常,当前: $ACTUAL_DNS" | mail -s "DNS Alert" admin@example.com
fi

第四,如果是服务器环境,建议关闭systemd-resolved的DNS缓存过期策略,避免缓存了错误的解析结果。在resolved.conf中可以设置Cache=no,但这会增加DNS查询量,需要权衡。

第五,对于容器化部署的Ubuntu环境(如Docker),容器内的resolv.conf通常继承宿主机配置,但如果使用了自定义网络,需要在docker run时指定--dns参数或者在daemon.json中全局配置:

{
  "dns": ["114.114.114.114", "8.8.8.8", "223.5.5.5"]
}

九、常见问题排查

如果配置后DNS仍然不生效,按以下步骤排查:首先确认systemd-resolved是否在运行(systemctl status systemd-resolved);其次检查/etc/resolv.conf是否是符号链接且指向正确;再次确认resolved.conf中没有语法错误(可以用resolved.conf的man手册检查格式);最后查看journalctl -u systemd-resolved日志,看是否有报错信息。

另一个常见问题是DHCP覆盖DNS配置。如果你的网络通过DHCP获取IP,DHCP服务器可能会下发自己的DNS地址。解决办法就是前面提到的在NetworkManager中设置ipv4.ignore-auto-dns yes,或者在netplan配置中明确指定DNS并覆盖DHCP:

network:
  version: 2
  ethernets:
    eth0:
      dhcp4: true
      nameservers:
        addresses: [114.114.114.114, 8.8.8.8, 223.5.5.5]

netplan是Ubuntu 18.04之后推荐的网络配置工具,通过/etc/netplan/*.yaml文件管理,配置后执行sudo netplan apply生效。这种方式配置的DNS不会被DHCP轻易覆盖,是生产环境中最稳妥的方案之一。

十、总结

Ubuntu运维中配置DNS故障转移并不复杂,关键是选对方法。新版系统优先使用systemd-resolved或netplan来管理DNS,避免直接修改resolv.conf。配置多个不同来源的DNS服务器,设置合理的超时和重试参数,定期监控验证,就能确保在任何单个DNS故障时业务不受影响。记住核心原则:多源DNS、正确配置入口、定期验证,这三点做到了,DNS故障转移就稳了。