☰
Linux域名解析全链路解析:从故障排查到自建DNS服务器实战
2026/10/24 1:05:04 网站建设 项目流程

1. 从一个"ping 得通 IP 却打不开网页"的经典故障说起

如果你在 Linux 上折腾过服务器或者桌面环境,大概率遇到过这种让人抓狂的场景:ping 8.8.8.8完全正常,延迟稳定,但ping www.example.com直接报unknown host,浏览器也打不开任何网站。这时候有经验的人第一反应就是——DNS 出问题了。

域名解析服务器(DNS Server)在 Linux 系统里扮演的角色,说白了就是一本"电话簿"。人记不住142.250.72.14这种数字串,但能记住www.example.com,DNS 的工作就是把这串人类友好的名字翻译成机器能识别的 IP 地址。听起来简单,但真正落到 Linux 系统上,从/etc/hosts到/etc/resolv.conf,从systemd-resolved到nscd,从dig到resolvectl,这一整套机制层层叠叠,稍有不慎就会踩坑。

这篇内容面向的是需要在 Linux 环境下配置、调试、自建域名解析服务的运维人员、后端开发者和网络爱好者。我会把 Linux 域名解析的完整链路拆开讲清楚:解析请求到底走了哪条路、配置文件之间谁覆盖谁、怎么自建一个可用的解析服务器、以及那些文档里不会写但实际会遇到的坑。不管你是刚接触 Linux 网络的新手,还是想系统梳理一遍解析链路的老手,都能从里面找到能直接用的东西。

2. Linux 域名解析的完整链路:请求到底走了哪条路

很多人以为 DNS 解析就是"系统发个请求给 DNS 服务器,拿回 IP",但 Linux 上的实际情况要复杂得多。一个域名解析请求从应用程序发起,到最终拿到 IP,中间可能经过五六个环节,任何一个环节配置不对,结果就是解析失败或者解析到了错误的结果。

2.1 应用程序到系统解析器的第一跳

当你在终端敲下curl www.example.com时,curl 并不会自己去查 DNS,它调用的是系统提供的解析接口,通常是getaddrinfo()这个函数。这个函数属于 glibc(GNU C 库),是绝大多数 Linux 程序做域名解析的入口。

getaddrinfo()拿到域名后,会按照一个固定的顺序去查找:

  1. 先查/etc/hosts文件。这是本地静态映射表,优先级最高。如果你在里面写了127.0.0.1 www.example.com,那么不管外部 DNS 怎么配,这个域名永远解析到本地。
  2. 再查/etc/nsswitch.conf里hosts:这一行的配置。这一行决定了查找顺序和使用的服务,典型配置是hosts: files dns,意思是先查文件(files 对应/etc/hosts),再查 DNS。
  3. 最后才走 DNS 查询,根据/etc/resolv.conf里配置的 nameserver 地址去请求。

这个顺序非常关键。我见过不少人调试了半天 DNS 服务器,结果发现是/etc/hosts里有一条陈旧的记录在作怪。所以排查解析问题的第一步,永远是cat /etc/hosts和cat /etc/nsswitch.conf。

2.2 /etc/resolv.conf 的真实面目

/etc/resolv.conf是 Linux 上最"名不副实"的文件之一。很多人以为它是手动配置的静态文件,但在现代 Linux 发行版上,它经常是被其他程序动态生成的。

这个文件里最核心的几行:

nameserver 8.8.8.8 nameserver 114.114.114.114 search example.local options timeout:2 attempts:3 rotate
  • nameserver:指定 DNS 服务器地址,最多可以写三个(glibc 的限制),按顺序尝试。
  • search:搜索域,当你查询一个不带点的短名字(比如webserver)时,系统会自动拼接成webserver.example.local去查。
  • options:控制解析行为,timeout是单次查询超时秒数,attempts是重试次数,rotate表示轮询使用多个 nameserver。

问题在于,这个文件可能被NetworkManager、systemd-resolved、dhclient、netplan等多个程序争抢着改写。你手动改完,重启网络服务后又被覆盖了,这是新手最常踩的坑。

