Linux BIND DNS 服务器搭建:正向反向解析与主从排错

发布时间:2026/10/27 13:22:26
Linux BIND DNS 服务器搭建:正向反向解析与主从排错 1. 先想清楚为什么自己架一台 DNS三类真实场景与选型前提在 Linux 上搭建 DNS 域名解析服务器这件事的门槛比很多人想象的低但踩坑率比想象的高得多。原因不是 BIND 难装而是大多数教程只教你复制粘贴一份named.conf和两个 zone 文件却不告诉你为什么要这么写、写错了会是什么现象、哪几个字段一旦错了排查起来能耗掉一整天。我自己第一次搭的时候区域文件里少写了一个结尾的点结果客户端查www.corp.test返回 NXDOMAIN日志里干干净净最后是盯着配置文件逐字符比对才发现的。先说清楚这篇文章的定位它面向的是需要在 Linux 上部署一台真正能对外提供服务的 DNS 服务器的人不管是给内网几百台机器做域名解析、给一台对外提供业务的主机做权威解析还是做一层本地缓存加速路径都是相通的。零基础能跟着走下来有经验的可以重点看第 3、4 章的字段解释和第 7 章的排错链路那里才有真正的信息量。1.1 内网域名、解析加速与权威托管需求完全不同很多人把搭 DNS当成一件事实际上至少是三种不同的需求配置方向完全相反。第一种是内网权威解析。典型场景是公司或者实验室内部有一批服务机器 IP 是192.168.10.x这种私有段希望用git.corp.test、db.corp.test这样的名字去访问而不是记 IP。这种需求下解析服务器只对自己管理的区域负责不需要去互联网上查任何东西递归应该关掉。第二种是本地缓存加速。内网出口带宽紧张或者上游解析服务偶尔抖动需要在本机或者网关位置放一台缓存服务器把常用域名的解析结果暂存下来。这种场景下服务器不管理任何区域只做转发和缓存recursion必须打开但只能对内网开放。第三种是对外权威托管。自己有域名需要在国内某台机器上跑一个二级或者三级权威服务器。这种场景对可用性要求最高通常要主从两台而且必须正确处理 SOA 的各个时间字段。三种需求混在一起配就会出现既想让它查外网又不想让它当开放解析器的纠结最后配出一个既慢又不安全的四不像。我的建议是一台机器只承担一种角色如果确实要合并用第 6 章讲的 view 做逻辑隔离。1.2 部署位置与机器数量单机缓存还是主从双机机器放哪里直接决定了后面的配置复杂度。内部权威解析器最合适的位置是内网核心交换机旁边的一台固定 IP 机器所有客户端的resolv.conf或者 DHCP 下发的 DNS 选项都指向它。这里有个容易被忽略的细节服务器本身的resolv.conf千万不要指向自己否则它需要向外查询的时候会绕回自己形成环路表现出来就是所有外部域名解析超时。正确做法是让服务器的上游指向另一个可用的解析地址或者干脆在named.conf里把转发器写死不依赖系统resolv.conf。数量上如果只是内网几十台机器一台足够一旦有对外业务依赖就必须两台起步做主从。主从的意义不只是宕机切换更重要的是分担查询压力和给变更留出回滚窗口。我曾经遇到过一台单点服务器因为区域文件写错导致整个内网域名全挂回滚只能靠手改如果有从库主库挂了从库还能继续应答一段时间那几分钟就是救命的时间。1.3 软件选型BIND、dnsmasq、PowerDNS 的取舍方案适合场景优势局限BIND权威解析、主从、视图分离功能最全、文档最多、几乎覆盖所有 DNS 特性配置语法严格一个分号错就起不来dnsmasq小型内网、配合 DHCP配置极简几十行搞定支持 hosts 风格不支持完整的区域传输和复杂视图PowerDNS大规模、需数据库后端记录存数据库变更方便API 友好引入数据库依赖运维面更大我自己的习惯是只要涉及主从、视图、DNSSEC 或者要给别人做权威解析就选 BIND如果只是给十几台机器做个名字映射顺带缓存dnsmasq 更省心。这篇文章以 BIND 为主线因为它的概念模型最完整学会了之后换别的工具只是换语法。还有一点常被忽略选 BIND 就要接受它的严格性。它的配置文件语法错误是致命的服务直接起不来但区域文件的错误往往是能起来但解析不对这种半失败状态才是最麻烦的所以第 2 章和第 3 章我把两类校验分开讲。2. BIND 落地的第一层地基安装、目录结构与主配置骨架装软件这一步本身没什么技术含量但装完之后文件在哪、默认配置被改成了什么样是后面所有问题的源头。不同发行版把 BIND 拆成了不同的包默认路径、运行用户、chroot 策略、甚至 SELinux 策略都不一样。我见过有人照着某篇教程在 Debian 系机器上找/etc/named.conf找了半小时以为装失败了其实文件叫/etc/bind/named.conf。2.1 发行版差异与被改过的默认路径RHEL 系和 Debian 系的路径对照最好在一开始就记清楚后面所有命令都依赖它项目RHEL 系Debian 系软件包名bind、bind-utilsbind9、bind9-utils主配置文件/etc/named.conf/etc/bind/named.conf区域文件目录/var/named//var/cache/bind/服务名namednamed或bind9运行用户namedbind安装命令按发行版走# RHEL 系 dnf install -y bind bind-utils # Debian 系 apt update apt install -y bind9 bind9-utils bind9-dnsutils装完之后先用systemctl status namedDebian 系是bind9看一眼如果直接是 failed先别急着改配置多半是默认配置文件里的监听地址或者 SELinux 策略导致的改之前先确认原始状态否则你会把装完就有问题和我改出来问题混在一起排查难度翻倍。RHEL 系还有一个历史包袱老版本的 chroot 会把所有路径重定到/var/named/chroot/下。现在主流的版本已经默认不启用 chroot但配置文件里的directory指令还是指向/var/named区域文件的相对路径都基于这个目录。建议所有区域文件路径一律写绝对路径别用相对路径省得后面因为directory改动而全部失效。2.2 named.conf 的分层写法与 include 约定很多人把整份配置塞进一个named.conf两百行下来自己都看不清。我推荐从第一天就用分层结构把不同职责拆到不同文件/etc/named.conf # 主入口只放全局 options 和 include /etc/named.conf.local # 自己管理的区域声明 /etc/named.conf.acl # ACL 定义 /etc/rndc.key # 远程管理密钥主文件的核心骨架大致是这样options { listen-on port 53 { 127.0.0.1; 192.168.10.11; }; listen-on-v6 { none; }; directory /var/named; dump-file /var/named/data/cache_dump.db; statistics-file /var/named/data/named_stats.txt; memstatistics-file /var/named/data/named_mem_stats.txt; allow-query { localhost; 192.168.10.0/24; }; recursion yes; allow-recursion { localhost; 192.168.10.0/24; }; dnssec-validation no; # 内网环境先关掉避免上游不可达时 SERVFAIL forwarders { 192.168.10.1; }; }; include /etc/rndc.key; include /etc/named.conf.local;几个注释值得展开。listen-on明确列出要监听的地址比写any安全得多因为多网卡机器上你可能并不想让某个网卡对外提供服务。dnssec-validation在内网环境我通常先设成no原因是很多内网的上游解析或者转发链不支持完整的 DNSSEC 校验一旦开了某些域名会直接返回 SERVFAIL而这种失败在dig输出上跟域名不存在看起来很像排查起来极其费劲。等整个链路跑通之后再考虑第 8 章讲的方式打开。include的顺序也有讲究rndc.key要在需要它的语句之前被加载否则动态更新和远程管理会报找不到密钥。这个错误日志里通常会写couldnt add command channel看起来跟密钥无关实际上就是加载顺序问题。2.3 启动前的两道校验named-checkconf 与权限归属配置写完别急着systemctl restart。BIND 自带两个校验工具用好了能省下大量时间# 检查主配置语法不检查区域文件内容 named-checkconf -z /etc/named.conf # 单独检查某个区域文件 named-checkzone corp.test /var/named/corp.test.zone-z参数的作用是顺便把配置里声明的所有区域都加载一遍能提前发现区域文件不存在、语法错误、SOA 缺失这类问题。我的习惯是每次改完配置必跑一遍输出为空才算通过。权限是第二个高频坑。BIND 以named或bind用户身份运行区域文件必须对它可读而目录必须对它可写因为要生成.jnl日志文件和.signed签名文件。标准的设置是chown root:named /var/named/corp.test.zone chmod 640 /var/named/corp.test.zone chown named:named /var/named chmod 750 /var/named这样做的理由区域文件本身属主是 root防止被服务进程篡改组是 named保证可读目录属主给 named保证它能在里面写运行时文件。这套权限组合在 RHEL 系上基本不会出问题在 Debian 系上把组名换成bind即可。2.4 放行 53 端口的三种拦截面防火墙、SELinux、监听地址服务起不来或者起来了但客户端查不通90% 集中在三个地方。我把它们列成检查顺序防火墙。RHEL 系用 firewalld一条命令搞定firewall-cmd --permanent --add-servicedns firewall-cmd --reloadDebian 系如果用 ufw则是ufw allow 53/tcp和ufw allow 53/udp。注意DNS 同时需要 UDP 和 TCP常规查询走 UDP超过 512 字节的响应、区域传输、部分 DNSSEC 场景走 TCP。只放 UDP 会出现小查询正常、大记录失败的诡异现象。SELinux。这是最容易被忽略的一层。开启 enforcing 的机器上named默认只能读特定目录你自己新建的区域文件目录可能直接被拒绝日志在/var/log/audit/audit.log里服务日志里只看到permission denied但文件权限明明是好的。三个处理方式# 方式一给文件打上正确的 SELinux 标签 restorecon -Rv /var/named/ # 方式二允许 named 写主区域文件动态更新场景需要 setsebool -P named_write_master_zones on # 方式三临时确认是不是 SELinux 的问题仅用于诊断 setenforce 0我一般先setenforce 0试一下如果立刻好了就说明是 SELinux再切回 enforcing 用前两种方式正式解决。诊断阶段可以临时关闭但绝不能就这么放着直接把 SELinux 永久关掉是很差的做法后面再想开回来会发现一堆东西已经依赖它关闭的状态了。监听地址。listen-on里没写实际网卡的 IP服务就只监听127.0.0.1本机dig一切正常别的机器死活查不到。用ss -lnup | grep :53一眼就能看出来监听的地址是不是0.0.0.0或者目标网卡 IP。3. 正向解析区域文件SOA 的每个字段都在干什么区域文件是 DNS 服务器的核心也是最多人抄了但不知道为什么的地方。我先给一份可以直接用的完整示例然后逐个字段拆开讲。假设内网网段是192.168.10.0/24域名是corp.test解析服务器本身是192.168.10.11。3.1 一个最小可用的 corp.test 区域文件什么样$TTL 3600 $ORIGIN corp.test. IN SOA ns1.corp.test. admin.corp.test. ( 2024051201 ; serial 3600 ; refresh 600 ; retry 604800 ; expire 300 ; minimum / negative TTL ) IN NS ns1.corp.test. IN NS ns2.corp.test. IN A 192.168.10.11 ns1 IN A 192.168.10.11 ns2 IN A 192.168.10.12 www IN A 192.168.10.20 git IN A 192.168.10.21 db IN A 192.168.10.22 api IN CNAME www.corp.test. IN MX 10 mail.corp.test. mail IN A 192.168.10.25 IN TXT vspf1 ip4:192.168.10.25 -all$TTL是默认生存时间$ORIGIN是相对名称的基准。这两个指令让后面的记录可以省掉重复的后缀但它们也是域名后面多出奇怪后缀这个经典问题的来源第 3.4 节专门讲。3.2 SOA 里那五个数字到底在控制什么SOA 记录是区域文件的灵魂缺了它整个区域加载失败但五个数字的含义经常被含糊带过。逐个说serial序列号区域的版本号。主从同步的唯一判据就是它——从库发现主库的 serial 比自己大就拉取新数据相等或更小就什么都不做。常见做法是用YYYYMMDDnn格式比如2024051201表示 5 月 12 日第 1 次修改。这里有个必须知道的坑serial 只能单调递增不是改了就生效而是改大了才生效。有人手改回一个较小的数字从库就再也不更新了而且日志里毫无提示因为对从库来说主库版本比我还旧根本不是错误。refresh刷新间隔从库多久主动问一次主库有没有更新。3600 秒1 小时是常见值。调小的代价是查询压力调大的代价是同步延迟。retry重试间隔从库连不上主库时隔多久再试。600 秒比较稳妥。这个值设得太小主库宕机时会看到从库疯了一样重连。expire过期时间从库连续多久联系不上主库就认为自己的数据彻底失效停止应答该区域。604800 秒7 天是常见配置。它的实际意义是给主库故障留出的最长容忍窗口设得太短主库维护几小时从库就罢工了设得太长主库数据早就变了从库还在用错误数据应答。minimum负缓存 TTL这个字段历史上含义变过现在的作用是告诉客户端这个域名不存在的结论可以缓存多久。300 秒是合理值太大意味着你新建了一个域名客户端要等好几分钟才能解析到。一句话总结这段的实操价值主从不同步先看 serial从库罢工先看 expire改完记录不生效先看 minimum。这三个字段能解释主从相关的绝大多数现象。3.3 A、CNAME、MX、TXT 的写法与真实踩坑点A 记录最直白但要注意一个主机名可以有多条 A 记录这叫轮询BIND 默认会按轮转顺序返回用来做简单的负载分担。不过它没有健康检查一台挂了客户端还是会被分到所以真正的负载均衡还是得靠专门的组件。CNAME 有三条硬规则违反任意一条都会导致named-checkzone报错或者行为异常同一个名字不能既有 CNAME 又有其他类型的记录CNAME 不能指向另一个 CNAME虽然不严格禁止但会导致额外查询和解析变慢区域顶点不能设 CNAME因为顶点必须有 SOA 和 NS 记录。示例里api指向www.corp.test.客户端查api.corp.test会拿到 CNAME 再追一次 A最终得到192.168.10.20。注意目标必须写完整 FQDN 并带结尾点否则会被加上$ORIGIN变成www.corp.test.corp.test.这个错误极其常见而且named-checkzone不会报错因为它语法上完全合法。MX 记录前面的数字是优先级越小越优先。10、20、30 这种间隔是惯例方便以后在中间插入新记录。MX 指向的目标必须是 A 记录不能是 CNAME这是规范要求。TXT 记录的引号是必须的内容里有空格时更要注意。SPF 就是靠 TXT 承载的示例里那行意思是只有 192.168.10.25 这台机器可以用这个域名发信其他一律拒绝。测试环境用-all没问题但如果哪天真的配了对外发信忘了把真实出口地址加进去邮件会全被拒。3.4 为什么你的域名后面多了一串后缀结尾那个点DNS 里的名字分两种绝对域名FQDN和相对域名。绝对域名以点结尾比如www.corp.test.相对域名不带结尾的点会被自动拼上$ORIGIN。这意味着在$ORIGIN corp.test.的环境下你写的实际含义wwwwww.corp.test.www.corp.testwww.corp.test.corp.test.www.corp.test.www.corp.test.第三条才是对的。所有跨区域的引用比如 CNAME 目标、NS 目标、MX 目标都必须带结尾点否则一定会多出一层后缀。这个错误的表现是dig能返回结果但结果是错的客户端拿到的 IP 可能是个完全不相干的东西或者干脆 NXDOMAIN。我处理这类问题的习惯是写完区域文件先跑named-checkzone然后dig三个典型记录顶点 A、一个子域名 A、一个 CNAME逐个核对返回的全名对不对。别嫌麻烦这一步能拦住八成的低级错误。4. 反向解析与网段倒序PTR 记录不是可选项很多人搭 DNS 只做正向解析觉得反向解析可有可无。这在小规模内网确实能用但只要涉及日志分析、邮件发送、安全审计反向解析就是刚需——对方的服务器会拿你的 IP 去反查域名来验证身份查不到就直接判定为可疑。4.1 in-addr.arpa 的倒序规则与区域命名反向解析的核心规则只有一条IP 地址的四个字节要倒过来写。192.168.10.0/24这个网段对应的反向区域名是10.168.192.in-addr.arpa注意是10.168.192不是192.168.10。区域的声明方式zone 10.168.192.in-addr.arpa IN { type master; file /var/named/192.168.10.rev; allow-update { none; }; };区域文件内容$TTL 3600 $ORIGIN 10.168.192.in-addr.arpa. IN SOA ns1.corp.test. admin.corp.test. ( 2024051201 3600 600 604800 300 ) IN NS ns1.corp.test. 11 IN PTR ns1.corp.test. 20 IN PTR www.corp.test. 21 IN PTR git.corp.test. 22 IN PTR db.corp.test. 25 IN PTR mail.corp.test.因为$ORIGIN已经限定到/24这个粒度所以20就代表192.168.10.20。如果要做/16的反向区域名字就是168.192.in-addr.arpa记录要写成10.20这种两段形式。这里有个细节值得留意反向区域的 NS 和 SOA 里写的是正向域名。这不是笔误反向区域的权威服务器本身也要有个名字而那个名字来自正向区域。所以正向区域没有正确配置时反向区域就算加载成功验证过程中也可能出问题。4.2 PTR 与 A 记录必须成对维护的理由严格来说 DNS 规范并不要求 A 和 PTR 必须成对存在但运维实践里应该当成硬规则来执行。原因是只有 A 没有 PTR任何基于反查的校验都会失败只有 PTR 没有 A对方反查得到一个域名再正查这个域名却查不到 IP这比查不到更让人怀疑。我在做安全加固时遇到过这样的情况某台机器的 A 记录改了IP 从.20挪到.30但反向区域忘了改结果反查.30返回的是另一台机器的名字。这种不一致不会引发任何告警但会在日志分析里制造混乱非常难追溯。所以我的做法是把正向和反向写在同一个变更清单里每次改 A 记录就同步检查对应的 PTR改 PTR 就检查 A。听起来很笨但它能避免一类非常隐蔽的问题。4.3 批量生成 PTR 的脚本思路内网机器多的时候手写 PTR 不现实可以用一段脚本从主机名清单生成#!/bin/bash # hosts.txt 每行格式IP 主机名 while read -r ip name; do last$(echo $ip | awk -F. {print $4}) printf %-4s IN PTR %s.corp.test.\n $last $name done hosts.txt生成之后不要直接覆盖线上文件先输出到临时文件用named-checkzone校验通过再替换并且记得手动把 serial 加一。脚本最容易忘的就是这一条忘了 serial 从库就永远不同步而且没有任何报错。5. 递归、转发与权限边界别把服务器变成开放解析器权限配置这一块是区分能跑和能上生产的分水岭。我见过太多内网 DNS 直接写allow-query { any; }; recursion yes;结果被当成公开解析器使用带宽被打满、日志被刷爆还带来一堆安全风险。5.1 recursion 与 allow-recursion 是两个独立开关这两个参数经常被混为一谈实际职责不同recursion yes|no这台服务器是否具备递归查询能力allow-recursion { ... }哪些客户端可以使用递归能力。如果只写recursion yes不写allow-recursionBIND 的默认行为是跟随allow-query也就是谁能查谁就能递归。这在很多教程里被直接忽略但它是内网 DNS 变成开放解析器的根本原因。对一台只做内网权威解析的服务器最保守的配置是recursion no; allow-query { localhost; 192.168.10.0/24; };如果这台机器同时要承担缓存转发职责就得打开递归但必须严格限定来源recursion yes; allow-recursion { localhost; 192.168.10.0/24; }; allow-query-cache { localhost; 192.168.10.0/24; };allow-query-cache控制谁能读缓存跟allow-recursion分开设可以让内部客户端读缓存但限制谁能触发新的递归查询。这个粒度在流量大的环境里很有用。5.2 forwarders 与 forward only 的适用场景转发器解决的是我这台机器不该自己去根服务器逐级查询的问题。典型场景是内网只有一台机器能出网其他机器都靠它。forwarders { 192.168.10.1; 114.114.114.114; }; forward first;forward first的意思是先问转发器转发器不回或者超时才自己去查。forward only则是完全不自己查转发器挂了就查不了。选哪个取决于你对内网环境的确定程度如果内网严格禁止 DNS 直接出网用forward only这样配置意图明确不会被误以为还能回落如果不确定转发器是否稳定用forward first多一层兜底代价是超时场景下响应会变慢。还有一点转发器不要配太多两三个足够。配五个以上第一轮全部超时的话客户端早就放弃了。而且转发器的地址本身不要指向自己这个环路错误表现出来就是所有外部域名解析超时日志里能看到大量发往本机 53 端口的查询。5.3 ACL 与 allow-query、allow-transfer 的收口ACL 是 BIND 里被严重低估的功能。与其在各种指令里重复写网段不如先定义acl internal { 127.0.0.1; 192.168.10.0/24; 192.168.20.0/24; }; acl slaves { 192.168.10.12; };然后在options里统一引用allow-query { internal; }; allow-recursion { internal; }; allow-transfer { slaves; };allow-transfer是最关键的一个。区域传输会把整个区域的记录全部吐出去如果不限制任何人都能通过dig axfr corp.test 你的服务器把内网所有主机名和 IP 拿走。这个操作在渗透测试里是标准的踩点步骤。验证是否收口成功从一台不在slaves列表里的机器执行dig axfr corp.test 192.168.10.11期望结果是Transfer failed.如果吐出一大堆记录说明配置没生效。5.4 rndc 与 TSIG 密钥远程管理与动态更新rndc是 BIND 的管理通道能远程执行 reload、查看状态、刷新缓存。它依赖一个共享密钥生成方式rndc-confgen -a -c /etc/rndc.key生成后在named.conf里include进来。这个密钥文件权限必须是640且属主是 root:named否则等于把管理权限公开了。动态更新allow-update用的是同一套 TSIG 机制允许特定客户端通过nsupdate增删记录zone corp.test IN { type master; file /var/named/corp.test.zone; allow-update { key update-key; }; };开启动态更新有两个副作用必须提前知道一是 BIND 会在区域文件旁边生成一个.jnl日志文件所有动态变更先写日志需要rndc sync或者定期rndc freeze/thaw才会合并进主文件二是区域文件所在目录必须对named用户可写否则所有更新都会失败。我见过有人把目录权限设成755 root:root结果动态更新一直静默失败日志里只有一行不太显眼的写入错误。如果不需要动态更新就老老实实写allow-update { none; };这是默认也是推荐做法。6. 主从复制与视图分离让解析服务顶得住主从架构的价值前面提过这里讲落地。核心配置其实只有主库三行、从库三行但每行的作用必须清楚。6.1 主从同步的四行关键配置主库侧zone corp.test IN { type master; file /var/named/corp.test.zone; allow-transfer { 192.168.10.12; }; also-notify { 192.168.10.12; }; };从库侧zone corp.test IN { type slave; masters { 192.168.10.11; }; file /var/named/slaves/corp.test.zone; };also-notify的作用是主库 serial 变化后主动通知从库而不是等从库按refresh周期来问。这能把同步延迟从一小时缩短到几秒代价可以忽略强烈建议加上。如果中间有网络设备拦截通知可能到不了从库还是能靠定时刷新兜底所以这不是单点依赖。从库的区域文件路径通常放在slaves子目录下这个目录必须对named可写——从库要自己下载并保存区域文件。这个目录忘了建是新手最常犯的错表现是服务能启动但dig查从库返回 REFUSED 或者 SERVFAIL日志里写着open: file not found之类。6.2 serial 不自增导致从库不更新的排查这是主从环境第一大问题而且排查起来很容易跑偏。完整的检查链路我总结成五步步骤检查项命令或位置典型现象1主库 serial 是否已增大查看主库区域文件数字没变2从库是否收到通知从库日志notify关键字没有相关记录3从库能否连上主库 53/tcpdig axfr corp.test 主库超时或被拒4allow-transfer是否包含从库 IP主库配置transfer failed5从库区域文件是否可写目录权限与 SELinux写入报错我遇到的最隐蔽一次是第三步防火墙只放行了 UDP 53allow-transfer配得完全正确但从库就是拿不到数据。因为区域传输走 TCP这个细节在大多数防火墙配置教程里被一笔带过。6.3 view 做内外网分离的实际写法如果一台机器要同时给内网和公网提供不同答案就得用 view。基本思路是先定义客户端分组再为每个组各写一套 zoneacl internal { 192.168.10.0/24; }; acl external { any; }; view internal-view { match-clients { internal; }; recursion yes; zone corp.test IN { type master; file /var/named/corp.test.internal.zone; }; }; view external-view { match-clients { external; }; recursion no; zone corp.test IN { type master; file /var/named/corp.test.external.zone; }; };内网版本里可以写db.corp.test指向192.168.10.22公网版本里同一个名字可能指向一个公网地址或者干脆不写这条记录。这就是所谓的内外网分离视图。view 有几个必须注意的限制一是所有 zone 必须写在某个 view 内部不能有游离在外的 zone二是 view 的匹配顺序是从上往下第一条命中就停所以external的any必须放在最后三是不同 view 里的同名 zone 是完全独立的serial 各自维护改了一边别忘另一边。6.4 动态更新与 DHCP 联动的思路内网机器地址由 DHCP 分配的场景下主机名和 IP 的对应关系是动态的手改区域文件跟不上。标准做法是 DHCP 服务在分配地址后通过 TSIG 认证向 DNS 发送动态更新自动写入 A 和 PTR 记录。这个链路配置不复杂但涉及两个服务的密钥一致性一旦密钥不匹配表现就是地址分配正常但反查一直查不到。排查时先用nsupdate手动发一条测试记录确认 DNS 侧能接受更新再去查 DHCP 侧的配置能把问题范围缩小一半。7. 排错链路从 dig 到日志的逐层定位方法DNS 排查最忌讳的就是东改一下西改一下。我习惯固定按一条链路走客户端 → 网络 → 服务监听 → 配置加载 → 区域数据 → 上游。每一层都用一个确定的命令验证不让猜测进入流程。7.1 dig 输出逐段解读dig 192.168.10.11 www.corp.test A输出里最该看的是这几段;; -HEADER- opcode: QUERY, status: NOERROR, id: 34567 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1 ;; ANSWER SECTION: www.corp.test. 3600 IN A 192.168.10.20 ;; Query time: 1 msec ;; SERVER: 192.168.10.11#53(192.168.10.11)status字段的含义直接指向问题类型NOERROR且有 ANSWER正常NOERROR但 ANSWER 为空域名存在但没有该类型的记录比如查 AAAA 但只有 ANXDOMAIN这个域名在该区域里不存在先检查区域文件里到底有没有写SERVFAIL服务器处理失败通常是区域文件语法错误、DNSSEC 校验失败、或者转发不可达REFUSED被权限拒绝了几乎一定是allow-query没包含这个来源地址。flags里的aa表示权威回答如果查的是自己有 zone 的域名却没有aa说明请求根本没命中那个 zone可能 zone 没加载成功也可能被别的 view 接走了。rd是客户端请求递归ra是服务器支持递归从库上看ra为 false 时递归查询会失败这是正常设计。7.2 五类高频故障与对应检查点现象最可能的原因检查命令客户端完全不通防火墙 / 监听地址ss -lnup | grep :53、firewall-cmd --list-all本机能查别机不能查listen-on只写了 127.0.0.1查看 named.conf 的 options返回 REFUSEDallow-query未包含来源检查 ACL 与 allow-query返回 SERVFAIL区域文件错误 / 上游不可达named-checkzone、看 named 日志改了记录不生效serial 未增 / 客户端缓存提高 serial、dig norecurse绕缓存第三行和第五行是最常见的两个。REFUSED 基本不用怀疑别的原因就是权限。而改了不生效里如果是从库环境先排除 serial如果只有一台机器那就是 TTL 和客户端缓存的问题用dig norecurse直连服务器能绕过缓存立即看到真实结果。7.3 named-checkzone 与日志级别调整named-checkzone的完整用法named-checkzone -d corp.test /var/named/corp.test.zone-d会输出详细的加载过程能看到每一条记录被解析成什么全名。这个功能在排查域名后缀重复问题时极其有效因为它直接把解析后的 FQDN 打出来。日志方面BIND 默认的日志级别比较克制很多问题看不出来。可以在named.conf里加一段logging { channel default_log { file /var/log/named/named.log versions 5 size 20m; severity info; print-time yes; print-category yes; print-severity yes; }; category default { default_log; }; category xfer-out { default_log; }; category update { default_log; }; };severity info已经能覆盖大部分排查需求再往上调到debug会刷出海量日志只在定位特别刁钻的问题时临时开定位完立刻关掉否则日志文件几小时就能撑满磁盘。8. 长期运行日志轮转、性能与安全加固配置跑通只是开始真正决定这套服务能不能长期用的是日志、性能和安全这三块。它们平时不显眼出问题的时候都是大问题。8.1 query log 的开与关想知道谁在查什么就打开查询日志logging { channel query_log { file /var/log/named/query.log versions 3 size 50m; severity info; print-time yes; }; category queries { query_log; }; };开启后每一条查询都会记录来源 IP、查询名、查询类型和结果状态。它的价值在于定位异常查询源比如某台机器在疯狂重试一个不存在的域名或者某个网段在做区域探测。但它有两个明显的代价一是日志写入量巨大几百台机器的内网每天能产生几百 MB二是磁盘 IO 有开销。所以我的做法是平时关着需要排查时临时打开用rndc reconfig生效排查完立即关闭。长期开着不仅是资源浪费写进去的内容也没人会看。8.2 rate-limit 与 minimal-responses 这两个开关rate-limit是限制单个客户端查询速率的机制rate-limit { responses-per-second 20; window 5; };它的实际作用是削弱放大攻击的影响——如果你的服务器被利用来做反射放大响应速率限制能让它不至于把出口带宽全部吃掉。内网环境一般用不上但只要服务器有公网地址就应该配上。默认值偏宽松20 次/秒对正常客户端足够对攻击流量能形成有效压制。minimal-responses yes;的作用是不在响应里附带不必要的记录。正常情况下 DNS 响应会带上 AUTHORITY 和 ADDITIONAL 段里面可能有 NS 记录和它们的 A 记录这些对客户端解析答案并不必需。开启之后响应体积明显缩小在高并发场景下能省下可观的带宽。这个参数对排查问题是有一点副作用的响应变短之后dig输出里少了那些附加信息有时候会让人误以为记录缺失。知道这一点就不会被误导。8.3 DNSSEC 校验要不要开DNSSEC 校验开启后服务器会验证上游返回的数据签名被篡改或者不完整的响应会被拒绝返回 SERVFAIL。它的安全价值是实实在在的但落地有两个前提一是上游链路必须完整支持。内网的多级转发、某些中间设备会破坏 DNSSEC 相关记录一旦断了开启校验后这些域名全部解析失败。二是排查难度上升。开启后 SERVFAIL 的原因多了一种而且从现象上跟上游不可达很难区分。我的做法是分阶段内网刚上线时设dnssec-validation no;把基础链路跑通稳定运行一段时间后改为auto观察一周重点看有没有出现成片的 SERVFAIL确认无异常再保持。如果确实需要清理某几个域名的校验问题用negative-trust-anchors做例外处理而不是把整个校验关掉。8.4 备份与变更流程DNS 配置的特点是改错一次影响全网所以变更流程比技术本身更重要。我这套流程是踩过坑之后固定下来的改之前先备份cp corp.test.zone corp.test.zone.$(date %Y%m%d)配置文件和区域文件一起备份本地校验named-checkzone和named-checkconf -z都跑一遍输出必须为空先改从库或者备用机用dig 备用机验证结果确认真实返回符合预期再改主库改完立即用dig从客户端侧验证不要只看服务器本机保留回滚文件至少一周确认无问题再清理。第 3 步是关键。直接改主库然后祈祷不出事是我早期最常犯的错误。把改动先在备用机上验证成本只有几十秒能避免的却是整个内网的解析中断。监控方面最朴素的方案往往最有效一个定时任务每分钟对几个关键域名做dig返回非 NOERROR 就告警。这个脚本不到二十行但能比任何复杂监控更早发现问题——毕竟 DNS 挂了的时候所有依赖域名的服务都会跟着挂而它们的报错信息通常完全看不出根因在 DNS 上。最后分享一个小技巧把常用的验证命令写成一个脚本改完配置直接跑一遍输出所有关键检查项。我自己的脚本里包含主库 SOA 检查、从库 SOA 比对确认 serial 一致、正向和反向各三条抽样、一次axfr拒绝测试。这套检查跑完不到五秒但它拦下的问题比我事后排查的加起来还多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询