运维人应该都遇见过这种糟心事:周一上班,业务报网站访问失败。团队一半人怀疑是应用 bug,另一半人觉得网络出问题,折腾半天,最后发现罪魁祸首居然是 DNS 解析故障。
DNS 问题隐蔽性很强,表象五花八门:有的域名能解析有的不行;dig 命令测试正常,但是业务程序、浏览器却报域名无法解析;改完/etc/resolv.conf,重启机器配置直接消失;宿主机解析没问题,Docker 容器内部却解析失败;连上 VPN 之后,内网域名死活解析不出来。
很多新手遇到这类问题,习惯上来就改配置文件、胡乱重启服务,越修越乱。今天结合 Ubuntu、Rocky Linux、RHEL 生产环境常见踩坑点,分享一套可落地的排错思路,按顺序走,快速定位到底是上游 DNS、本地解析器、防火墙、NSS 配置,还是容器、VPN 带来的问题。
适用系统:Ubuntu22.04/24.04、Rocky Linux8/9、RHEL8/9。执行命令尽量使用 sudo,不建议直接登录 root 账号操作。 需要提前安装工具包:RHEL/Rocky 安装
bind‑utils,Ubuntu 安装bind9‑dnsutils,提供 dig、host 等解析工具。
开始排错前,先做好几件准备工作
不要一上来就修改配置文件,先做基础检查,避免做无用功。
- 确认自己服务器发行版版本,不同系统 DNS 管理组件不一样,配置文件路径有区别。
- 确认当前 DNS 由谁管理:现代 Linux 主要两种,
systemd‑resolved或者 NetworkManager,不要直接硬改resolv.conf。 - 准备一个可靠的公共 DNS 做对比测试,国内推荐
223.5.5.5,国外可以用8.8.8.8、1.1.1.1。 - 先确认基础网络是否通:ping IP 地址看通不通,不要把网络中断误判为 DNS 故障。
- 修改配置前务必备份
/etc/resolv.conf,防止改错回退无门。 - 如果主机后缀是
.local,注意这个后缀属于 mDNS/Avahi 服务,不是普通 DNS 解析,不要拿普通 DNS 逻辑去排查它。
很多刚买的 VPS 服务器,不同厂商默认 DNS 配置差异很大,新装机器出现解析异常,优先考虑服务商默认解析器的问题。
第一步:先确认,真的是 DNS 故障吗?
这一步很多人会跳过,白白浪费大量时间。
判断标准很简单:ping 域名报错,但是 ping IP 地址正常,基本就确定是 DNS 解析问题;如果 IP 也 ping 不通,那是底层网络故障,跟 DNS 无关。
#测试域名解析
ping -c 3 baidu.com
#测试基础网络连通性,直接ping公网IP
ping -c 3 223.5.5.5
现在服务器大多是 IPv4/IPv6 双栈,别忽略 IPv6 问题。有可能 A 记录解析正常,AAAA 记录 IPv6 解析坏掉,应用优先走 IPv6 就直接超时。可以用下面命令同时看 IPv4、IPv6 解析结果。
dig AAAA baidu.com +short
getent ahosts baidu.com
如果 ping 域名提示 “名称或服务未知”,但是 ping 公网 IP 没问题,就可以确定问题落在 DNS 解析链路,继续往下排查。
第二步:搞懂谁在管理本机 DNS,别白改 resolv.conf
这是国内运维踩坑最多的一个点:手动编辑/etc/resolv.conf,重启或者 DHCP 更新之后,配置直接被覆盖消失。
Ubuntu 默认使用systemd‑resolved,/etc/resolv.conf只是一个软链接,指向 stub 本地解析服务,直接修改这个软链接文件是无效的,会被系统自动重置。
Rocky、RHEL8‑9 版本,默认是 NetworkManager 接管 DNS,同样不建议直接修改 resolv.conf 文件。
执行命令看文件属性,以及解析服务运行状态:
ls -la /etc/resolv.conf
cat /etc/resolv.conf
resolvectl status
输出如果看到/etc/resolv.conf -> /run/systemd/resolve/stub‑resolv.conf,就代表当前是 systemd‑resolved 接管 DNS。
踩坑提醒:如果你发现 resolv.conf 是普通实体文件,不是软链接,说明有人曾经手动强制修改过。这种配置临时生效,重启就复原,要修改真正的源配置文件,而不是这个文件本身。
另外再提醒一次,.local结尾主机名,要用avahi‑resolve -n xxx.local测试,不要用 dig,dig 查 mDNS 域名是查不出结果的。
第三步:绕过本地解析器,直接向外部 DNS 查询
这是整个排查流程的核心。
用 dig 命令直接指定公共 DNS 服务器查询域名,绕过本机所有本地解析组件。
#直接向阿里DNS查询baidu.com,跳过本机配置
dig @223.5.5.5 baidu.com +short
#走本机系统配置解析
dig baidu.com +short
对比两次结果:
- 如果
@223.5.5.5可以正常返回 IP,但是本机 dig 查询超时无结果 → 问题出在本机解析服务,域名本身没有问题。 - 如果指定公共 DNS 也查不到 → 问题出在域名本身、防火墙拦截 53 端口,或者外网访问受限。
DNS 默认使用 UDP53 端口,很多防火墙只放行 80、443,把 UDP53 出站给拦截了,网页能访问,但是 DNS 解析全部失败,这个坑特别隐蔽。我们可以用 nc 命令确认 53 端口通不通。
nc -zuv 223.5.5.5 53
sudo firewall‑cmd --list‑all
如果端口测试失败,就要检查防火墙策略是否放开 DNS 出站 UDP53 端口。
除了 dig,还有几个高频工具:
host baidu.com:输出简洁,适合写脚本自动化。nslookup baidu.com:老工具,Windows 迁移过来的运维比较熟悉。resolvectl query baidu.com:systemd‑resolved 专属,能看到真正应答查询的那台 DNS 服务器 IP,这点 dig 做不到,多 DNS 服务器环境排错非常好用。dig +trace baidu.com:完整追踪从根域名服务器往下的完整解析链路,怀疑上游域名服务器异常的时候使用。
第四步:检查 /etc/nsswitch.conf,dig 正常,应用却解析失败元凶
非常经典现象:dig 命令解析域名完全正常,curl、浏览器、业务程序却报无法解析主机名。
很多人不知道,dig 是直接访问 DNS 协议,不走系统 glibc 名称解析流程。系统真正给程序提供解析顺序,是由/etc/nsswitch.conf里面 hosts 行控制的。
grep ^hosts /etc/nsswitch.conf
一个正常配置类似: hosts: files resolve [!UNAVAIL=return] dns
关键点:如果使用 systemd‑resolved,hosts 行必须带上resolve;传统 DNS 模式要有dns。 如果这一行缺失对应模块,哪怕 DNS 服务运行完好,普通应用完全不会去查询 DNS。
⚠️不要直接整行覆盖,保留原有 mdns4_minimal、myhostname 这些模块,只补全缺失字段。修改完成后用getent hosts baidu.com验证解析是否生效。
第五步:DNSSEC 校验与缓存损坏排查
故障现象:别的机器解析域名没问题,本机返回 SERVFAIL 解析失败。
有可能是 DNSSEC 签名校验失败,也有可能本地解析缓存损坏。
Ubuntu 默认关闭 DNSSEC 校验,这个问题出现概率低;Rocky、RHEL 或者自建 BIND DNS 服务器,DNSSEC 问题会频繁出现。
dig +cd baidu.com,+cd 参数代表关闭 DNSSEC 校验做测试。 如果加+cd参数解析成功,不加就失败,代表就是 DNSSEC 校验不通过。
注意:
+cd只是诊断手段,生产环境不要永久关闭 DNSSEC,会有域名被劫持的风险,需要去修复上游域名签名问题。
清理 systemd‑resolved 缓存,重启解析服务:
resolvectl flush‑caches
systemctl restart systemd‑resolved
第六步:宿主机正常,Docker 容器内部解析失败
又一个高频坑:宿主机域名解析一切正常,进入 Docker 容器,域名直接解析报错。
Docker 默认网桥网络,容器启动时复制宿主机 resolv.conf;自定义网络模式,Docker 内置 DNS127.0.0.11接管容器解析。注意127.0.0.53这个 systemd‑resolved 本地地址,在容器网络命名空间内部是无效的,容器访问不到宿主机的 127.0.0.53,直接导致解析失败。
快速测试容器解析:
docker run --rm alpine nslookup baidu.com
docker run --rm alpine cat /etc/resolv.conf
看到 nameserver 是 127.0.0.11,就是 Docker 内置 DNS。临时测试可以直接给容器指定 DNS 服务器。
docker run --rm --dns=223.5.5.5 --dns=8.8.8.8 alpine nslookup baidu.com
想要所有容器永久生效,修改 docker 的 daemon.json 配置文件。
{
"dns": ["223.5.5.5","8.8.8.8"]
}
修改保存后重启 docker 服务systemctl restart docker。
第七步:VPN 拆分隧道场景 DNS 异常
现象:没连 VPN,外网域名解析正常;连上 VPN 之后,内网域名解析不出来,或者外网访问变得很慢。
拆分隧道 VPN,一部分流量走隧道网卡 tun0,一部分流量走物理网卡。DNS 查询要区分域名后缀,内网域名发给 VPN 提供的 DNS,普通公网域名继续用原有 DNS 服务器。
resolvectl status
resolvectl domain
看 tun0 虚拟网卡对应的 DNS Domain 字段,带波浪号~corp.internal代表路由域,只有匹配后缀的域名,才会走这个网卡对应的 DNS 服务器。 如果内网域名后缀没有出现在 VPN 网卡 domain 列表,内网解析请求不会送到 VPN DNS,自然解析失败;反之公网域名被强制走 VPN DNS,外网解析卡顿超时。
这不是 DNS 服务器本身坏掉,是 systemd‑resolved 的域名路由规则配置问题。
事后:写个简单脚本监控 DNS,不要等故障发生才救火
DNS 故障总是业务报错之后才被发现。写个简单 shell 脚本,配置 crontab 定时执行,提前发现解析异常,写入日志,对接告警系统。
#!/bin/bash
DOMAINS="baidu.com your‑internal‑app.corp.internal"
for d in $DOMAINS; do
if ! dig +short +time=2 +tries=1 "$d" > /dev/null; then
echo "DNS故障:$d 解析失败 $(date)" >> /var/log/dns‑healthcheck.log
fi
done
配置 crontab 每 5 分钟执行一次,提前捕获 DNS 解析异常,不用等业务用户反馈。
常见 FAQ 整理
Q:dig 解析成功,curl/wget 程序报域名无法解析? A:dig 不走 nsswitch 解析链路,优先排查/etc/nsswitch.conf hosts 配置行,确认 resolve/dns 字段存在。
Q:修改 /etc/resolv.conf 重启服务器配置丢失? A:Ubuntu systemd‑resolved、RHEL Rocky NetworkManager 会重写该软链接文件,修改真正的源配置,而不是修改 resolv.conf 本身。
Q:外网能访问网页,但是 DNS 解析全部超时? A:检查防火墙 UDP53 出站端口,网页 TCP80/443 正常,不代表 UDP53 端口没有被拦截。
Q:宿主机正常,Docker 容器解析失败? A:容器隔离网络空间,无法访问宿主机 127.0.0.53,给容器指定外部 DNS 或者修改 docker daemon.json。
Q:连上 VPN 内网域名解析失效? A:检查resolvectl domain,确认内网域名后缀绑定到 VPN 隧道网卡。
Q:DNSSEC 问题怎么区分? A:dig +cd关闭校验可以解析,不加参数 SERVFAIL 报错,就是 DNSSEC 校验失败。不要直接永久关闭校验,修复域名签名。
整套排错思路,从网络底层、本地解析器、系统名称解析配置,再到 DNSSEC、容器、VPN 特殊场景,一步一步隔离故障点,不需要瞎猜,每一步都有命令做验证,遇到 DNS 故障照着流程走,绝大多数问题都能定位出来。