2.3 systemd-resolved 带来的新格局

从 Ubuntu 18.04、CentOS 8 开始,systemd-resolved成了默认的解析管理服务。它会在127.0.0.53:53上启动一个本地 DNS 缓存和转发器,然后/etc/resolv.conf通常被改成指向127.0.0.53的软链接。

这时候你cat /etc/resolv.conf看到的是:

nameserver 127.0.0.53 options edns0 trust-ad

真正的上游 DNS 配置藏在/run/systemd/resolve/resolv.conf或者通过resolvectl status查看。这种设计的好处是支持 per-interface 的 DNS 配置、内置缓存、DNSSEC 验证等,坏处是排查问题时多了一层,很多人搞不清楚请求到底发给了谁。

判断当前系统用的是哪种模式,可以看两个地方:

检查项传统模式systemd-resolved 模式
/etc/resolv.conf类型普通文件软链接到/run/systemd/resolve/stub-resolv.conf
nameserver 地址真实 DNS 服务器 IP127.0.0.53
查看上游 DNS直接看 resolv.confresolvectl status
缓存无(除非装 nscd)内置缓存

2.4 解析请求的完整路径还原

把上面这些串起来,一个完整的解析路径是这样的:

应用程序调用getaddrinfo()→ glibc 读/etc/nsswitch.conf确定顺序 → 查/etc/hosts→ 未命中则读/etc/resolv.conf拿到 nameserver → 如果 nameserver 是127.0.0.53,请求发给 systemd-resolved → systemd-resolved 查自己的缓存 → 未命中则转发给上游 DNS → 上游返回结果 → 逐层返回给应用程序。

理解这条链路的意义在于:当解析出问题时,你可以沿着这条路径逐段排查,而不是盲目地重启网络或者乱改配置。我个人的习惯是先用dig直接指定 DNS 服务器测试,确认是上游问题还是本地问题,再决定往哪个方向查。

3. 自建域名解析服务器:选型、安装与最小可用配置

搞清楚了客户端链路,接下来聊服务端。自建 DNS 服务器的需求场景很多:内网域名解析、本地开发环境、广告过滤、缓存加速、学习研究等。选哪个软件、怎么配、配完怎么验证,这里面有不少门道。

3.1 主流 DNS 服务软件怎么选

Linux 上能当 DNS 服务器用的软件不少,但常用的就那么几个,各有各的定位:

  • BIND:老牌选手,功能最全,支持权威解析、递归解析、DNSSEC、视图(view)等,配置相对复杂。适合需要完整权威 DNS 功能的场景。
  • dnsmasq:轻量级,配置极简,同时提供 DNS 和 DHCP 功能,自带缓存。适合内网小规模环境、本地开发、路由器等场景。
  • Unbound:专注递归解析和缓存,安全性好,支持 DNSSEC 验证,配置比 BIND 简单。适合做本地递归解析器。
  • CoreDNS:Go 写的,插件化架构,配置用 Corefile,在容器和云原生环境里很流行。适合 K8s 集群内 DNS。

选型建议很直接:如果你只是想让内网几台机器能互相用域名访问,或者本地开发想自定义域名,dnsmasq 是最省事的选择;如果你要搭建一个对外提供解析的权威服务器,BIND 或 CoreDNS 更合适;如果你只是想要一个带缓存的本地递归解析器,Unbound 最省心。

下面我以 dnsmasq 为例,走一遍完整的搭建流程,因为它的配置最直观,也最容易验证效果。

3.2 dnsmasq 的安装与核心配置逐行拆解

安装很简单,主流发行版都有包:

# Debian/Ubuntu 系 sudo apt update sudo apt install dnsmasq # RHEL/CentOS 系 sudo yum install dnsmasq

安装完先别急着启动,因为很多系统上 53 端口可能被systemd-resolved占着。检查一下:

sudo ss -tulnp | grep :53

