碰到好几次这种事了:一台Ubuntu 24.04 LTS主机,早上还能正常访问,中午突然所有网页都打不开,ssh倒是没断开。远程上去敲了两条命令,ping 8.8.8.8通,ping www.baidu.com却直接报Temporary failure in name resolution。看到这个报错基本就锁定了问题方向:Ubuntu网络配置里的DNS解析链路出了问题。这篇文章就围绕这个场景展开,从故障定位、原理拆解到最终修好,把DNS解析失败的诊断过程和解决思路完整走一遍。无论你是刚接触Linux服务器运维,还是在开发机上被域名解析卡了许久,这套排查方法都能直接用。
1. 故障定位:先搞清楚到底是不是DNS在背锅
1.1 分清“链路问题”和“解析问题”
排查网络故障最忌讳一上来就乱改配置。我见过太多人看到“上不了网”就直接把DNS改成8.8.8.8或者223.5.5.5,结果改完还是不通,白折腾半小时。正确做法是先确认物理链路和IP路由是否正常,再判断DNS是否真的出了问题。
第一步判断链路是否通畅。如果你能ping通一个公网IP(比如ping 8.8.8.8),说明网卡、IP地址、默认路由、物理链路都是通的。链路没问题,而ping域名报错,这时才可以把嫌疑集中到DNS解析上。反过来,如果连公网IP都不通,那你首先要排查的是网线、Wi-Fi连接、IP地址冲突、网关配置,而不是DNS。
判断DNS问题的典型信号有三个,按出现频率排序:
ping: www.example.com: Temporary failure in name resolutioncurl: Could not resolve host: www.example.com- 浏览器一直转圈,最终提示“找不到服务器IP地址”
这类报错有一个共同点:系统不是“连不上”某个地址,而是根本没拿到这个地址对应的IP。就像一个快递包裹,不是送不到收件人手里,而是压根就不知道收件人住在哪条街。
1.2 用命令验证解析链路
确认疑似DNS问题后,第二步就是用工具直接试探解析链路。我常用的命令是nslookup和dig,Ubuntu默认装了nslookup,如果没有就装一下dnsutils(sudo apt install dnsutils,里面包含dig、nslookup、host)。
先不指定DNS服务器,直接解析一个域名,看系统默认解析结果:
nslookup www.baidu.com如果系统使用的DNS服务器本身有问题,nslookup会返回类似SERVFAIL或connection timed out。这时候可以马上指定一个公共DNS再做一次,判断问题出在“系统当前使用的DNS服务器”还是“系统解析链路本身”:
nslookup www.baidu.com 223.5.5.5这里223.5.5.5是阿里公共DNS,另外常见的还有腾讯119.29.29.29、Google的8.8.8.8、Cloudflare的1.1.1.1。如果指定公共DNS之后能正常返回IP地址,基本可以断定:你的网络连接没坏,麻烦出在系统拨号或配置文件里指定的DNS服务器上。这一步只花十秒,但能把排查范围缩小一大半。
实操中我还习惯配合dig多看一层信息:
dig +short www.baidu.com dig @223.5.5.5 +short www.baidu.comdig的输出比nslookup更丰富,能看到查询耗时、上游服务器返回状态码,还支持+trace参数逐级查看根域、顶级域的解析过程,定位是哪一级解析卡住了,非常直观。
1.3 确认当前“谁在提供DNS服务”
既然知道了问题出在DNS服务器,第三步就是搞清楚系统到底在用哪个DNS服务器。看/etc/resolv.conf是最直接的入口:
cat /etc/resolv.conf执行完你会看到nameserver 127.0.0.53这样的内容。这是一个非常典型的Ubuntu配置现象——系统把解析请求转发给本机的systemd-resolved服务,再由它去查询真正的上游DNS。也就是说,/etc/resolv.conf里面的地址不一定代表实际的DNS服务器。
想看到真正生效的DNS服务器地址,需要用另一个命令:
resolvectl status这个命令会分链路(网卡)列出当前生效的DNS服务器、搜索域、解析协议。如果机器上有多块网卡(比如虚拟机里常见ens33、ens38),每一块的DNS配置都会列出来。看到实际生效的DNS服务器之后,再跟网络环境中“应该使用”的DNS地址对比,马上就能判断是配置错了还是上游DNS不可达。
提示:很多人在这一步就直接去改
/etc/resolv.conf。在传统Linux发行版上这样做没问题,但在Ubuntu(尤其是18.04之后的版本)上,这个文件通常由systemd-resolved托管,手动改完重启网络服务甚至重启机器就会被覆盖。关于这一点,第二章节会重点展开。
2. 原理复盘:Ubuntu的DNS解析链路为什么这么绕
2.1 /etc/resolv.conf 只是个“转发窗口”
很多老教程会告诉你“改DNS就编辑 /etc/resolv.conf ”,这句话在CentOS 6时代是成立的,在今天的Ubuntu上已经不完全适用。你先执行一条命令:
ls -l /etc/resolv.conf大概率会看到这样的输出:
lrwxrwxrwx 1 root root 29 ... /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf也就是说,/etc/resolv.conf本身是一个软链接,真正的内容由systemd-resolved动态生成。systemd-resolved是本机的一个DNS转发缓存服务,它监听127.0.0.53这个本地地址,收到应用程序的解析请求后,根据各网卡当前的DNS配置向上游发起查询。
这套设计的初衷是好的:统一管理多网卡的DNS配置、提供本地缓存、支持域名搜索域按链路区分。但也因为它引入了“本地转发”这一层,很多人第一次排查Ubuntu网络配置问题时会被搞晕——看到nameserver 127.0.0.53,还以为是配置错了,其实这是Ubuntu的默认正常状态。
2.2 systemd-resolved 的角色与坑
systemd-resolved的核心理念是“按链路管理DNS”。每块网卡可以有自己的DNS服务器列表和搜索域,当一个域名需要解析时,resolved会根据请求来源、域名后缀等条件选择一个合适的上游。
它有一个关键特性:如果某块网卡开启了DHCP,DHCP服务器下发的DNS地址会自动被resolved接管,覆盖你手工配置的内容。这就解释了为什么很多用户明明在netplan或者NetworkManager里手动设置了DNS,apply之后resolvectl status看到的却还是别的地——多半是DHCP的自动DNS又把它覆盖回去了。
另一个常见坑是DNS搜索域(search domain)串线。公司内网环境里,如果一块网卡配置了search example.com,另一块没配,解析gitlab这样的短主机名时,系统可能先尝试gitlab.example.com,失败后才试gitlab。这个机制本身没问题,但在多网卡环境下容易造成“时好时坏”的诡异现象。
2.3 Desktop和Server的DNS管控制度并不相同
Ubuntu桌面版和服务器版在网络配置走的是不同路线,这一点不搞清楚,排查的时候会不断踩坑。
桌面版(Desktop)默认使用NetworkManager作为网络管理后端,无线网络、有线网络的连接配置都由NetworkManager管理。DNS配置既可以通过图形界面改,也可以通过nmcli命令行改。服务器版(Server)默认使用systemd-networkd配合netplan管理,纯命令行操作。
netplan是Ubuntu官方推动的“网络配置抽象层”,它读取/etc/netplan/*.yaml文件,把抽象的网络配置转换成systemd-networkd或NetworkManager能识别的具体配置。也就是说,netplan不是网络服务本身,而是一个“翻译官”。
正因为多了这层翻译,操作时就要格外小心:如果YAML文件格式有问题,netplan apply会直接报错拒绝执行;如果renderer指定错了,配置可能被当前的后端忽略;如果你同时改了NetworkManager的连接配置,netplan的配置又会被覆盖回去。
2.4 完整配置链:为什么改了总是不生效
把上面这些串起来,Ubuntu的DNS配置链路大致是:
用户配置层(netplan yaml 或 NetworkManager 连接配置) ↓ 管理后端(systemd-networkd 或 NetworkManager) ↓ DNS服务托管(systemd-resolved) ↓ 应用程序读取(/etc/resolv.conf,实际是stub链接)任何一个环节配置被覆盖,最终生效的DNS就跟你的期望不一致。最常见的两种情况:
第一,在netplan里配置了DNS,但DHCP自动获取的DNS优先级更高,系统依然使用DHCP下发的地址。第二,直接编辑/etc/resolv.conf,但软链接指向systemd-resolved生成的stub文件,网络服务一重启,修改内容就没了。
理解这条链路之后,“改完重启就还原”就是必然结果,而不是什么玄学。
3. 实操落地:从临时修改到持久化配置的完整流程
3.1 应急手段:临时生效但不持久
如果线上服务正在跑,来不及慢慢查配置,可以先临时指定DNS让系统恢复解析能力。最简单粗暴的方式是直接覆盖 resolv.conf,但前提是你已经知道真实DNS服务器是谁:
sudo sh -c 'echo "nameserver 223.5.5.5" > /etc/resolv.conf'注意,写之前最好备份一下原文件:
cp /etc/resolv.conf /etc/resolv.conf.bak sudo sh -c 'echo "nameserver 223.5.5.5" > /etc/resolv.conf'这个操作立刻生效,应用程序马上可以解析域名。但它是临时方案,因为文件是systemd-resolved动态管理的,重启network服务或者机器之后会被重置成原来的stub链接。另外,如果你当前的resolv.conf是软链接,直接重定向写入不会破坏软链接本身吗?会,我这里用的是sh -c 'echo ... > file',它会把文件内容替换掉,但保留的是那个软链接目标文件。相对稳妥的做法还是通过resolvectl来临时改:
sudo resolvectl dns ens33 223.5.5.5 8.8.8.8 sudo resolvectl domain ens33 example.com这里ens33替换成你实际的网卡名称,可以用ip a或nmcli device status查看。这种改法直接操作systemd-resolved的运行时状态,不需要动任何配置文件,测试完还可以用resolvectl revert恢复。适合临时验证某个DNS服务器是否可用,验证通过后再去改持久化配置。
验证是否生效:
resolvectl status ens33 ping www.baidu.com如果解析和访问都恢复正常,说明问题确实在于原来的DNS服务器不可用或配置错误。接下来就可以踏踏实实做持久化配置。
3.2 持久化方案一:修改netplan配置(服务器场景)
服务器版Ubuntu最推荐的持久化修改位置是/etc/netplan/下的YAML文件。先看看当前netplan长什么样:
ls /etc/netplan/ cat /etc/netplan/01-netcfg.yaml # 文件名可能不同常见初始配置是dhcp4自动获取。假如你想固定DNS,可以这样改:
network: version: 2 renderer: networkd ethernets: ens33: dhcp4: true dhcp4-overrides: use-dns: false nameservers: addresses: - 223.5.5.5 - 8.8.8.8 search: - example.com这里解释一下几个关键点:
renderer: networkd表示后端使用systemd-networkd,服务器版默认值,不要随便改成NetworkManager,除非你明确知道自己的环境是桌面版。dhcp4-overrides.use-dns: false非常重要。它的意思是“DHCP获取IP地址,但不使用DHCP下发的DNS”。如果不加这一段,DHCP服务器下发的DNS会覆盖nameservers里的配置,你改了也白改。nameservers.addresses填写你想用的DNS服务器,可以写多个。search是可选字段,配置内网域名搜索域,日常家用环境可以不写。
改完之后执行:
sudo netplan generate sudo netplan applygenerate会先检查YAML语法并把配置翻译给后端,有错会在这里报出来;apply才是真正生效的动作。如果担心apply后对SSH连接有影响,可以先备份当前网络状态,甚至可以用netplan try先试运行60秒,确认没问题再确认提交:
sudo netplan trytry设计得非常贴心:配置如果有问题,60秒后自动回滚,给你留了安全退路。远程操作服务器时我强烈建议用try代替apply。
3.3 持久化方案二:通过NetworkManager设置(桌面场景)
如果你的Ubuntu是桌面版,或者安装了NetworkManager管理网络,使用nmcli修改更直接。这样操作前先查一下当前的连接名称:
nmcli con show输出里会有一个连接名,比如有线网卡通常叫Wired connection 1。然后执行:
nmcli con mod "Wired connection 1" ipv4.dns "223.5.5.5 8.8.8.8" nmcli con mod "Wired connection 1" ipv4.ignore-auto-dns yes nmcli con up "Wired connection 1"第二条命令很关键:ipv4.ignore-auto-dns yes表示忽略DHCP下发的DNS,否则你手工设置的ipv4.dns会被自动获取的DNS覆盖。第三条命令重启连接,让配置生效。
这里顺便提醒一个桌面版容易踩的坑:很多人用图形界面“设置-网络”修改DNS,点了保存发现解析还是老样子,原因通常是DHCP自动DNS覆盖了手动配置。解决办法就是确保IPv4设置里DNS那一栏旁边勾选了“自动”关闭选项,或者在nmcli里明确设置ignore-auto-dns。
3.4 验证DNS是否彻底修复
配置完成后,不要只ping一个域名就算完。我习惯做一轮“四级验证”:
第一步,确认当前生效配置:
resolvectl status重点看目标网卡下面的DNS Servers和Current DNS Server,是否已经变成你期望的地址。
第二步,清除本地解析缓存。有时候问题出在缓存了一条坏记录,配置改了但缓存没刷新。执行:
sudo resolvectl flush-caches sudo systemctl restart systemd-resolved第三步,验证域名解析是否正常:
nslookup www.baidu.com ping www.baidu.com第四步,实际访问一次,模拟真实业务:
curl -I https://www.baidu.com如果curl能正常返回HTTP响应头,说明通过DNS获取IP、建立TCP连接、发HTTP请求整个链路都没问题了。
3.5 静态IP和固定DNS的组合配置模板
服务器场景常需要静态IP配合固定DNS,避免IP漂移导致服务不可达。netplan的完整配置模板大致这样:
network: version: 2 renderer: networkd ethernets: ens33: dhcp4: false addresses: - 192.168.1.100/24 routes: - to: default via: 192.168.1.1 nameservers: addresses: - 192.168.1.2 - 223.5.5.5 search: - internal.example.com静态配置虽然“固定”,但也要注意:如果你把DNS指向公司内网DNS服务器(比如192.168.1.2),这台服务器挂掉时,公网域名就解析不了了。很多内网DNS服务器配置了上游转发,但万一没配或者转发失败,表现就是“内网窥视正常,公网全挂”。所以我的习惯是内网DNS和公共DNS混着写,并且把内网DNS放前面:
nameservers: addresses: - 192.168.1.2 - 223.5.5.5这样内网域名优先走内网解析,公共域名走备用的公共DNS,两者互不干扰。
提示:netplan文件对缩进极其敏感,一律使用两个空格,禁止用Tab。一个常见的低级错误是addresses下面忘了加
-,或者冒号后面没空格,netplan generate直接报语法错,连配置都加载不了。
4. 高频踩坑:实际环境中那些让人头大的DNS疑难杂症
4.1 改完配置重启后又被还原
这是被问得最多的一个问题。我帮人远程排查时,第一句话通常先问:你改了哪个文件?十有八九回答是“ /etc/resolv.conf ”。这个文件的软链接机制在前面已经讲过,它本来就是动态生成的,重启还原是“设计如此”。
真正要做的就是在netplan或者NetworkManager层面修改。但还有一个更隐蔽的情况:netplan里也改了nameservers,apply之后resolvectl status显示的还是旧DNS。这时候检查一下DHCP:
- 后端是networkd:看看有没有在
dhcp4下配置dhcp4-overrides.use-dns: false。 - 后端是NetworkManager:看看连接配置里
ipv4.ignore-auto-dns是否为yes。
这条规则几乎能解决九成“改完重启失效”的问题。
4.2 虚拟机、双网卡、容器场景DNS“串线”
虚拟机场景非常典型。VMware的NAT模式里,虚拟机通过虚拟网关(通常是192.168.x.1)上网,DNS也由这个虚拟网关提供。如果你在同一台宿主机上创建了多个虚拟机网卡,或者虚拟机里既有ens33又有ens38,系统会选择哪块网卡的DNS去解析,规则不一定符合你的直觉。
用resolvectl status能看到每块网卡的DNS和路由域。当一个域名解析失败时,先确认它是不是被路由到了错误的网卡:
ip route show resolvectl status ens33 resolvectl status ens38如果发现默认路由走的是ens33,但DNS配置却在ens38上,那解析必然出问题。解决方法就是把两条链路的DNS都配置成一致,或者干脆把不用那块网卡的DNS关掉。
Docker容器也有类似的坑。容器默认的DNS是127.0.0.11,这是Docker自带的嵌入式DNS server,它会把无法解析的请求转发给宿主机的DNS。如果宿主机DNS配置有问题,容器里就会出现“能ping通IP但解析不了域名”的现象。排查时不要陷在容器里绕圈,回到宿主机上先确认DNS是否正常,往往一测就破案。
4.3 IPv6解析优先导致的“慢半拍”
很多用户遇到过这种场景:DNS服务器明明是通的,域名最终也能解析,但每次访问网页都卡好几秒才出内容。这往往不是DNS“失败”,而是IPv6优先解析惹的祸。
系统可能同时解析出IPv6的AAAA记录和IPv4的A记录,如果系统优先尝试IPv6地址,而当前网络环境根本没有可用的IPv6出口(或者IPv6路由质量极差),TCP连接就会一直等超时,超时后才退回IPv4,体感上就是“每次访问都卡几秒”。
我自己的处理方式是先确认是不是IPv6问题:
dig AAAA baidu.com +short如果确实返回了IPv6地址,而你的网络环境IPv6不可用,可以在/etc/gai.conf里调整地址选择优先级,让IPv4优先:
sudo sh -c "echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf"这个配置的作用是告诉系统“IPv4映射地址优先选择”,避免IPv6超时拖慢整体体验。改完保存即刻生效。如果你的网络环境没有IPv6,也可以考虑在netplan网卡配置里直接禁用IPv6相关功能,但具体操作取决于上游网络还发不发路由器通告(RA),不少环境折腾起来比gai.conf复杂得多,我多数情况下会用gai.conf来做。
4.4 自建DNS服务器之后,内网域名解析不稳定
很多团队发展到一定阶段会自建DNS服务器来解析内部服务名(比如gitlab.example.com指向内网某台机器),我用得最多的是dnsmasq,配置轻量、占用少,比自建BIND的维护成本低一个数量级。
dnsmasq做内网域名解析时,一个简洁的配置片段长这样:
resolv-file=/etc/resolv.dnsmasq server=/internal.example.com/192.168.1.2 server=/#/223.5.5.5其中resolv-file指定普通域名走哪份上游DNS;server=/internal.example.com/192.168.1.2表示*.internal.example.com全部交给内网DNS解析;server=/#/223.5.5.5表示剩下的所有域名默认走阿里公共DNS。
这块特别容易踩的坑是防火墙。DNS默认使用UDP/TCP 53端口,自建DNS服务器后,如果服务器自身防火墙没放行53端口,或者网络ACL没放行,内网其他机器查过来就超时。排查时在客户端执行:
timeout 3 bash -c 'echo > /dev/tcp/192.168.1.2/53' && echo "TCP 53 OK"如果提示超时,先去检查防火墙规则。
4.5 高频排查命令速查表
我把日常排查DNS问题用到的命令整理成一个速查表,贴在终端旁边,遇到问题先按顺序过一遍:
| 排查目的 | 命令 | 预期结果说明 |
|---|---|---|
| 确认基础链路 | ping -c 4 8.8.8.8 | 能通说明链路和IP正常 |
| 确认域名解析 | ping -c 2 www.baidu.com | 报unknown host或Temporary failure即DNS问题 |
| 查询当前DNS服务器 | resolvectl status | 看是否存在异常或空配置 |
| 测试指定DNS解析 | nslookup www.baidu.com 223.5.5.5 | 能解析说明本地DNS配置有问题 |
| 查看解析细节 | dig +trace www.baidu.com | 定位哪一级解析中断 |
| 清空缓存 | sudo resolvectl flush-caches | 改完DNS后建议执行一次 |
| 临时设置DNS | sudo resolvectl dns ens33 223.5.5.5 | 应急恢复解析能力 |
| 确认53端口 | timeout 3 bash -c 'echo > /dev/tcp/223.5.5.5/53' | 不通说明上游DNS侧链路被阻断 |
这套命令组合足够覆盖单台主机的大多数DNS故障场景。如果整套流程走完还是无法解析,再去考虑是不是系统本身被某种安全软件或者异常服务劫持了解析流程。
最后分享一点我个人的工作习惯。网络排障这件事,最大的成本不是敲命令,而是“猜”。环境越复杂,越要克制住直接改配置的冲动。我现在遇到DNS问题,第一件事永远是跑一遍resolvectl status看当前到底谁在管解析,第二件事是验证“用指定DNS服务器手动解析一下”,第三件事才是动手改。多数疑难杂症到第二步就已经能定位了。另外,建议装好系统之后就把网络基线信息记下来,网关IP、DNS、DHCP还是静态、网卡列表都写进笔记。下次再出问题,对比基线信息,往往一眼就能看出哪里被人动过,排查速度会快很多。