Ubuntu 20.04 DNS配置持久化:解决systemd-resolved自动重置问题

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 选项来控制:

  1. 整合模式(Integration Mode) :默认模式。 systemd-resolved 会生成一个临时的 /etc/resolv.conf 文件,并将其软链接(或直接覆盖)到标准的 /etc/resolv.conf 位置。这个临时文件里通常只有一行: nameserver 127.0.0.53 。所有应用程序的DNS查询都会先发往本地的53端口,由 systemd-resolved 统一处理,它再根据其内部配置向上游DNS服务器发起查询。
  2. 转发模式(Forwarding Mode) systemd-resolved 仍然运行,但 /etc/resolv.conf 指向真实的上游DNS服务器。此时 systemd-resolved 可能只负责缓存或处理特定类型的查询。
  3. 禁用模式(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 图形界面配置

这是最直观的方法:

  1. 点击屏幕右上角的网络图标,选择“有线设置”或“Wi-Fi设置”。
  2. 在弹出的设置窗口中,找到你当前连接的网络,点击旁边的齿轮图标。
  3. 切换到“IPv4”或“IPv6”标签页。
  4. 如果你的IP是自动获取(DHCP),将“自动(DHCP)”切换为“手动”。
  5. 在“DNS”输入框中,输入你想要的DNS服务器地址,多个DNS用逗号分隔,例如: 8.8.8.8, 114.114.114.114
  6. 点击“应用”。

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服务器列表是否被篡改:

  1. 重启网络服务: sudo systemctl restart systemd-networkd (如果使用) 或 sudo systemctl restart NetworkManager
  2. 重启 systemd-resolved 服务本身: sudo systemctl restart systemd-resolved
  3. 断开并重新连接网络(桌面环境下点击网络图标断开再连接)。
  4. 最后,重启整个系统。

如果经过以上“折腾”,你配置的DNS服务器依然坚挺地显示在 systemd-resolve --status 中,那么恭喜你,DNS“自动重置”问题已被彻底解决。

8. 常见问题与排查技巧实录

即使按照上述步骤操作,你可能还是会遇到一些“坑”。以下是我在实际操作中遇到的一些典型问题及解决方法。

8.1 问题:配置了Netplan,但重启后DNS又变回DHCP下发的了。

排查思路

  1. 检查Netplan配置语法 :运行 sudo netplan generate ,看是否有错误输出。一个常见的错误是YAML格式不对,比如缩进用了Tab键而不是空格。

  2. 检查渲染器(renderer) :确认你的Netplan配置文件指定的渲染器与系统实际使用的网络管理器一致。桌面版通常用 network-manager ,服务器版用 networkd 。如果不确定,可以都写上: renderer: NetworkManager renderer: networkd

  3. 检查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。

  1. 使用 nmcli connection show 列出所有连接,找到你的有线连接名称(例如“Wired connection 1”)。
  2. 对有线连接也执行一遍DNS设置命令: sudo nmcli connection mod "Wired connection 1" ipv4.dns "8.8.8.8 1.1.1.1" ipv4.ignore-auto-dns yes
  3. 重新激活该有线连接。

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被自动重置的问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值