如果看到systemd-resolve占用了 53 端口,需要先处理冲突。最简单的办法是禁用 systemd-resolved 的 stub listener,编辑/etc/systemd/resolved.conf,把DNSStubListener=yes改成DNSStubListener=no,然后重启服务。

接下来是 dnsmasq 的主配置文件/etc/dnsmasq.conf,我挑几个最关键的参数讲:

# 监听地址,只监听本地和内网网卡 listen-address=127.0.0.1,192.168.1.10 # 不读取 /etc/resolv.conf 作为上游,避免循环 no-resolv # 手动指定上游 DNS server=223.5.5.5 server=119.29.29.29 # 缓存大小,150 条足够小规模使用 cache-size=150 # 自定义域名解析,把内网域名指向指定 IP address=/dev.local/192.168.1.100 address=/test.internal/192.168.1.101 # 记录查询日志,调试时打开 log-queries log-facility=/var/log/dnsmasq.log

这里有几个点值得展开说。no-resolv这个参数很多人会忽略,如果不加,dnsmasq 会去读/etc/resolv.conf拿上游服务器,而如果/etc/resolv.conf又指向了 dnsmasq 自己(127.0.0.1),就会形成解析循环,所有查询都超时。这是自建 DNS 最经典的坑之一。

address=/dev.local/192.168.1.100这种写法是 dnsmasq 的杀手锏,它会把dev.local及其所有子域名(比如api.dev.local、db.dev.local)全部解析到192.168.1.100。做本地开发时特别方便,不用每加一个子域名就改一次配置。

3.3 启动、验证与常见启动失败排查

配置写好后启动服务:

sudo systemctl restart dnsmasq sudo systemctl status dnsmasq

验证解析是否生效,用dig指定服务器地址测试:

dig @127.0.0.1 api.dev.local

如果返回的 ANSWER SECTION 里显示192.168.1.100,说明配置生效了。再测一个外部域名:

dig @127.0.0.1 www.example.com

能正常返回结果,说明上游转发也通了。

启动失败最常见的原因有三个:一是 53 端口被占用,ss -tulnp | grep :53一看便知;二是配置文件语法错误,用dnsmasq --test可以检查配置;三是权限问题,dnsmasq 默认以dnsmasq用户运行,如果日志目录或缓存目录权限不对会启动失败,看journalctl -u dnsmasq的报错信息基本能定位。

3.4 让客户端用上自建 DNS 的几种方式

服务器搭好了,怎么让其他机器用上?有几种做法:

  • 手动改客户端/etc/resolv.conf:最直接,但重启网络后可能被覆盖。
  • 在 DHCP 服务器里下发 DNS 地址:如果内网有 DHCP,把下发的 DNS 改成自建服务器地址,所有通过 DHCP 获取 IP 的机器自动生效。
  • 在 NetworkManager 里配置:桌面环境常用,通过nmcli或图形界面设置。
  • 在路由器上配置:把路由器的 DNS 转发指向自建服务器,全内网生效。

我个人推荐在 DHCP 层面统一下发,这样管理最集中,也不会因为某台机器手动改配置而出现解析不一致的问题。

4. 解析排错实战:从现象到根因的完整排查链路

DNS 问题的难点不在于配置,而在于排查。因为解析失败的表现形式很多,可能是超时、可能是返回错误 IP、可能是时好时坏,每种现象背后的原因都不一样。这一节我把常见的几类问题拆开,给出完整的排查思路。

4.1 工具选择:dig、nslookup、host 该用哪个

Linux 上排查 DNS 的工具主要有三个:

  • dig:功能最强,输出信息最全,支持指定服务器、查询类型、追踪解析路径。排查首选。
  • nslookup:交互式,简单查询够用,但输出信息少,逐渐被淘汰。
  • host:最简洁,适合快速查一条记录。

我基本只用 dig,因为它的输出包含了查询时间、返回状态码、flags 等关键信息。几个常用姿势:

# 查 A 记录 dig www.example.com # 指定 DNS 服务器 dig @8.8.8.8 www.example.com # 查特定记录类型 dig MX example.com dig TXT example.com # 追踪完整解析路径 dig +trace www.example.com # 只看简短结果 dig +short www.example.com

