1. 从一个让人抓狂的故障说起:为什么DNS值得单独拎出来讲
刚入行那会儿,我遇到过一个至今印象深刻的故障。一台服务器上的Web服务跑得好好的,本地用IP访问一切正常,但换成域名就时通时不通,重启服务、检查防火墙、翻看日志,折腾了大半天毫无头绪。最后一位老同事过来,敲了两条命令,问题定位了——域名解析结果指向了一个早已下线的旧IP。整个过程里,服务本身没有任何毛病,坏的是"名字到地址"这条翻译链路。
这件事让我意识到,DNS(Domain Name System,域名系统)虽然平时默默无闻,却是整个网络世界里最不能出问题的基础设施之一。它就像电话簿:你记得住"张三"这个名字,但拨号需要的是那串号码,DNS干的就是把名字翻译成号码的活。一旦这本电话簿出错、过期或者查不到,后面所有的通信都无从谈起。
这篇内容我想聊的不是教科书上那种"DNS是什么"的科普,而是从实际运维和配置的角度,把DNS的工作原理、常见记录类型、服务端配置、客户端排查这几件事讲透。适合谁看?如果你是需要自己搭DNS服务的运维、被域名解析问题折磨过的开发、或者想搞明白"为什么改了域名要等一会儿才生效"的普通用户,这篇都能给你一些能直接上手的东西。我会尽量把每个配置项背后的"为什么"讲清楚,而不是甩一堆参数让你照抄。
2. DNS到底在背后做了什么:一次解析的完整旅程
2.1 递归查询与迭代查询:两种角色的分工
很多人对DNS的理解停留在"输入域名,返回IP"这一层,但真正理解它的工作方式,得先分清两个概念:递归查询和迭代查询。
打个比方。你要找某个陌生人的电话号码,但你不知道。你可以去问一个"万事通"朋友(递归解析器),你说"帮我查一下这个人的号码",他要么直接告诉你,要么自己去一层层问别人,最后把结果给你。你只问了一次,这就是递归查询——你委托对方帮你把结果拿到手。
而"万事通"朋友自己是怎么查的呢?他先问根服务器"这个域名谁管",根服务器说"你去问某个顶级域服务器";他再去问顶级域服务器,对方说"你去问某个权威服务器";最后问到权威服务器,才拿到真正的号码。这一层层问过去、每次只拿到"下一步该问谁"的过程,就是迭代查询——每次只得到线索,需要自己继续追。
理解这个分工很关键。你本机配置的DNS服务器(比如运营商给的、或者公司内网的)通常扮演递归解析器的角色,它负责替你跑完整个迭代过程,然后把最终答案缓存下来。这也是为什么第二次访问同一个域名会快很多——答案被缓存了。
2.2 缓存与TTL:为什么改了记录不能立刻生效
这是被问得最多的问题之一:"我把域名指向改了,为什么过了半天还没生效?"
答案就在TTL(Time To Live,生存时间)这个参数上。每条DNS记录都有一个TTL值,单位是秒,表示这条记录可以被缓存多久。当递归解析器拿到一条记录后,它会在本地缓存TTL指定的时间。在这段时间内,再有用户来查同一个域名,解析器直接返回缓存里的旧结果,根本不会去问权威服务器。
所以当你修改记录时,那些已经缓存了旧记录的解析器,要等缓存过期才会去拿新结果。这就是"生效延迟"的来源。常见的TTL设置从300秒(5分钟)到86400秒(24小时)不等。
提示:如果你计划做一次重要的域名迁移,提前把TTL调小(比如改成300秒),等旧TTL过期后再切换,切换完确认无误再调回较大值。这个"预热"操作能大幅缩短切换期间的混乱窗口。
2.3 解析链路中的每一环都可能出问题
一次完整的解析,涉及本机缓存、hosts文件、本地DNS服务器、递归解析器、根服务器、顶级域服务器、权威服务器等多个环节。任何一环出问题,表现都是"域名打不开",但根因可能天差地别。
我习惯按这个顺序排查:先看本机缓存和hosts文件(最容易忽略),再看本地配置的DNS服务器是否可达,然后看解析结果是否正确,最后才怀疑权威服务器。这个顺序是从近到远、从简单到复杂,能帮你快速排除掉大部分低级问题。
3. 那些必须搞清楚的记录类型:不只是A记录
3.1 A、AAAA与CNAME:最常用的三兄弟
A记录是最基础的,把域名指向一个IPv4地址。AAAA记录(读作"quad-A")对应IPv6地址,格式上就是多了一倍的位数。这两个是"名字到地址"的直接映射。
CNAME记录(Canonical Name,别名记录)则不同,它把一个域名指向另一个域名,而不是IP。比如你把www.example.com设成example.com的CNAME,那么访问前者时,解析器会先找到后者的A记录,再返回IP。
CNAME的价值在于解耦。假设你有一堆子域名都指向同一台服务器,如果直接写A记录,服务器换IP时你得改一堆地方;用CNAME指向一个主域名,只改主域名的A记录就够了。但CNAME有个硬性限制:一个域名不能同时存在CNAME和其他记录(比如MX),因为CNAME会"接管"整个域名的解析,其他记录就没法共存了。这个坑我踩过,当时想给一个已经设了CNAME的域名加邮件记录,怎么都不生效,后来才明白是这个规则在作祟。
3.2 MX、TXT、NS、SOA:各有各的战场
MX记录(Mail Exchange)指定邮件服务器,它带一个优先级数值,数值越小优先级越高。发邮件时,发送方会按优先级依次尝试。配置邮件服务时,MX记录写错或者优先级搞反,是邮件发不出去的常见原因。
TXT记录最初是用来放任意文本的,现在最常见的用途是域名所有权验证和邮件安全策略(比如SPF、DKIM这些反垃圾邮件机制)。很多云服务商让你加一条TXT记录来证明"这个域名是你的",就是利用了这个机制。
NS记录(Name Server)指定这个域名由哪些DNS服务器负责解析,它决定了"权威"在哪里。SOA记录(Start of Authority)则包含了一个区域的核心管理信息,比如主服务器、管理员邮箱、序列号、各种刷新和过期时间。SOA里的序列号特别重要——从服务器靠它判断主服务器的数据有没有更新,序列号没涨,从服务器就不会同步。
3.3 记录类型速查与常见误用
| 记录类型 | 作用 | 典型场景 | 常见坑 |
|---|---|---|---|
| A | 域名到IPv4 | 网站、服务器 | 忘改导致指向旧IP |
| AAAA | 域名到IPv6 | 支持IPv6的服务 | 环境不支持IPv6时反而变慢 |
| CNAME | 域名到域名 | CDN、多子域统一管理 | 与MX等其他记录冲突 |
| MX | 邮件路由 | 企业邮箱 | 优先级数值搞反 |
| TXT | 文本信息 | 验证、邮件策略 | 内容超长需分段 |
| NS | 权威服务器 | 域名托管 | 改NS后需等待传播 |
| SOA | 区域管理信息 | 主从同步 | 序列号忘记递增 |
这张表我建议存下来,配置时对照着看,能避开不少低级错误。
4. 亲手搭一个DNS服务:从选型到跑通
4.1 为什么选这个方案:几种常见DNS服务端对比
自己搭DNS,常见的选择有几种。BIND是元老级的,功能全、资料多,但配置文件语法有点反人类;dnsmasq轻量,适合做本地缓存和简单转发,配置极简;CoreDNS是较新的选择,插件化设计,配置用类似配置文件的语法,很适合容器化环境;PowerDNS则在数据库后端支持上做得很好,适合大规模场景。
如果你的需求只是"给内网做缓存加速+少量自定义解析",dnsmasq基本够用,几十行配置就能跑起来。如果要做权威解析、管理大量区域,BIND或PowerDNS更合适。我下面以BIND为例讲配置,因为它的概念最完整,学会了再迁移到其他工具会轻松很多。
4.2 主配置文件与区域文件:两个核心概念
BIND的配置分两层:主配置文件(通常是named.conf)定义"我管理哪些区域、去哪里找区域文件";区域文件(zone file)则具体描述"这个区域里有哪些记录"。
主配置文件里,一个典型的区域定义长这样:
zone "example.com" IN { type master; file "/etc/bind/db.example.com"; allow-transfer { 192.0.2.2; }; };这里type master表示这是主服务器,file指向区域文件路径,allow-transfer限制哪些从服务器可以来同步数据——这个限制很重要,不设的话任何人都能拉走你整个区域的记录,等于把内网结构暴露了。
区域文件则是记录的实际存放地,一个简化版本:
$TTL 3600 @ IN SOA ns1.example.com. admin.example.com. ( 2024010101 ; 序列号 3600 ; 刷新 1800 ; 重试 604800 ; 过期 86400 ) ; 最小TTL @ IN NS ns1.example.com. @ IN A 192.0.2.10 www IN CNAME @ mail IN A 192.0.2.20 @ IN MX 10 mail.example.com.几个细节值得说。第一行的$TTL是默认TTL,没单独指定的记录都用它。SOA记录里那个2024010101是序列号,每次改完区域文件必须递增它,否则从服务器不会同步——这是新手最容易忘的一步。@符号代表当前区域名本身,省得你到处写全称。
4.3 配置完必须做的验证动作
配置写完不代表能用。我习惯按这几步验证:
- 语法检查:
named-checkconf检查主配置,named-checkzone example.com /etc/bind/db.example.com检查区域文件。这两条命令能在重启服务前就发现大部分语法错误,省得服务起不来还找不到原因。 - 重启或重载服务:改完配置用
rndc reload重载,比直接重启更平滑。 - 本地查询验证:用
dig @127.0.0.1 www.example.com直接问本机DNS,看返回是否符合预期。 - 序列号确认:
dig @127.0.0.1 example.com SOA看序列号是不是你刚改的值。
注意:区域文件里每条记录的域名结尾那个点(
.)千万别漏。ns1.example.com和ns1.example.com.在DNS里是两个完全不同的东西——前者会被自动补上当前区域名,变成ns1.example.com.example.com,这个错误极其隐蔽,我第一次配的时候就被它坑了半天。
5. 客户端这边:解析配置与排查手法
5.1 resolv.conf与解析顺序
客户端这边,Linux系统主要看/etc/resolv.conf,里面用nameserver指定DNS服务器地址,可以写多个,按顺序尝试。还有个search选项,指定搜索域,让你输入短名字时自动补全后缀。
但要注意,很多系统上这个文件是被网络管理工具动态生成的,你手动改了,重启网络或者重连之后可能被覆盖。要持久化配置,得改对应的网络管理配置,而不是直接编辑这个文件。这个坑我见过太多次了——有人改完当时好用,第二天重启就"失效"了,其实是被覆盖回去了。
解析顺序上,系统一般先查本机缓存,再查/etc/hosts,最后才去问DNS服务器。/etc/hosts的优先级很高,有时候域名解析异常,就是因为这里有一条陈旧的静态映射在捣乱。
5.2 dig、nslookup、host:三把排查利器
排查DNS问题,dig是最强大的工具。dig example.com会返回完整的应答,包括状态码、标志位、答案段、权威段等。重点看几个地方:status是不是NOERROR,ANSWER SECTION里有没有你要的记录,flags里有没有aa(权威应答)。
nslookup更老派但交互式用起来方便,host则最简洁,适合快速看一眼。我个人的习惯是:快速确认用host,深入分析用dig,交互式反复查用nslookup。
一个实用技巧是指定DNS服务器查询:dig @8.8.8.8 example.com直接问某个公共DNS,用来对比"是本地DNS的问题还是权威服务器的问题"。如果问公共DNS正常、问本地DNS异常,那问题就在本地DNS这一环。
5.3 缓存清理:不同系统的不同做法
改了记录想立刻验证,得先清缓存。但DNS缓存是分层的,你清了本机的,递归解析器那边可能还缓存着。
本机层面,Linux上如果用了systemd-resolved,用resolvectl flush-caches;用了nscd的,重启nscd服务。Windows上用ipconfig /flushdns。macOS上稍复杂,较新版本用sudo dscacheutil -flushcache配合sudo killall -HUP mDNSResponder。
但递归解析器的缓存你控制不了,只能等TTL过期。所以验证时,最可靠的办法是直接问权威服务器:dig @ns1.example.com example.com,绕过所有缓存,看权威数据到底是什么。如果权威数据是对的,那问题就只是缓存还没过期,等着就行。
6. 踩过的坑与实战心得
6.1 序列号忘记递增:从服务器死活不同步
前面提过序列号,这里展开说一个真实场景。有次我在主服务器上加了条记录,本地查询一切正常,但从服务器上怎么查都查不到新记录。检查网络、检查allow-transfer、检查防火墙,全都没问题。最后盯着区域文件看了半天,才发现序列号还是老值。
从服务器的同步逻辑是:定期去问主服务器"你的SOA序列号是多少",如果比自己手里的大,才发起同步。序列号没变,从服务器就认为"数据没更新",压根不会去拉。所以每次修改区域文件,递增序列号是铁律。我现在养成的习惯是,改完文件第一件事就是改序列号,用日期加序号的方式(比如2024010101改成2024010102),既直观又不会重复。
6.2 正向解析通了,反向解析没配
正向解析是"域名到IP",反向解析是"IP到域名",用的是PTR记录,而且它挂在特殊的in-addr.arpa区域下。很多人只配了正向,忘了反向。
反向解析什么时候重要?邮件服务器做反垃圾检查时,经常会查发信IP的反向记录,没有或者不匹配的,邮件容易被判为垃圾。还有一些日志分析、访问统计工具,也依赖反向解析把IP翻译成可读的域名。所以如果你的服务涉及邮件或者需要对外提供可识别的身份,反向记录别落下。
配置反向区域时,IP地址是倒着写的。比如192.0.2.10对应的反向区域名是10.2.0.192.in-addr.arpa。这个倒序规则第一次见会觉得别扭,记住就好。
6.3 TTL设太长的代价
TTL设长有好处——减少查询次数、降低权威服务器压力、加快客户端响应。但代价是变更不灵活。我曾经把一个不常变的域名TTL设成了24小时,结果后来需要紧急切换IP,硬生生等了一整天才全网生效,那24小时里一部分用户访问的是旧地址,体验很差。
现在的做法是:稳定期用较大TTL(比如几小时到一天),计划变更前提前调小。这个"提前"要留足时间,至少是旧TTL的一倍以上,确保所有缓存都过期了再动手。变更完成后观察一段时间,确认稳定了再调回去。
6.4 内网DNS与外网DNS的边界
很多公司内网有自己的DNS,用来解析内部系统。这里有个常见误区:把内网DNS配成"只解析内网、其他一律不响应",结果员工访问外网时解析不了。正确做法是配置转发——内网域名自己解析,其他域名转发给上游的公共DNS。
BIND里用forwarders配置转发目标,dnsmasq里用server指令。这样既保证了内网解析的私密性和可控性,又不影响正常上网。转发配置里还可以设forward first或forward only,前者是"先问转发目标,不行再自己迭代",后者是"只问转发目标"。内网环境一般用forward only,把外部解析完全交给上游,管理更简单。
7. 把DNS当成一门需要持续打理的活
DNS这东西,配好之后可以很久不出问题,但一旦出问题,往往表现得很"玄学"——时好时坏、部分用户受影响、换个网络就正常。这些表象背后,几乎都能归结到缓存、TTL、记录配置或者解析链路这几个点上。
我的经验是,与其等出问题再救火,不如平时就把几件事做扎实:区域文件改完必查语法、必递增序列号;重要变更前先调小TTL;正向反向都配齐;内网外网用转发划清边界。这些动作花不了多少时间,但能挡掉绝大多数故障。
最后分享一个我常用的排查口诀:先本机、再本地、后权威,先缓存、再记录、后链路。按这个顺序走,大部分DNS问题都能在几分钟内定位到根因,而不是像我最开始那样,对着一个"域名打不开"干着急。DNS不难,难的是它太基础,基础到大家平时都不愿意花时间真正搞懂它——而恰恰是这种"不愿意",让它在出问题时显得格外棘手。