资讯动态

内网DNS服务器搭建实战:BIND正向解析、主从同步与缓存转发

发布时间:2026/10/21 9:09:33 来源:尧图企业网站定制
1. 项目概述与核心思路做运维这些年DNS 算是打交道最多的基础设施之一。可能有人觉得这不就是个域名解析吗但真到排查线上故障时DNS 配置不规范引来的问题往往比应用本身的问题更隐蔽、更难查。这个项目的核心目标是从零搭建一套内网 DNS 实战环境把正向解析、反向解析、主从同步、缓存转发这些基础能力一项项落到底最终形成一套可以复现的配置模板既能给团队内部用也能作为自己理解 DNS 协议与 BIND 实现的练习场。我选择用 Linux 下的 BIND 9 作为承载服务。理由很简单它是目前使用范围最广、文档最全、行为最符合标准实现的 DNS 服务器软件社区踩坑记录足够多出了问题也容易搜到答案。相比直接使用云厂商提供的 DNS 服务或路由器内置的简单转发BIND 能让你真正掌握解析链路里的每个环节——从 named.conf 的语法、zone 文件的格式、SOA 记录的序列号机制到 NOTIFY 通知和增量传输的工作方式。只有亲手配置过一遍日后在处理生产环境问题时才不至于两眼摸黑。这个方案适合谁一类是想在纯内网环境做域名管理、不想依赖公网 DNS 的团队另一类是刚入门的运维或网络方向同学想搞明白输入一个网址到拿到 IP 地址之间到底发生了什么。无论哪类读者我都建议跟着下文把每个配置亲手敲一遍而不是只读不练。DNS 的配置文件语法严格、错误反馈也比较老实用它入门 Linux 服务配置管理其实是很合适的练手项目。1.1 为什么需要在内部搭建 DNS 服务内部搭建 DNS 的直接动力是机器多了以后记 IP 记不过来了。服务器、虚拟机、容器、网络设备、打印机……只要设备数量超过十几台靠 hosts 文件维护映射关系就会非常痛苦。hosts 文件只能解决本机访问的问题设备一变、IP 一变所有机器的 hosts 都得跟着改维护成本直线上升。而一套内部 DNS 可以做到集中管理。只需要在个别节点把 DNS 指向这台上游新增设备时在 zone 文件里加一条 A 记录全内网立刻生效。另一个常见场景是服务间调用微服务架构里服务和服务的通信往往基于域名而非 IP 地址比如应用通过db.internal访问数据库主节点通过api.registry访问注册中心。这些内部域名如果交给公网 DNS 解析显然不合理也没有必要。自建 DNS 后还能顺手把内部服务的 TTL 调低方便服务迁移时快速切换解析结果。1.2 这套方案要解决的核心痛点结合我实际维护过几套内网 DNS 的经验最大的痛点往往不是配不出来而是配出来以后过两周就出问题。典型的有三类一是配置语法写错但服务没报错。BIND 对 zone 文件里的空行、Tab 和注释位置比较敏感多一个空格少一个括号都可能造成解析异常。好在有named-checkconf和named-checkzone这两个工具用它们提前校验绝大多数低级错误都能在重启服务之前被发现。二是主从不同步。很多团队做了主从架构但改完主服务器上的 zone 后从服务器迟迟不更新。常见原因就是 SOA 记录里的序列号没递增从服务器认为数据没变化自然就不做区域传输。这个坑极其经典下文专门有一节讲它。三是权限和防火墙问题。BIND 通常在 53 端口上监听 TCP 和 UDP如果防火墙只放行了 TCPUDP 查询就会全部超时。还有 SELinux 环境下zone 文件放错目录会导致 named 进程读不到数据。这些问题排查起来不难但没遇到过的人往往会绕圈子。这个项目就是把上述问题全部过一遍给出一套能用的配置指出哪里容易踩坑再配合 dig 命令的验证方法让整个排查链路清晰可见。2. 环境准备与基础安装动手之前先把实验环境说清楚。这套配置适用于主流的 Linux 发行版我用的是 RHEL 系和 Debian 系各跑过一遍。RHEL 系比如 CentOS、Rocky Linux用yum或dnf安装Debian 系用apt。底层原理一致只是包名和配置文件路径稍有差异。下文以 RHEL 系为主路径写Debian 系的差异点我会在合适的位置单独标注。建议准备两台虚拟机或容器来完整演练主从同步。第一台作为主 DNS第二台作为从 DNS操作系统配置干净纯净不要预装多余服务避免端口冲突干扰排查。2.1 选型说明为什么是 BIND可能有人会问现在有很多轻量级 DNS 服务器比如 dnsmasq、PowerDNS、CoreDNS为什么偏偏选 BIND我的回答是学习阶段选 BIND是为了学完整的 DNS生产环境如果只是给开发环境做简单缓存dnsmasq 当然更轻快但那样学不到 DNS 的核心机制。BIND 覆盖了 DNS 服务器的完整功能面权威解析、递归查询、转发、缓存、视图、DNSSEC、ACL、日志分类全部都有成熟的配置语法。虽然它的配置门槛比 dnsmasq 高但理解它的核心概念区域、资源记录、SOA、NS之后再看其他 DNS 软件的文档几乎是降维打击。并且 BIND 的性能和稳定性经过了几十年的生产环境验证很多企业级网络设备的管理界面背后跑的就是 BIND 或其商业版本选它做实验和学习对象技术投入不会浪费。2.2 安装步骤与启动安装命令很简单。RHEL 系yum install -y bind bind-utilsDebian 系apt update apt install -y bind9 bind9utils bind9-docRHEL 系安装后的主配置是/etc/named.conf配置文件目录在/etc/named/zone 文件默认放在/var/named/下Debian 系主配置是/etc/bind/named.confzone 文件通常在/etc/bind/下。我习惯在 RHEL 系环境下用/etc/named/存放自定义 zone 文件用include语句引入保持主配置干净清晰。安装完成后先不着急写配置把服务启动一次确认能跑起来systemctl start named systemctl status named如果启动失败直接看日志journalctl -u named -n 50一个常见情况是服务起不来但配置看着没问题那十有八九是配置文件权限或 SELinux 上下文不对。检查一下/var/named/下的文件权限确保 named 进程对 zone 文件有读取权限。RHEL 系建议用restorecon -Rv /var/named恢复 SELinux 上下文这个问题我下文会再展开。提示实验环境里如果不需要开机自启可以保持 disabled但作为内网基础设施生产环境建议systemctl enable named。3. 核心配置文件拆解named.conf 与 zone 机制BIND 的配置文件体系分为两层第一层是named.conf负责定义全局选项、访问控制、日志和区域声明第二层是 zone 文件负责存放具体域名与 IP 映射的资源记录。理解这两层的关系Debug DNS 就等于完成了一半。3.1 named.conf 全局选项的含义打开新建的 named.conf通常是空壳加几个默认配置。我以一个最精简的内网权威 DNS 配置为例options { listen-on port 53 { 192.168.1.10; }; # 监听内网IP listen-on-v6 port 53 { none; }; # 不监听IPv6 directory /var/named; dump-file /var/named/data/cache_dump.db; statistics-file /var/named/data/named_stats.txt; allow-query { 192.168.1.0/24; }; # 只允许内网网段查询 allow-transfer { 192.168.1.20; }; # 只允许从服务器做区域传输 recursion yes; dnssec-enable yes; };这里面有几个参数要特别注意。listen-on决定了服务绑定的网卡地址。默认配置会监听本机所有 IP但安全起见我一般明确写到内网 IP避免服务意外暴露到公网或测试网段。如果本机有多个网卡不写这个参数就全部暴露了这不算大错但不符合最小暴露面原则。allow-query是查询权限。实验环境可以写成内网网段如果未来要对接办公室的多个 VLAN可以用 ACL 集中定义。注意这里控制的是谁能问我问题而不是我能回答什么问题后者由 zone 的配置和递归行为决定。allow-transfer很关键。它限制哪些服务器可以对这台权威服务器发起区域传输请求。区域传输会把整个 zone 的数据一次性同步过去如果这个选项配置不当外部任何人都能把自己伪装成从服务器把内网域名记录全部拖走这是内外网信息泄露的高危点。生产环境务必要把这个配置成具体 IP 或专用 ACL。3.2 zone 声明与 named.rfc1912.zones 的关系在 RHEL 系默认配置里/etc/named.conf末尾通常会 include 一个/etc/named.rfc1912.zones里面预置了 localhost 和 loopback 等基础 zone。我的习惯是把自己管理的业务 zone 单独写一个文件比如/etc/named/rdnz_zones.conf再在主配置里 include 进来。这样一来主配置只负责全局选项和 ACL业务区域和公共配置分离后续用配置管理工具批量下发时思路也清晰。一个典型的正向 zone 声明是zone example.com IN { type master; file example.com.zone; allow-update { none; }; };type master表示这是权威主区域。file指向 zone 文件路径注意这里用的是相对路径实际根目录由directory选项指定即/var/named。allow-update一般直接none避免开放动态更新。反向 zone 声明则指向 in-addr.arpa 域zone 1.168.192.in-addr.arpa IN { type master; file 192.168.1.zone; };3.3 zone 文件语法与资源记录详解zone 文件是 DNS 配置里最容易被写错的部分。我先解释核心概念再给完整示例。zone 文件至少要包含SOA记录和NS记录然后根据业务需求添加 A、AAAA、CNAME、MX、TXT、PTR 等记录。SOA记录是整个区域的主控记录它包含如下字段serial序列号。主从同步靠它判断数据新旧每次修改 zone 文件后必须递增建议用日期格式2025021501。refresh从服务器多久检查一次主服务器数据版本。retry从服务器检查失败后重试的间隔。expire从服务器一直连不上主服务器时多久之后丢弃本地数据。negative TTL无记录应答的缓存时间。把这些参数理解透主从不同步的问题基本解决了一半。NS记录用于声明区域的权威服务器也就是哪些服务器有资格回答这个区域的查询。实际写 zone 文件时行首的域名可以省略不写用 Tab 或空格开头就表示继承上一行的域名。这个特性让新手困惑过一阵——看起来没写域名但记录其实属于某个明确的所有者。建议刚开始写的时候不要偷懒每一行都写全域名避免理解偏差。4. 正向解析与反向解析实战这一节把最常用的正向解析和反向解析完整走一遍每一个步骤都可以照抄。4.1 正向解析 zone 配置示例业务需求如下内网网段192.168.1.0/24主 DNS 服务器 IP 为192.168.1.10域名为ns1.example.com从 DNS 服务器为192.168.1.20域名为ns2.example.com。需要解析的机器有web1192.168.1.11、web2192.168.1.12、db1192.168.1.13等。在/var/named/下创建example.com.zone$TTL 86400 IN SOA ns1.example.com. admin.example.com. ( 2025021501 ; serial 3600 ; refresh 600 ; retry 86400 ; expire 300 ) ; negative cache TTL ; ; Name servers IN NS ns1.example.com. IN NS ns2.example.com. ; ; DNS server hostnames ns1 IN A 192.168.1.10 ns2 IN A 192.168.1.20 ; ; Web servers web1 IN A 192.168.1.11 web2 IN A 192.168.1.12 ; ; Database server db1 IN A 192.168.1.13 ; ; Alias for load balancer www IN CNAME web1.example.com.几个要点说明。$TTL 86400表示默认 TTL 一天。内部服务建议把 TTL 调短比如 300 秒这样服务 IP 有变化时内网机器能在 5 分钟内拿到新的解析结果不需要等缓存过期。结尾的域名一定要带点号。ns1.example.com.末尾的点表示这个是完整域名FQDN如果不写点号BIND 会默认把当前 zone 的域名追加上去结果变成ns1.example.com.example.com.。这个错误非常经典排查时dig返回的解析结果会多出一段奇怪的域名后缀我见到过不下五次。CNAME记录指向的域名也必须是完整域名并且最好指向一个本来就存在的 A 记录。如果指向了一个同样用 CNAME 定义的域名就会形成 CNAME 链链太长会影响解析效率甚至在某些代理环境下解析失败。4.2 反向解析 zone 配置实例反向解析的作用是把 IP 解析成域名主要用于邮件服务器反垃圾邮件校验和日志审计。内网不一定需要但作为完整 DNS 服务建议配置上。创建/var/named/192.168.1.zone$TTL 86400 IN SOA ns1.example.com. admin.example.com. ( 2025021501 ; serial 3600 600 86400 300 ) ; IN NS ns1.example.com. IN NS ns2.example.com. ; 11 IN PTR web1.example.com. 12 IN PTR web2.example.com. 13 IN PTR db1.example.com.然后声明反向区域在/etc/named/rdnz_zones.conf中添加zone 1.168.192.in-addr.arpa IN { type master; file 192.168.1.zone; };反向 zone 的文件名格式是反向网段.zone内容里每行开头的数字是要解析的 IP 最后一段。注意反向域名的书写顺序网段192.168.1.0/24对应反向域是1.168.192.in-addr.arpaIP 的最后一段写在前面网段顺序完全反转。4.3 配置检查与加载验证写完配置后不要急着 restart 服务先做检查。named-checkconf /etc/named.conf named-checkzone example.com /var/named/example.com.zone没输出即代表通过。如果有问题它会直接告诉错误在哪行哪个 token。检查通过后重载systemctl reload named验证解析结果使用digdig 192.168.1.10 web1.example.com dig 192.168.1.10 -x 192.168.1.11第一条命令期望输出里有web1.example.com. 86400 IN A 192.168.1.11第二条期望看到11.1.168.192.in-addr.arpa. 86400 IN PTR web1.example.com.。如果dig返回SERVFAIL优先检查 zone 文件语法返回REFUSED则检查allow-query权限是否覆盖了来源 IP。5. 主从同步实战从原理到落地主从架构是内网 DNS 高可用的基础。主服务器负责读写从服务器通过区域传输获得数据副本。客户端可以随便指向任意一台挂了另一台还能继续解析。5.1 主从同步原理与前提主从同步的触发机制是 NOTIFY 通知配合 SOA 序列号轮询。主服务器加载新 zone 后会向 NS 记录中声明的从服务器发送 NOTIFY 消息告知我有新数据了从服务器收到通知后主动向主服务器查询 SOA 序列号。如果序列号比本地的数字大则发起区域传输把整个 zone 或增量数据同步过来。即便没有 NOTIFY从服务器也会每隔 refresh 时间去主服务器查一次序列号。所以序列号递增不是可选项是硬性要求。改 zone 文件后我一般手动 1没有用过自动生成。部分团队会用脚本来检测文件变化并自动递增序列号生产环境那样做更省心但手工理解机制是第一步。5.2 主服务器端配置主服务器上在 zone 声明中添加also-notify或直接在 NS 记录中声明从服务器即可。推荐两个都做NS 记录用来声明权威性allow-transfer 用来开放传输权限。主服务器 zone 声明调整zone example.com IN { type master; file example.com.zone; allow-transfer { 192.168.1.20; }; also-notify { 192.168.1.20; }; };zone 文件中要确认 NS 记录里包含ns2.example.com.和它的 A 记录。如果从服务器的 NS 声明缺失一些严谨的客户端会拒绝使用这台从服务器做解析因为它们认为它不是这个区域的权威。这个点之前容易漏。修改完成后照旧named-checkconf、named-checkzone然后 reload。5.3 从服务器端配置从服务器的 zone 声明使用type slave不需要手工创建 zone 文件区域传输会自己把数据落盘。zone example.com IN { type slave; masters { 192.168.1.10; }; file slaves/example.com.zone; };注意file指向的位置建议放在从服务器有写权限的目录RHEL 系默认是/var/named/slaves/这个目录存在并且 named 有写入权限。如果不放在这个目录区域传输可能因为权限问题失败。从服务器的named.conf全局选项中allow-transfer就没必要留了从服务器不向其他机器再提供区域传输。allow-query依然配置内网网段保证客户端能查询。重启从服务器的 named然后观察日志systemctl restart named journalctl -u named -f如果主从配置正确日志里会出现类似zone example.com/IN: Transfer started和transfer of example.com/IN from 192.168.1.10#53: Transfer status: success的内容。客户端把 DNS 指向从服务器再 dig 一次能拿到同样的解析结果说明主从架构生效了。5.4 序列号更新的实际意义前面反复提到序列号这里用一个真实场景佐证它的重要性。某次我在 A 环境改了 web1 的 IP从 11 改成 15修改完成后忘记递增 SOA 序列号。主管服务器重启之后从服务器自己也正常但客户端访问新 IP 一直不稳定时好时坏。排查到最后才发现从服务器的序列号还是旧值它认为主服务器的序列号更小或相同不触发区域传输。当客户端恰好被负载均衡指到从服务器时拿到的还是旧的解析结果。修复方式很简单序列号加一reload 主服务器然后立刻在从服务器上执行rndc reload example.com强制刷新这个区域。几秒钟内数据就同步过来了。这个案例说明对 SOA 序列号机制的理解不能停留在知道要改。它直接影响主从同步是否触发是整个分布式 DNS 体系里最基础也最关键的握手协议。6. 缓存与转发配置实践除了承担权威解析DNS 服务器经常同时充当内网客户端的递归解析入口。客户端把 DNS 指向它它负责向外部的上游 DNS 查询结果并缓存下次再有相同查询时直接返回缓存减少跨公网的查询延迟。6.1 缓存机制与 TTL 的关系BIND 默认自带缓存功能不需要额外安装。缓存的主要控制参数就是 TTLzone 文件里设置的 TTL 值决定了缓存最长保留多久。比如公网域名的 TTL 是 600 秒那么内网 DNS 第一次递归解析后会缓存这个答案 10 分钟期间再有客户端查询直接从缓存返回不产生外网流量。缓存虽好但也要警惕缓存污染。如果上游返回了错误答案或者攻击者注入了一条伪造记录内网 DNS 会把这个答案原样缓存并分发给所有查询者。基础的防御手段是开启 DNSSEC 验证BIND 9 默认配置基本已经启用dnssec-enable但要真正验证还需要配置信任锚以及dnssec-validation auto。内网环境如果没有统一的 DNSSEC 信任链建议至少保留默认的 auto 模式并在递归出口防火墙上限制对外访问降低被攻击面。6.2 配置转发模式forward only 与 forward first递归模式下BIND 会自行向根服务器发起迭代查询。但很多内网 DNS 不希望直接暴露到公网更希望把出网流量集中交给某个上游 DNS比如运营商 DNS 或公司统一提供的出口 DNS。这时候就用到forward配置。在 named.conf 的 options 里加forwarders { 223.5.5.5; 8.8.8.8; }; forward only;forward only表示这台服务器自己不向根服务器查询只把请求转发给列出的上游。如果上游全部不可达就返回 SERVFAIL。另一种模式是forward first表示先转发如果超时再自己迭代查询。生产环境为了流量可控我一般用forward only但上游至少配两个避免单点。需要注意转发只影响非本机权威的域名查询。本地定义的 example.com 依然由本机直接回答不会跑到转发器上绕一圈。这个逻辑很容易理解权威数据优先于转发数据。6.3 测试缓存与转发效果配置完成后执行dig 192.168.1.10 www.example.org dig 192.168.1.10 www.example.org第二次查询的Query time应该显著小于第一次并且 ANSWER SECTION 中可以看到来自缓存的记录。再用rndc stats可以查看缓存命中率输出中Cache Hits和Cache Misses的比例能直观反映缓存效果。实际运营里我经常开着rndc dumpdb -cache来检查缓存里是否有不该存在的异常记录这些命令在排查疑难解析问题时很顺手。7. 常见问题与排查技巧实录最后这部分把我在实操中真正遇到频率最高的坑和排查方法汇总成一张排障地图。它既是备忘录也是团队接手这套 DNS 时的快速上手文档。7.1 解析超时的排查顺序客户端dig 192.168.1.10 example.com没反应或者超时我建议按下面顺序排查。第一确认服务进程活着。systemctl status named或ps -ef | grep named如果进程不在看日志找启动失败原因。第二确认 UDP 53 端口有监听。ss -ulnp | grep 53没有输出说明监听地址配错了。检查listen-on是否覆盖了你访问的内网 IP。之前有一次就是因为监听写成了127.0.0.1客户端怎么查都超时。第三确认防火墙。RHEL 系执行firewall-cmd --list-all看 UDP 53 和 TCP 53 是否放行。TCP 53 常常被忽略虽然普通查询走 UDP但响应包太大时比如 DNSSEC 记录会切换到 TCPTCP 被墙同样会导致部分查询失败。第四确认客户端到服务器的网络连通性。ping服务器 IP 能通不代表 DNS 一定通直接用dig加time3 tries2参数快速试探能减少等待时间。7.2 主从不同步的经典原因主从不同步优先看从服务器日志。日志里如果有zone example.com/IN: refresh: retry limit reached说明从服务器连不上主服务器或序列号没有变化。逐个排查主服务器的allow-transfer是否包含从服务器 IP从服务器的masters写的 IP 是否有误。主从之间的防火墙是否放行了 TCP 53。区域传输走 TCP不是 UDP这点特别容易忽略。主服务器 zone 文件序列号是否递增。从服务器日志提示got NOTIFY但随后no newer serial时基本就是序列号问题。从服务器的 zone 文件目录是否可写。/var/named/slaves权限不对时区域传输会报permission denied。提示修改主服务器 zone 后手工执行rndc reload example.com可以加速通知推送比等 NOTIFY 自动触发更可控。生产环境做变更时我习惯先 reload 主服务器再在从服务器上rndc reload强制同步确保数据落地。7.3 SELinux 与文件权限导致的问题RHEL 系的 SELinux 对 named 的限制也是重灾区。zone 文件如果临时从其他位置拷贝过来SELinux 上下文不正确named 进程会拒绝读取。表现是配置检查通过、服务也能启动但解析时永远报 SERVFAIL。排查方法ls -lZ /var/named/example.com.zone正常输出里应该包含named_zone_t这样的上下文标识。如果不对执行restorecon -v /var/named/example.com.zone还有一个常见现象服务重启正常但journalctl里频繁出现denied字样这时候看 SELinux 的 audit 日志/var/log/audit/audit.log里能找到具体的违规操作。定位后如果是合法操作用audit2allow生成对应策略放行或者调整文件上下文而不是粗暴关闭 SELinux。7.4 日常运维命令速查整理一份我日常用得最多的命令清单用途命令检查主配置语法named-checkconf检查 zone 文件语法named-checkzone example.com /var/named/example.com.zone重载配置systemctl reload named重启服务systemctl restart named查看服务状态systemctl status named跟踪日志journalctl -u named -f测试解析dig 127.0.0.1 web1.example.com测试反向解析dig 127.0.0.1 -x 192.168.1.11查看缓存统计rndc stats强制重载某区域rndc reload example.com开启查询日志rndc trace/rndc trace 3最后再分享一个我常用的保命技巧每次准备大规模的 DNS 变更前先导出当前配置文件的备份并把 zone 文件单独打包。命令很简单cp /etc/named.conf /etc/named.conf.bak再加上tar czf /var/named/zones_backup.tgz /var/named/*.zone。这个习惯帮我避免过好几次误操作后的手忙脚乱。DNS 一旦出错影响的是整网所有依赖域名的服务宁可慢五分钟也要保证每一步回退路径清晰。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