dig +trace特别有用,它会从根域名服务器开始,一步步追踪到最终的权威服务器,能清楚看到解析在哪一环断掉了。

4.2 超时与 SERVFAIL:上游不可达还是配置错误

dig返回connection timed out; no servers could be reached,说明请求根本没发出去或者没收到响应。排查顺序:

  1. 确认 DNS 服务器地址可达:ping 8.8.8.8看网络通不通。
  2. 确认 53 端口没被防火墙拦:sudo iptables -L -n | grep 53或者sudo firewall-cmd --list-all。
  3. 确认本地 DNS 服务在监听:ss -tulnp | grep :53。
  4. 确认没有解析循环:检查/etc/resolv.conf和 dnsmasq 配置里有没有互相指向。

返回SERVFAIL则是服务器收到了请求但处理失败,常见原因是上游 DNS 返回了错误、DNSSEC 验证失败、或者域名本身配置有问题。这时候用dig @上游DNS 域名直接测上游,能快速判断是本地问题还是上游问题。

4.3 解析结果不对:缓存、hosts 与 search 域的干扰

有时候解析不报错,但拿到的 IP 是错的。这种情况排查起来更绕,因为"错"的定义不明确。常见原因:

  • 本地缓存:systemd-resolved 或 nscd 缓存了旧记录。用resolvectl flush-caches或systemctl restart nscd清缓存。
  • /etc/hosts 干扰:前面提过,优先级最高,一定要检查。
  • search 域拼接:查询短名字时被自动拼接了 search 域,导致查到了意料之外的域名。用dig查完整域名(末尾带点,如www.example.com.)可以避免拼接。
  • 多个 nameserver 轮询:options rotate会导致请求在不同服务器间轮询,如果其中一台返回了不同结果,就会出现时好时坏。去掉 rotate 或者统一各服务器配置。

我遇到过一个典型案例:某台机器解析内网域名时好时坏,最后发现是/etc/resolv.conf里配了两个 nameserver,一个是正确的内网 DNS,另一个是运营商 DNS,rotate 模式下请求轮询到运营商 DNS 时,内网域名自然解析不了。去掉 rotate 并把内网 DNS 放第一位就解决了。

4.4 用 tcpdump 抓包定位解析请求的真实去向

当所有配置看起来都对,但解析就是不对时,抓包是终极手段。在 DNS 服务器上执行:

sudo tcpdump -i any -n port 53

然后在客户端发起一次解析,观察抓包结果:

  • 如果看不到任何包,说明请求根本没到这台服务器,问题在客户端配置或网络路径。
  • 如果看到请求但没看到响应,说明服务器收到了但没回,可能是服务没正常运行或处理出错。
  • 如果看到请求和响应,但客户端说解析失败,说明响应内容有问题,可能是格式错误或被中间设备篡改。

抓包能直接告诉你"请求到底去了哪",避免在配置层面瞎猜。这个技能在排查跨网段、跨防火墙的 DNS 问题时尤其管用。

5. 缓存、性能与安全:让解析服务真正好用

一个能用的 DNS 服务器和一个好用的 DNS 服务器之间,差的是缓存策略、性能调优和安全防护。这部分内容很多教程不会讲,但实际生产环境里非常关键。

5.1 缓存命中率决定了你的解析有多快

DNS 查询是有网络延迟的,一次递归查询可能要几百毫秒。缓存的作用就是把重复查询的结果存在本地,下次直接返回,延迟降到微秒级。

dnsmasq 的cache-size参数控制缓存条目数,默认 150,对于几十台机器的内网够用,但如果是几百台机器的环境,建议调到 1000 以上。查看缓存命中情况:

# 向 dnsmasq 发送 SIGUSR1 信号,会把统计信息写入日志 sudo kill -USR1 $(pidof dnsmasq) sudo tail -f /var/log/dnsmasq.log

