1. 问题现象与根源剖析
如果你在Ubuntu 20.04上手动修改了DNS设置,比如编辑了
/etc/resolv.conf
文件,或者通过
nmcli
、
netplan
等工具配置了静态DNS,但重启网络服务、重启系统,甚至只是等上一段时间后,发现DNS又变回了原来的样子——最常见的是被重置为
127.0.0.53
,指向本地的systemd-resolved服务,那么恭喜你,你遇到了一个非常典型的“现代Linux网络管理”问题。这绝不是你的操作有误,而是Ubuntu 20.04默认的网络管理架构在背后“自作主张”。很多从老版本迁移过来,或者习惯了传统
/etc/network/interfaces
配置方式的朋友,第一次遇到这个问题时都会感到困惑和恼火。
简单来说,问题的核心在于
“管理权冲突”
。在Ubuntu 20.04中,DNS的解析权默认被一个名为
systemd-resolved
的系统服务牢牢掌控。这个服务设计了一套自己的配置逻辑和优先级,它会监听并管理
/etc/resolv.conf
这个文件。你手动修改这个文件,就像在一个被自动同步的云文档里直接打字,保存后很快就会被系统进程覆盖回去。因此,理解
systemd-resolved
的工作机制,是解决这个问题的唯一钥匙。
这个问题的普遍性,从你提供的那些热搜词就能看出来:“linux修改dns后重启网络+还原”、“dns永久生效”、“ubuntu20.04安装后没有wifi”(网络问题常伴随DNS异常)等等。无论是为了获得更快的解析速度(如使用
114.114.114.114
或
8.8.8.8
),还是为了内网开发需要指定特定的DNS服务器(比如解析内部域名),亦或是为了解决某些网站访问异常的问题,我们都需要一个稳定、持久的DNS配置方案。
注意:在开始任何操作前,请先确认你的网络连接方式。是通过NetworkManager管理的无线/有线连接,还是通过
netplan配置的静态网络(常见于服务器),亦或是古老的/etc/network/interfaces?不同的管理工具,其“正确”的配置姿势也略有不同,但最终大多需要与systemd-resolved“打好招呼”。
2. 理解幕后主角:systemd-resolved 服务
要解决问题,必须先了解“对手”。
systemd-resolved
是systemd项目的一部分,它提供了一个本地DNS解析器,主要目的是为了提升DNS解析的可靠性、安全性和性能(如支持DNSSEC、DNS-over-TLS等)。在Ubuntu 20.04上,它是默认启用并处于活跃状态的。
它的工作模式主要有三种,可以通过
/etc/systemd/resolved.conf
文件中的
DNSStubListener
选项来控制:
-
整合模式(Integration Mode)
:默认模式。
systemd-resolved会生成一个临时的/etc/resolv.conf文件,并将其软链接(或直接覆盖)到标准的/etc/resolv.conf位置。这个临时文件里通常只有一行:nameserver 127.0.0.53。所有应用程序的DNS查询都会先发往本地的53端口,由systemd-resolved统一处理,它再根据其内部配置向上游DNS服务器发起查询。 -
转发模式(Forwarding Mode)
:
systemd-resolved仍然运行,但/etc/resolv.conf指向真实的上游DNS服务器。此时systemd-resolved可能只负责缓存或处理特定类型的查询。 -
禁用模式(Disabled Mode)
:完全停用
systemd-resolved的本地解析功能,/etc/resolv.conf由其他网络管理工具完全控制。
我们遇到的“自动重置”问题,绝大多数发生在
整合模式
下。因为在此模式下,
/etc/resolv.conf
被
systemd-resolved
视为一个由其管理的“符号链接”或“动态文件”。你直接修改它,相当于破坏了它的管理规则,它会在下次网络状态变更(如重启
systemd-resolved
服务、NetworkManager重连)时,依据其内部状态重新生成该文件。
那么,
systemd-resolved
的“内部状态”从哪里来?主要来自以下几个地方,按优先级从高到低排列:
-
每个链接(Per-Link)的DNS配置
:这是最推荐的方式。通过NetworkManager或
netplan为 特定的网络接口 (如eth0,wlan0)配置DNS。systemd-resolved会汇总所有活跃接口的DNS设置。 -
全局DNS配置
:在
/etc/systemd/resolved.conf中设置的DNS=参数。 -
从DHCP获取的DNS
:如果你的网络是通过DHCP获取IP的,那么DHCP服务器下发的DNS地址也会被
systemd-resolved接收。
所以,我们的目标不是去“打败”
systemd-resolved
,而是学会如何“正确地告诉”它我们想要的DNS服务器是什么。
3. 解决方案一:通过 Netplan 配置静态DNS(服务器/桌面版通用)
对于Ubuntu 20.04服务器版,或者桌面版中使用了
netplan
进行网络配置的情况(默认的服务器安装和某些云镜像常用),这是最官方、最持久的方法。
netplan
是Ubuntu 17.10以后引入的新的网络配置抽象层,它负责将简洁的YAML配置,在后台渲染成
systemd-networkd
或NetworkManager所能理解的配置。
3.1 定位并编辑 Netplan 配置文件
首先,找到你的Netplan配置文件。它们通常位于
/etc/netplan/
目录下,文件名可能是
01-netcfg.yaml
、
50-cloud-init.yaml
或
00-installer-config.yaml
等。
sudo ls -lh /etc/netplan/
使用你喜欢的文本编辑器(如
nano
或
vim
)打开它。这里以
nano
为例,假设文件是
00-installer-config.yaml
:
sudo nano /etc/netplan/00-installer-config.yaml
3.2 配置DNS参数
在配置文件中,你需要为对应的网络接口(如
ens33
,
eth0
)添加
nameservers
字段。下面是一个配置静态IP和DNS的完整示例:
network:
version: 2
ethernets:
ens33: # 你的网卡名称,请用 `ip a` 命令查看
addresses:
- 192.168.1.100/24 # 静态IP地址和子网掩码
routes:
- to: default
via: 192.168.1.1 # 默认网关
nameservers:
addresses:
- 8.8.8.8 # 首选DNS
- 8.8.4.4 # 备用DNS
- 114.114.114.114 # 另一个备用DNS
search: [localdomain] # 可选的搜索域
如果你使用的是DHCP获取IP,但想使用自定义DNS,可以这样配置:
network:
version: 2
ethernets:
ens33:
dhcp4: yes # 使用DHCP获取IP
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
关键点解释 :
-
nameservers下的addresses列表顺序即DNS查询的优先级。 -
即使使用DHCP,在这里指定
nameservers后,netplan也会在生成配置时,指示底层的网络管理器(systemd-networkd或NetworkManager)忽略DHCP下发的DNS,而采用此处定义的DNS。这是实现“持久化”的关键。
3.3 应用配置
编辑保存后,使用以下命令测试配置语法并应用:
# 测试配置文件语法是否正确(非常重要,避免错误配置导致断网)
sudo netplan try
# 如果上一条命令执行成功并确认,或者直接应用配置(在可远程管理的服务器上慎用,可能导致连接中断)
sudo netplan apply
执行
netplan apply
后,它会重新配置网络,并将DNS信息传递给
systemd-resolved
。此时,你可以检查
/etc/resolv.conf
,它应该仍然指向
127.0.0.53
,但通过
systemd-resolve --status
命令可以看到,上游DNS已经变成了你设置的值。
实操心得:在物理服务器或远程VPS上操作时,强烈建议先使用
sudo netplan try。这个命令会应用新配置并给你一个回滚的倒计时(默认120秒)。如果在倒计时内你失去了网络连接(比如配置写错了网关),没有在终端按回车确认,配置会在倒计时结束后自动回滚到之前的状态,这是救命的功能。在本地虚拟机或不怕断网的机器上,可以直接用apply。
4. 解决方案二:通过 NetworkManager 配置(桌面版图形界面/Cli)
对于Ubuntu 20.04桌面版,图形界面通常使用NetworkManager来管理网络。通过GUI或命令行
nmcli
来配置,效果与
netplan
类似,都是设置“每链接DNS”,能被
systemd-resolved
正确识别。
4.1 图形界面配置
这是最直观的方法:
- 点击屏幕右上角的网络图标,选择“有线设置”或“Wi-Fi设置”。
- 在弹出的设置窗口中,找到你当前连接的网络,点击旁边的齿轮图标。
- 切换到“IPv4”或“IPv6”标签页。
- 如果你的IP是自动获取(DHCP),将“自动(DHCP)”切换为“手动”。
-
在“DNS”输入框中,输入你想要的DNS服务器地址,多个DNS用逗号分隔,例如:
8.8.8.8, 114.114.114.114。 - 点击“应用”。
NetworkManager会将这个配置写入其自身的配置数据库中,并在该网络连接激活时,将此DNS信息推送给
systemd-resolved
。
4.2 命令行配置(nmcli)
如果你更喜欢命令行,或者需要在脚本中操作,
nmcli
工具非常强大。
首先,列出所有连接,找到你要修改的连接名称(
CONNECTION
列):
nmcli connection show
假设你的连接名是“有线连接 1”,你想设置DNS为
8.8.8.8
和
1.1.1.1
,并同时配置静态IP(可选):
# 修改IPv4的DNS服务器(如果使用DHCP获取IP,这个方法同样有效且持久)
sudo nmcli connection mod "有线连接 1" ipv4.dns "8.8.8.8 1.1.1.1"
# 如果你希望完全忽略DHCP下发的DNS,只使用你设置的DNS
sudo nmcli connection mod "有线连接 1" ipv4.ignore-auto-dns yes
# 如果需要同时设置静态IP、网关和DNS
sudo nmcli connection mod "有线连接 1" ipv4.method manual ipv4.addresses 192.168.1.100/24 ipv4.gateway 192.168.1.1 ipv4.dns "8.8.8.8 1.1.1.1"
# 让配置立即生效(重新激活连接)
sudo nmcli connection down "有线连接 1" && sudo nmcli connection up "有线连接 1"
# 或者使用 reload 命令
sudo nmcli connection reload
sudo nmcli connection up "有线连接 1"
关键参数解释 :
-
ipv4.dns:设置DNS服务器地址列表,用空格分隔。 -
ipv4.ignore-auto-dns:设为yes后,NetworkManager将不会使用从DHCP获取的DNS信息,这对于强制使用自定义DNS非常有用。 - 修改后必须 重启(down/up) 网络连接或重新加载配置,更改才会生效。
配置完成后,可以通过以下命令验证
systemd-resolved
接收到的DNS:
systemd-resolve --status | grep -A5 "DNS Servers"
5. 解决方案三:直接配置 systemd-resolved 全局DNS
如果你希望为所有网络连接设置一个统一的、后备的DNS,而不是针对每个连接单独设置,可以修改
systemd-resolved
的全局配置文件。这个方法简单粗暴,但优先级低于“每链接”配置。
编辑
/etc/systemd/resolved.conf
文件:
sudo nano /etc/systemd/resolved.conf
找到
[Resolve]
部分,取消
DNS
和
FallbackDNS
行的注释(删除行首的
#
),并填入你想要的DNS服务器地址。
FallbackDNS
是在
DNS
服务器不可用时的备用选择。
[Resolve]
DNS=8.8.8.8 114.114.114.114
FallbackDNS=1.1.1.1 9.9.9.9
#Domains=
#DNSSEC=no
#DNSOverTLS=no
#Cache=yes
#DNSStubListener=yes
保存文件后,重启
systemd-resolved
服务使配置生效:
sudo systemctl restart systemd-resolved
然后,检查解析状态:
systemd-resolve --status
在输出中,你应该能看到在“Global”部分下有你设置的DNS服务器。
注意事项:这种方法设置的DNS是全局性的。如果某个网络连接通过NetworkManager或netplan配置了特定的DNS(每链接DNS),那么该连接的DNS将以每链接配置为准,全局配置将不生效。这可以理解为一个“默认值”。
6. 解决方案四:禁用 systemd-resolved(传统方法,不推荐)
如果你实在无法适应
systemd-resolved
,或者某些老旧应用、脚本必须直接读取
/etc/resolv.conf
中的真实IP,你可以选择禁用它,将
/etc/resolv.conf
恢复为一个普通的静态文件。但这意味着你将失去
systemd-resolved
带来的缓存、DNSSEC等特性。
步骤1:停止并禁用服务
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
步骤2:将
/etc/resolv.conf
从符号链接改为普通文件
首先,删除现有的符号链接:
sudo rm -f /etc/resolv.conf
然后,创建新的
/etc/resolv.conf
文件并写入你的DNS服务器:
sudo nano /etc/resolv.conf
内容如下:
nameserver 8.8.8.8
nameserver 114.114.114.114
options edns0 trust-ad
步骤3:防止其他服务重新创建符号链接
需要告诉系统不要再去管理这个文件。对于使用NetworkManager的系统,还需要修改其配置,告诉它不要管理
resolv.conf
:
sudo nano /etc/NetworkManager/NetworkManager.conf
在
[main]
部分,确保或添加以下行:
[main]
dns=none
然后重启NetworkManager:
sudo systemctl restart NetworkManager
重要警告:这是最不推荐的方法,尤其是在桌面环境或依赖NetworkManager的系统中。它可能与其他系统组件产生冲突,并且在系统更新后配置可能被还原。除非你非常清楚自己在做什么,并且有充分的理由,否则请优先使用前三种“合作”方案。
7. 验证与诊断:如何确认DNS配置已生效且稳定
配置完成后,如何验证一切工作正常且不会“自动重置”呢?以下是一套组合诊断拳法。
7.1 检查 systemd-resolved 状态
这是最权威的视图,显示了
systemd-resolved
内部认为的当前DNS配置。
systemd-resolve --status
你会看到类似下面的输出,重点关注你的活动链接(如
ens33
)和“Global”部分下的“DNS Servers”:
Link 2 (ens33)
Current Scopes: DNS
LLMNR setting: yes
MulticastDNS setting: no
DNSSEC setting: no
DNSSEC supported: no
DNS Servers: 8.8.8.8
114.114.114.114
DNS Domain: ~local
如果这里显示的是你配置的DNS服务器,而不是DHCP下发的或其他奇怪的地址,说明配置已被
systemd-resolved
成功接收。
7.2 检查 /etc/resolv.conf 的实质
/etc/resolv.conf
现在通常是一个指向
/run/systemd/resolve/stub-resolv.conf
的符号链接。直接
cat
它,大概率只看到
127.0.0.53
。这
是正常的
,不代表配置失败。因为查询会发往本地的
systemd-resolved
,再由它根据你上面的配置去查询上游DNS。
你可以查看这个“存根”文件的内容:
cat /run/systemd/resolve/stub-resolv.conf
也可以查看
systemd-resolved
使用的真实上游配置:
cat /run/systemd/resolve/resolv.conf
这个文件里应该包含了你设置的真实DNS服务器IP。
7.3 使用 dig 或 nslookup 测试解析
使用
dig
命令测试域名解析,并指定查询服务器为
127.0.0.53
(即本地的
systemd-resolved
):
dig @127.0.0.53 baidu.com
在输出的“SERVER”行,你会看到
127.0.0.53#53(127.0.0.53)
。在“ANSWER SECTION”看到正确的IP地址,说明解析功能正常。
你也可以不指定服务器,直接
dig baidu.com
,默认就会使用
/etc/resolv.conf
中配置的DNS(即
127.0.0.53
)。
7.4 模拟“重置”触发条件进行验证
配置完成后,进行以下操作,然后再次执行
systemd-resolve --status
,检查DNS服务器列表是否被篡改:
-
重启网络服务:
sudo systemctl restart systemd-networkd(如果使用) 或sudo systemctl restart NetworkManager。 -
重启
systemd-resolved服务本身:sudo systemctl restart systemd-resolved。 - 断开并重新连接网络(桌面环境下点击网络图标断开再连接)。
- 最后,重启整个系统。
如果经过以上“折腾”,你配置的DNS服务器依然坚挺地显示在
systemd-resolve --status
中,那么恭喜你,DNS“自动重置”问题已被彻底解决。
8. 常见问题与排查技巧实录
即使按照上述步骤操作,你可能还是会遇到一些“坑”。以下是我在实际操作中遇到的一些典型问题及解决方法。
8.1 问题:配置了Netplan,但重启后DNS又变回DHCP下发的了。
排查思路 :
-
检查Netplan配置语法 :运行
sudo netplan generate,看是否有错误输出。一个常见的错误是YAML格式不对,比如缩进用了Tab键而不是空格。 -
检查渲染器(renderer) :确认你的Netplan配置文件指定的渲染器与系统实际使用的网络管理器一致。桌面版通常用
network-manager,服务器版用networkd。如果不确定,可以都写上:renderer: NetworkManager或renderer: networkd。 -
检查DHCP覆盖 :在Netplan配置中,即使你指定了
nameservers,如果dhcp4设为yes,某些旧版本或特定环境下,DHCP下发的DNS可能仍有较高优先级。尝试在接口配置下明确添加dhcp4-overrides: use-dns: false。network: version: 2 ethernets: ens33: dhcp4: yes dhcp4-overrides: use-dns: false # 关键:禁止使用DHCP提供的DNS nameservers: addresses: [8.8.8.8, 1.1.1.1]
8.2 问题:使用nmcli配置后,连接WiFi时DNS生效,换到有线连接又失效了。
原因与解决
:这是因为
nmcli connection mod
修改的是
某个特定连接配置文件
的DNS。你的WiFi和有线连接在NetworkManager里是两个不同的“连接”(Connection)。你需要分别为它们配置DNS。
-
使用
nmcli connection show列出所有连接,找到你的有线连接名称(例如“Wired connection 1”)。 -
对有线连接也执行一遍DNS设置命令:
sudo nmcli connection mod "Wired connection 1" ipv4.dns "8.8.8.8 1.1.1.1" ipv4.ignore-auto-dns yes。 - 重新激活该有线连接。
8.3 问题:所有方法都试了,但某些容器或虚拟机内的应用还是解析不了域名。
排查思路
:这可能是因为
systemd-resolved
的DNS存根监听器(
DNSStubListener
)没有在所有网络接口上监听。默认它只监听在回环地址
127.0.0.53
上。如果你的Docker容器或虚拟机使用的是桥接网络或另一个IP段,它们可能无法访问到这个地址。
解决方案A(推荐)
:在容器或虚拟机内,将DNS服务器直接设置为宿主机的物理IP地址(例如
192.168.1.100
),并确保宿主机的防火墙允许53端口的入站连接。
解决方案B
:修改
systemd-resolved
配置,让其监听在所有接口上(
安全性需自行评估
)。编辑
/etc/systemd/resolved.conf
,设置:
[Resolve]
DNSStubListener=yes
# 监听在特定地址,例如 0.0.0.0(所有IPv4接口)
DNSStubListenerExtra=0.0.0.0
然后重启
systemd-resolved
。之后,容器内就可以将DNS设置为宿主机的IP了。
8.4 问题:修改DNS后,网络变慢了,或者某些国内网站访问异常。
原因
:这通常是因为你使用了国外的公共DNS(如
8.8.8.8
),这些DNS服务器可能对国内CDN的解析不够优化,导致你被解析到距离很远的服务器上。
解决 :使用国内运营商或更智能的DNS。可以配置多个DNS,将国内的放在前面。例如:
-
阿里DNS
:
223.5.5.5,223.6.6.6 -
腾讯DNS
:
119.29.29.29 -
114DNS
:
114.114.114.114,114.114.115.115 -
运营商DNS
:通常网关地址就是(如
192.168.1.1),延迟最低。
在你的Netplan或NetworkManager配置中,可以这样设置:
nameservers:
addresses: [223.5.5.5, 114.114.114.114, 8.8.8.8]
这样会优先使用阿里DNS,失败时尝试114DNS,最后才用Google DNS作为保障。
8.5 终极排查工具:查看 systemd-resolved 的日志
如果问题非常诡异,可以查看
systemd-resolved
的详细日志,这能帮助你看到它到底在做什么决定。
# 查看实时日志
sudo journalctl -fu systemd-resolved
# 查看最近的相关日志
sudo journalctl -u systemd-resolved --since "5 minutes ago"
在日志中,你可以搜索“Using DNS server”(正在使用的DNS服务器)、“Fallback DNS server”(回退DNS)等关键词,来追踪DNS配置的变化来源。
我个人在实际操作中的体会是,Ubuntu 20.04的DNS管理虽然初期让人觉得复杂和“多管闲事”,但一旦理解了
systemd-resolved
作为中央管理者的角色,并学会通过
netplan
或
NetworkManager
这些“官方渠道”去和它沟通,配置反而变得更加清晰和模块化。记住黄金法则:
忘掉直接编辑
/etc/resolv.conf
这个习惯
,转而通过配置管理工具来设置“每链接DNS”或“全局DNS”,让系统服务去自动维护那个文件,这样才能一劳永逸地解决DNS被自动重置的问题。

350

被折叠的 条评论
为什么被折叠?



