恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
DNS主从与分离解析实战:BIND高可用架构配置指南
首页
资讯中心
/
DNS主从与分离解析实战:BIND高可用架构配置指南
DNS主从与分离解析实战:BIND高可用架构配置指南
发布时间:2026/10/20 19:37:06
1. 为什么还要折腾DNS主从与分离解析很多人觉得DNS这东西配一次能解析就行了搞什么主从、搞什么分离解析纯属给自己找事。我刚开始接触DNS那会儿也是这么想的直到有一次线上环境出问题——单台DNS服务器扛不住查询压力解析延迟飙升业务方电话直接打到我这里来。从那以后我才认真去研究DNS的高可用架构也就是主从服务器加分离解析这套组合拳。先说清楚这套东西到底是什么、能干什么。DNS主从服务器简单理解就是一台主DNS负责写记录一台或多台从DNS自动同步主服务器的记录对外提供查询服务。主挂了从还能顶上去解析不中断。分离解析也叫视图解析是指同一台DNS服务器根据请求来源的不同返回不同的解析结果。比如内网用户查某个域名返回内网地址外网用户查同一个域名返回公网地址。这两个技术组合在一起基本能覆盖中小规模网络环境里DNS的高可用和内外网隔离需求。这套方案适合谁我总结下来是三类人一是负责企业内网运维的工程师需要保证内网DNS稳定二是做IDC或云环境的技术人员需要给不同网络区域的用户返回不同解析结果三是自己搭实验环境学习DNS原理的爱好者。不管你基础如何只要跟着步骤走这套配置是能直接落地的。我下面会从整体设计思路讲起然后拆解主从同步和分离解析的核心配置再给出一套完整的实操流程最后把我踩过的坑和排查经验整理出来。内容比较长建议先收藏配环境的时候对着看。2. 整体架构设计与方案选型思路2.1 为什么选主从加视图这套组合DNS服务器的高可用方案其实有好几种。最简单的可以用多台独立DNS服务器每台手动配一样的记录但这样维护成本极高改一条记录要登好几台机器。也可以用负载均衡设备在前面挡一层后面挂多台DNS但负载均衡本身又成了单点。相比之下主从架构的优势在于记录只在主服务器上维护从服务器自动同步既保证了数据一致性又实现了查询层面的冗余。那分离解析为什么也要加进来因为很多场景下内外网需要不同的解析结果。举个典型例子公司内网有一台文件服务器内网访问走192.168段的地址外网员工访问要走公网地址。如果只有一套解析记录要么内网用户绕公网走一圈要么外网用户拿到内网地址根本连不上。分离解析就是解决这个问题的。我选择的是BIND这套DNS软件来实现原因很直接它是目前最成熟、文档最全的开源DNS服务器实现主从同步和视图功能都支持得很好社区里遇到问题也容易找到答案。虽然现在也有一些新的DNS服务端软件但BIND的稳定性和功能完整度在实战中还是最让人放心的。2.2 网络拓扑与角色划分在动手之前先把架构想清楚。我这套方案里涉及三台服务器虚拟机也行角色作用说明主DNS服务器维护所有区域记录响应从服务器同步请求同时配置内外网视图从DNS服务器自动从主服务器同步记录对外提供查询同样配置内外网视图测试客户端验证解析结果分别从内外网段发起查询主从之间的同步走的是DNS协议的区域传送机制。从服务器会定期检查主服务器上区域的SOA记录序列号如果发现序列号变大了就发起一次区域传送把最新记录拉过来。这个机制的好处是自动化你只需要在主服务器上改记录、升序列号从服务器就会自己更新。注意主从服务器的时间要同步否则SOA记录里的时间戳比对可能出问题。建议都配上时间同步服务。2.3 分离解析的实现原理分离解析在BIND里是通过view视图来实现的。每个view里可以定义一组区域并且通过match-clients参数指定这个视图匹配哪些来源IP。当DNS服务器收到查询请求时会按照view的定义顺序去匹配找到第一个匹配的view就用那个view里的区域来回答。这里有个关键点view的匹配顺序很重要。BIND会按配置文件里view出现的顺序依次匹配一旦匹配上就不再往下找。所以通常把范围小的view放在前面范围大的放在后面。比如内网视图匹配192.168.0.0/16外网视图匹配any那内网视图必须写在前面否则所有请求都会被外网视图截获。还有一个容易忽略的点一旦使用了view所有区域都必须定义在view里面不能再有裸露在view外面的zone配置。包括根区域、localhost这些都要分别写进每个view。这个坑我后面会详细说。3. 主从同步的核心配置细节3.1 主服务器区域配置要点主服务器的配置文件里每个需要同步的区域都要标明类型是master并且用allow-transfer指定允许哪些从服务器来拉取记录。我一般还会配上also-notify这样主服务器记录一变更就主动通知从服务器来同步不用等从服务器轮询。# 主服务器 named.conf 中某个区域的配置示例 zone example.internal IN { type master; file /etc/named/zones/example.internal.zone; allow-transfer { 192.168.1.12; }; also-notify { 192.168.1.12; }; notify yes; };这里allow-transfer里写的是从服务器的IP。生产环境建议用密钥认证TSIG而不是单纯靠IP限制因为IP是可以伪造的。不过实验环境用IP就够了先把流程跑通。区域文件本身有几个关键参数必须注意。SOA记录里的序列号是主从同步的核心每次修改记录后必须增大这个值否则从服务器不会触发同步。序列号的格式通常是YYYYMMDDnn比如2025011501表示2025年1月15日的第1次修改。这个格式的好处是直观一看就知道是什么时候改的。# 区域文件中的SOA记录示例 $TTL 86400 IN SOA ns1.example.internal. admin.example.internal. ( 2025011501 ; 序列号每次修改必须增大 3600 ; 刷新时间从服务器多久检查一次 1800 ; 重试时间同步失败后多久重试 604800 ; 过期时间从服务器多久没同步就放弃 86400 ; 最小TTL )刷新时间我设的是3600秒也就是从服务器每小时检查一次主服务器的序列号。如果对同步实时性要求高可以调小到300秒甚至更短。但调太小会增加主服务器负担需要权衡。重试时间1800秒是同步失败后的重试间隔这个不用设太短避免网络抖动时频繁重试。3.2 从服务器配置与同步验证从服务器的配置相对简单区域类型设为slave然后用masters指定主服务器地址。从服务器不需要自己维护区域文件BIND会自动把同步过来的记录存到file指定的路径。# 从服务器 named.conf 中对应区域的配置 zone example.internal IN { type slave; file /etc/named/slaves/example.internal.zone; masters { 192.168.1.11; }; };配置完成后重启BIND服务从服务器会自动向主服务器发起一次区域传送。你可以通过查看从服务器的日志来确认同步是否成功。同步成功后file指定的路径下会出现区域文件这个文件是BIND自动生成的不要手动去改。验证同步是否正常我常用的方法是两个一是看日志里有没有transfer of example.internal/IN from 192.168.1.11#53: Transfer completed这样的记录二是直接在从服务器上用dig命令查询一条记录看能不能返回正确结果。# 在从服务器上验证解析 dig localhost www.example.internal A short如果返回了正确的IP说明同步没问题。如果返回空或者报错就要去查日志排查了。实操心得从服务器的区域文件目录权限要设对BIND进程用户必须有写权限否则同步过来的文件存不下去。我见过好几次因为目录权限问题导致同步失败的案例。3.3 主从同步的常见故障点主从同步出问题排查思路其实很固定。先确认从服务器能不能连到主服务器的53端口再确认主服务器的allow-transfer有没有放行从服务器IP然后看序列号有没有增大最后看区域文件语法有没有错误。有一个特别隐蔽的坑区域文件里的域名结尾有没有加点。在BIND的区域文件里完全限定域名FQDN必须以点结尾比如ns1.example.internal.。如果忘了加点BIND会把它当成相对域名自动拼接上当前区域名变成ns1.example.internal.example.internal.这就错了。这种错误不会导致BIND启动失败但解析结果会不对排查起来很费时间。另一个常见问题是序列号忘了改。改完记录直接重启BIND主服务器自己解析是新记录但从服务器死活不同步。原因就是序列号没变从服务器认为数据没更新。养成习惯改完区域文件第一件事就是升序列号。4. 分离解析的配置与实操4.1 视图配置的整体结构分离解析的配置全部围绕view展开。一个典型的双视图配置结构是这样的# 内网视图匹配内网网段 view internal { match-clients { 192.168.0.0/16; 10.0.0.0/8; }; recursion yes; zone example.internal IN { type master; file /etc/named/zones/internal/example.internal.zone; }; zone . IN { type hint; file /etc/named/zones/internal/named.ca; }; zone localhost IN { type master; file /etc/named/zones/internal/localhost.zone; }; }; # 外网视图匹配所有其他来源 view external { match-clients { any; }; recursion no; zone example.internal IN { type master; file /etc/named/zones/external/example.internal.zone; }; zone . IN { type hint; file /etc/named/zones/external/named.ca; }; zone localhost IN { type master; file /etc/named/zones/external/localhost.zone; }; };注意每个view里面都要把根区域、localhost这些基础区域重新定义一遍。这就是前面提到的坑用了view之后所有zone都必须在view内部不能有遗漏。漏了哪个那个view里就查不到对应的解析。4.2 内外网区域文件的差异化配置两个视图里的区域文件内容可以完全不同。内网视图的区域文件里所有记录都指向内网地址外网视图的区域文件里记录指向公网地址。# 内网视图的区域文件片段 www IN A 192.168.1.100 mail IN A 192.168.1.101 ftp IN A 192.168.1.102 # 外网视图的区域文件片段 www IN A 203.0.113.10 mail IN A 203.0.113.11 ftp IN A 203.0.113.12这样配置之后内网用户查www.example.internal拿到的是192.168.1.100外网用户查同一个域名拿到的是203.0.113.10。两边互不干扰。注意外网视图里通常要关闭递归recursion no只回答自己权威区域的查询避免被利用做DNS放大攻击。内网视图可以开递归方便内网用户查询外部域名。4.3 视图匹配的验证方法配置完成后怎么验证视图有没有生效最直接的方法是从不同网段的机器上分别发起查询看返回结果是否不同。# 从内网机器查询 dig 192.168.1.11 www.example.internal A short # 预期返回 192.168.1.100 # 从外网机器查询 dig 203.0.113.11 www.example.internal A short # 预期返回 203.0.113.10如果两边返回的结果一样说明视图匹配有问题。排查方向先确认match-clients的网段写对了没有再确认view的顺序对不对最后看查询请求的源IP是不是真的匹配到了预期的view。BIND提供了一个很好用的调试手段在named.conf里加上querylog yes;然后看日志里每条查询匹配到了哪个view。日志里会明确写出类似client 192.168.1.50#12345: view internal: query: www.example.internal IN A这样的记录一眼就能看出匹配结果。4.4 主从加视图的联合配置把主从和视图结合起来的时候配置会稍微复杂一点。每个view里都要分别定义master和slave区域。主服务器的内网视图区域类型是master从服务器的内网视图区域类型是slave外网视图同理。# 主服务器内网视图中的区域 view internal { match-clients { 192.168.0.0/16; }; zone example.internal IN { type master; file /etc/named/zones/internal/example.internal.zone; allow-transfer { 192.168.1.12; }; also-notify { 192.168.1.12; }; }; }; # 从服务器内网视图中的区域 view internal { match-clients { 192.168.0.0/16; }; zone example.internal IN { type slave; file /etc/named/slaves/internal/example.internal.zone; masters { 192.168.1.11; }; }; };这里有个细节主从同步的区域传送请求本身也是从从服务器发起的源IP是从服务器的IP。如果从服务器的IP不在主服务器内网视图的match-clients范围内那同步请求可能会匹配到外网视图导致同步失败。所以要么确保从服务器IP在内网视图匹配范围内要么在主服务器的外网视图里也放行从服务器的同步请求。我一般会把从服务器的IP明确加到内网视图的match-clients里这样最稳妥。5. 完整实操流程与参数计算5.1 环境准备与软件安装假设我们有两台服务器主DNS的IP是192.168.1.11从DNS的IP是192.168.1.12。操作系统以常见的Linux发行版为例。# 两台服务器都安装BIND # 基于Debian系的系统 apt-get update apt-get install -y bind9 bind9utils bind9-doc # 基于RHEL系的系统 yum install -y bind bind-utils安装完成后先确认BIND服务能正常启动。默认配置下BIND只监听本地回环地址我们需要修改监听配置让它监听所有网卡或者指定网卡。# named.conf.options 中的监听配置 options { listen-on port 53 { any; }; listen-on-v6 { none; }; directory /var/named; dump-file /var/named/data/cache_dump.db; statistics-file /var/named/data/named_stats.txt; recursion yes; dnssec-validation no; };实验环境里我把dnssec-validation关掉了因为DNSSEC配置比较繁琐而且和主从同步、视图混在一起容易出问题。生产环境建议开启但那是另一个话题了。5.2 主服务器完整配置主服务器的named.conf整体结构如下# 全局选项 options { listen-on port 53 { any; }; directory /var/named; recursion yes; allow-query { any; }; dnssec-validation no; }; # 内网视图 view internal { match-clients { 192.168.0.0/16; 127.0.0.1; }; recursion yes; zone example.internal IN { type master; file /var/named/zones/internal/example.internal.zone; allow-transfer { 192.168.1.12; }; also-notify { 192.168.1.12; }; }; zone . IN { type hint; file /var/named/named.ca; }; zone localhost IN { type master; file /var/named/zones/internal/localhost.zone; }; zone 0.0.127.in-addr.arpa IN { type master; file /var/named/zones/internal/127.0.0.zone; }; }; # 外网视图 view external { match-clients { any; }; recursion no; zone example.internal IN { type master; file /var/named/zones/external/example.internal.zone; allow-transfer { 192.168.1.12; }; also-notify { 192.168.1.12; }; }; zone . IN { type hint; file /var/named/named.ca; }; zone localhost IN { type master; file /var/named/zones/external/localhost.zone; }; };内网视图的区域文件$TTL 86400 IN SOA ns1.example.internal. admin.example.internal. ( 2025011501 3600 1800 604800 86400 ) IN NS ns1.example.internal. IN NS ns2.example.internal. ns1 IN A 192.168.1.11 ns2 IN A 192.168.1.12 www IN A 192.168.1.100 mail IN A 192.168.1.101 ftp IN A 192.168.1.102外网视图的区域文件$TTL 86400 IN SOA ns1.example.internal. admin.example.internal. ( 2025011501 3600 1800 604800 86400 ) IN NS ns1.example.internal. IN NS ns2.example.internal. ns1 IN A 203.0.113.11 ns2 IN A 203.0.113.12 www IN A 203.0.113.10 mail IN A 203.0.113.11 ftp IN A 203.0.113.12注意两个视图的区域文件序列号要保持一致这样从服务器同步的时候不会混乱。实际上从服务器会分别同步两个视图的区域文件各自独立维护序列号。5.3 从服务器完整配置从服务器的配置结构和主服务器类似区别在于区域类型是slave并且用masters指定主服务器地址。# 从服务器 named.conf options { listen-on port 53 { any; }; directory /var/named; recursion yes; allow-query { any; }; dnssec-validation no; }; view internal { match-clients { 192.168.0.0/16; 127.0.0.1; }; recursion yes; zone example.internal IN { type slave; file /var/named/slaves/internal/example.internal.zone; masters { 192.168.1.11; }; }; zone . IN { type hint; file /var/named/named.ca; }; zone localhost IN { type master; file /var/named/zones/internal/localhost.zone; }; }; view external { match-clients { any; }; recursion no; zone example.internal IN { type slave; file /var/named/slaves/external/example.internal.zone; masters { 192.168.1.11; }; }; zone . IN { type hint; file /var/named/named.ca; }; zone localhost IN { type master; file /var/named/zones/external/localhost.zone; }; };从服务器的slaves目录需要提前创建好并且给BIND进程用户写权限。mkdir -p /var/named/slaves/internal mkdir -p /var/named/slaves/external chown -R named:named /var/named/slaves5.4 配置检查与启动BIND提供了一套配置检查工具在启动前一定要先检查配置文件语法。# 检查主配置文件语法 named-checkconf /etc/named.conf # 检查区域文件语法 named-checkzone example.internal /var/named/zones/internal/example.internal.zone named-checkzone example.internal /var/named/zones/external/example.internal.zonenamed-checkconf没有输出就表示语法没问题。named-checkzone会输出区域的序列号、记录数量等信息如果有语法错误会明确报出来。确认无误后启动服务# 启动BIND systemctl start named # 或者 systemctl start bind9 # 设置开机自启 systemctl enable named启动后查看日志确认没有报错journalctl -u named -f # 或者 tail -f /var/log/messages | grep named5.5 同步与解析验证主从都启动后在从服务器上观察同步日志。正常情况下会看到区域传送完成的记录。然后在从服务器上查询记录确认能返回正确结果。# 从服务器上查询内网视图记录 dig 192.168.1.12 www.example.internal A short # 预期返回 192.168.1.100 # 从服务器上查询外网视图记录需要从外网网段发起 dig 192.168.1.12 www.example.internal A short # 如果从内网发起返回的仍然是内网地址因为匹配到了内网视图这里有个验证上的细节从服务器本身在内网网段所以从从服务器上直接dig匹配的是内网视图。要验证外网视图需要从外网网段的机器发起查询或者用dig的-b参数指定源IP如果从服务器有多个网卡。# 指定源IP查询模拟外网请求 dig -b 203.0.113.100 192.168.1.12 www.example.internal A short5.6 序列号更新与同步触发实操日常维护中最常见的操作就是改记录。流程是改主服务器区域文件升序列号重载配置然后等从服务器自动同步。# 1. 编辑主服务器区域文件修改记录 vi /var/named/zones/internal/example.internal.zone # 2. 修改序列号比如从2025011501改成2025011502 # 3. 检查区域文件语法 named-checkzone example.internal /var/named/zones/internal/example.internal.zone # 4. 重载BIND配置 rndc reload # 5. 在从服务器上观察同步日志 tail -f /var/log/messages | grep transferrndc reload会重新加载所有区域如果只想重载单个区域可以用rndc reload example.internal。重载后主服务器会通过also-notify主动通知从服务器从服务器收到通知后发起区域传送。实操心得如果从服务器没有及时同步可以先手动在从服务器上执行rndc retransfer example.internal强制发起一次区域传送用来确认主从通道是否正常。如果手动能同步但自动不行那问题多半出在notify或者序列号上。6. 常见问题排查与避坑指南6.1 主从同步失败排查速查表现象可能原因排查方法从服务器日志无同步记录主服务器allow-transfer未放行检查主服务器allow-transfer配置同步报transfer failed网络不通或防火墙拦截telnet主服务器53端口测试同步成功但解析结果旧序列号未增大检查SOA序列号是否比从服务器大从服务器区域文件为空目录权限不足检查slaves目录属主和权限同步时好时坏网络抖动或主服务器负载高查看主服务器负载和网络质量6.2 视图匹配异常排查视图匹配出问题最典型的表现是内外网返回了相同的解析结果。排查步骤我一般按这个顺序来先确认请求源IP到底是多少。有时候你以为是从内网发起的请求实际上走了代理或者NAT源IP变成了外网地址。用dig加上subnet参数或者直接在服务器上抓包看源IP最准确。再确认view的顺序。BIND按顺序匹配第一个匹配上的view生效。如果外网视图写在了内网视图前面那所有请求都会被外网视图截获。这个错误很隐蔽因为配置文件语法检查不会报错。最后确认match-clients的网段写法。BIND支持CIDR格式比如192.168.0.0/16也支持单个IP和网段范围。写错了不会报错但匹配不上。6.3 那些年我踩过的坑坑一忘了在view里定义根区域。用了view之后根区域必须在每个view里重新定义。我一开始只在内网视图里定义了根区域外网视图忘了加结果外网用户查询外部域名全部失败。因为外网视图里没有根区域BIND不知道怎么去迭代查询。坑二从服务器的视图配置和主服务器不一致。主服务器内网视图匹配192.168.0.0/16从服务器写成了192.168.1.0/24。结果从服务器从192.168.2.0/24网段收到的查询匹配到了外网视图返回了公网地址。这种问题在测试阶段不容易发现因为测试机通常都在同一个网段。坑三区域文件里的域名忘了加点。这个前面提过但真的太常见了。ns1.example.internal和ns1.example.internal.在BIND眼里是两个完全不同的东西。前者会被拼接成ns1.example.internal.example.internal.。养成习惯区域文件里所有FQDN都加点结尾。坑四序列号改小了。有一次我手误把序列号从2025011502改成了2025011401从服务器认为主服务器的数据比自己的旧拒绝同步。BIND的序列号比较用的是RFC 1982定义的序列号算术不是简单的数值比较。改小了会导致同步逻辑混乱。所以改序列号只能往大了改。坑五防火墙只开了TCP 53没开UDP 53。DNS查询默认走UDP区域传送走TCP。如果只开了TCP普通查询会失败只开了UDP区域传送会失败。两个都要开。6.4 性能与安全优化建议主从架构下从服务器承担了大部分查询压力主服务器主要负责记录维护和同步。可以在从服务器上适当调大缓存减少对主服务器的查询依赖。安全方面allow-transfer一定要限制来源不要写成any。区域传送如果对所有人开放相当于把整个区域的记录都暴露了。生产环境建议用TSIG密钥做区域传送认证比单纯靠IP限制更安全。外网视图记得关闭递归。开放递归的DNS服务器容易被利用做DNS放大攻击。内网视图可以开递归方便内网用户。提示如果内网用户也需要查询外部域名可以在内网视图里配置forwarders指向上游DNS这样内网查询外部域名时由内网视图转发出去既方便又可控。7. 日常维护与扩展思路日常维护主要就是改记录、升序列号、重载配置这三步。我习惯在改之前先备份区域文件改完用named-checkzone检查语法确认无误再重载。重载后去从服务器确认同步完成整个流程走完才算结束。如果区域比较多手动改序列号容易忘可以写个脚本自动生成序列号。最简单的做法是用日期加序号每次改完自动递增。也可以用rndc的一些高级功能来辅助管理。扩展方面这套架构可以进一步做这些事加第二台从服务器做负载分担用TSIG密钥加固主从同步配置DNS查询日志分析了解哪些域名被查询得最多结合监控系统对DNS服务做健康检查主服务器故障时自动把从服务器提升为主。这些都是在现有基础上可以逐步完善的。我个人在实际操作中的体会是DNS这东西配置本身不复杂但细节特别多一个标点符号错了都可能导致解析异常。所以每次改完一定要验证主服务器验证完从服务器也要验证内网验证完外网也要验证。养成这套验证习惯之后基本就不会出大问题了。