日志里会显示cache size、cache insertions、cache evictions、cache hits、cache misses等统计。如果 evictions 很高,说明缓存太小,需要调大;如果 hits 很低,说明查询的域名太分散,缓存帮助有限。

TTL(Time To Live)也影响缓存效果。每条 DNS 记录都有自己的 TTL,dnsmasq 会遵守这个值。如果某个域名的 TTL 只有 60 秒,那缓存 60 秒后就失效了,需要重新查询。对于内网自定义域名,可以通过配置强制指定 TTL,减少重复查询。

5.2 上游服务器的选择与容灾

上游 DNS 的选择直接影响解析速度和可靠性。几个原则:

  • 就近选择:物理距离近的 DNS 服务器延迟低。国内常用的有 223.5.5.5、119.29.29.29 等。
  • 多配几个:至少配两个上游,一个挂了另一个顶上。
  • 区分场景:内网域名走内网 DNS,外网域名走公共 DNS,可以通过 dnsmasq 的server=/domain/ip语法实现分流。
# 所有 example.com 的查询走指定服务器 server=/example.com/192.168.1.1 # 其他查询走默认上游 server=223.5.5.5 server=119.29.29.29

这种分流配置在企业内网很常见,能避免内网域名泄露到公共 DNS,也能保证外网解析走最优路径。

5.3 防止 DNS 被滥用:访问控制与限速

自建 DNS 服务器如果暴露在公网上,很容易被利用来做 DNS 放大攻击。基本的防护措施:

  • 限制监听地址:只监听内网网卡,不要监听0.0.0.0。
  • 配置访问控制:dnsmasq 用interface和except-interface控制,BIND 用 ACL。
  • 开启查询日志:便于发现异常查询模式。
  • 限制递归查询:只对信任的网段开放递归,其他网段只能查权威记录。

dnsmasq 的一个简单访问控制配置:

# 只允许内网网段查询 interface=eth0 except-interface=eth1 bind-interfaces

bind-interfaces让 dnsmasq 真正绑定到指定接口,而不是监听所有接口再过滤,安全性更好。

5.4 日志与监控:出问题时能快速定位

DNS 服务的日志平时看着烦,出问题时是救命稻草。dnsmasq 开启log-queries后,每次查询都会记录:

query[A] www.example.com from 192.168.1.50 forwarded www.example.com to 223.5.5.5 reply www.example.com is 93.184.216.34

从日志里能看出谁在查、查了什么、转发给了谁、返回了什么。排查"某台机器解析异常"这类问题时,直接 grep 那台机器的 IP 就能看到它的所有查询记录。

对于规模稍大的环境,建议把 DNS 日志接入集中日志系统,配合告警规则,比如"某域名查询量突增"、"SERVFAIL 比例超过阈值"等,能提前发现异常。

6. 那些文档里不写、但实际会踩的坑

配置 DNS 这件事,理论看一遍就懂,但实际操作中总有一些让人摸不着头脑的问题。这一节我把自己和身边同行踩过的坑整理出来,都是真实遇到过的场景。

6.1 resolv.conf 被覆盖的三种典型场景

手动改完/etc/resolv.conf,重启后失效,这是最高频的问题。覆盖它的"元凶"主要有三个:

  • NetworkManager:桌面版和部分服务器版默认启用,会根据自己的连接配置重写 resolv.conf。解决办法是在连接配置里设置ipv4.ignore-auto-dns yes并手动指定 DNS,或者直接改 NetworkManager 的 dns 配置模式。
  • dhclient:DHCP 客户端拿到 IP 后会根据 DHCP 下发的 DNS 重写 resolv.conf。可以在/etc/dhcp/dhclient.conf里加supersede domain-name-servers 127.0.0.1;来覆盖。
  • systemd-resolved:前面讲过,它会把 resolv.conf 变成软链接。要手动控制的话,得先禁用它或者改它的配置。

判断是谁在覆盖,可以看/etc/resolv.conf的文件类型和修改时间,结合systemctl status看相关服务状态。最彻底的办法是明确指定一个管理方,其他都禁用,避免多个程序抢着改。

6.2 容器环境里的 DNS 特殊性

在 Docker 和 Kubernetes 环境里,DNS 的行为和宿主机不一样,这是很多人容易忽略的。

Docker 默认会给容器配置一个内置的 DNS 解析器(通常是 127.0.0.11),它负责解析容器名和转发外部查询。如果你在容器里手动改了/etc/resolv.conf,重启容器后会被 Docker 覆盖。要自定义,得在docker run时用--dns参数,或者在daemon.json里配置全局 DNS。

Kubernetes 里每个 Pod 的/etc/resolv.conf由 kubelet 根据 Pod 的 dnsPolicy 生成,默认是ClusterFirst,先查集群内的 CoreDNS,查不到再转发给上游。如果集群内 DNS 出问题,Pod 之间的服务发现就会失败,表现是服务名解析不了。排查时先看 CoreDNS 的 Pod 是否正常,再看 CoreDNS 的 ConfigMap 配置。

6.3 IPv6 与双栈环境下的解析顺序

在同时有 IPv4 和 IPv6 的环境里,解析行为会更复杂。getaddrinfo()默认会同时查询 A 记录(IPv4)和 AAAA 记录(IPv6),然后根据系统的地址选择策略决定用哪个。

如果 IPv6 网络不通但系统优先返回了 IPv6 地址,就会出现"解析成功但连接失败"的现象。这种情况可以通过修改/etc/gai.conf调整地址选择优先级,或者在应用层面强制使用 IPv4。

排查这类问题时,dig A 域名和dig AAAA 域名分别查一下,看看两种记录是否都正常返回,再结合ping6测试 IPv6 连通性,基本能定位问题。

6.4 域名末尾那个点:容易被忽略的细节

DNS 里域名末尾的点表示"绝对域名"(FQDN),不带点则表示相对域名,会拼接 search 域。这个细节在配置和排查时经常被忽略。

比如/etc/resolv.conf里有search corp.local,你查询db,系统实际查的是db.corp.local。如果你本意是查一个叫db的绝对域名,就得写成db.(末尾带点)。

在 dnsmasq 配置里,address=/dev.local/192.168.1.100这种写法匹配的是dev.local及其子域,但不会匹配dev.local.这种带末尾点的形式(实际上 dnsmasq 会做规范化处理,但理解这个概念对排查问题有帮助)。写配置时养成明确使用 FQDN 的习惯,能避免很多歧义。

7. 我个人的几条实操心得

折腾 DNS 这些年,有几个习惯是踩坑踩出来的,分享出来供参考。

第一,改任何 DNS 配置前先备份。cp /etc/resolv.conf /etc/resolv.conf.bak这种操作花不了几秒钟,但出问题时能救命。尤其是远程服务器上,改错 DNS 配置可能导致 SSH 都连不上(如果 SSH 登录用了域名),有备份至少能通过其他方式恢复。

第二,用 dig 而不是 ping 来验证解析。ping 只能告诉你通不通,dig 能告诉你解析到了什么、走了哪条路径、耗时多少。排查问题时信息量完全不是一个级别。

第三,自建 DNS 一定要配好上游容灾。我见过只配一个上游 DNS 的服务器,上游一挂,整个内网解析全瘫。至少配两个,最好来自不同的服务商,避免同时故障。

第四,日志该开就开,但别一直开着。log-queries在排查时非常有用,但长期开启会产生大量日志,占满磁盘。建议平时关着,需要排查时临时打开,查完再关。

第五,理解优先级顺序比记住配置命令更重要。/etc/hosts>nsswitch.conf指定的顺序 >/etc/resolv.conf的 nameserver,这个顺序是排查一切解析问题的基石。记住它,遇到问题先按这个顺序过一遍,大部分问题都能快速定位。

DNS 这东西,配置起来不难,难的是理解它背后的整套机制和排查思路。把链路搞清楚,把工具用熟练,剩下的就是经验积累了。希望这篇内容能帮你在下次遇到解析问题时,少走一点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询