当一家公司的业务同时部署在电信和联通两个机房时,管理员常常会收到这样的反馈:联通用户访问域名很慢, tracert一看,流量居然绕到了电信机房再折回来。问题多半出在DNS上——无论谁来查询,服务器都返回同一个IP,跨网访问自然就慢了。解决这个问题的标准做法,就是让DNS根据用户的来源返回不同的解析结果,也就是所谓的智能DNS解析策略。

一、策略生效的前提:搞清楚DNS解析链路
在动手配置之前,必须先明白策略到底作用在链路的哪个环节。用户在浏览器输入域名后,本机首先查看缓存和hosts文件,找不到就问本地配置的递归DNS(比如运营商分配的DNS或公共的223.5.5.5)。递归DNS如果没有缓存,会从根服务器开始逐级迭代查询,最终问到域名所属的权威DNS服务器。
注意一个关键点:权威服务器看到的查询来源,并不是终端用户自己的IP,而是递归服务器的出口IP。比如广州电信用户配置了阿里公共DNS,那么权威DNS收到的请求源IP很可能属于阿里云的机房。这意味着基于来源的策略实际判断的是递归服务器的位置,所以专业方案都会维护一份“递归节点归属库”,把各大公共DNS的出口IP段映射回典型的服务区域,尽量还原用户的真实来源。
另外要留意EDNS Client Subnet(ECS)扩展。支持ECS的递归服务器会在查询报文中携带用户IP的前若干位,权威服务器据此可以精确判断用户所在的网络,主流公共DNS基本都已支持。如果自建权威服务,选BIND 9.11以上或支持ECS的软件会显著提升调度精度。
二、实现来源判断的三种主流手段
第一种是ACL加view的静态配置方式,以BIND为代表。管理员手工定义若干IP段列表,比如telecom对应电信地址段、unicom对应联通地址段,然后为每组地址段创建一个view,view内部各自维护同名域名的记录。查询到达时,服务器按顺序匹配view,命中哪个view就返回哪个view里的结果。这种方式实现简单、行为可控,缺点是地址段列表需要定期更新,国内运营商的地址段经常调整,一旦更新不及时就会出现调度错误。
第二种是基于GeoIP数据库的动态判断。服务启动时加载MaxMind GeoLite或国内维护的IP库(如ip2region、chunzhen的纯真库),查询时实时查库得出IP的运营商和省份,再套用策略规则。这种方式不用手工维护地址段,精度也更高,成本是引入了外部数据依赖,IP库的更新频率直接决定调度质量。很多商业智能DNS云服务(阿里云解析、DNSPod的线路分组)底层就是这种思路,只是把运维工作托管了。
第三种是策略路由式的分层调度,常用于CDN场景。权威DNS返回的不是最终业务IP,而是一个中间调度层(如LVS、Nginx集群或四层负载均衡)的地址,由调度层根据TCP连接的真实客户端IP做更精确的转发。这种方案绕开了ECS覆盖不全的问题,因为TCP握手时拿到的就是用户真实IP,代价是多了一跳转发。实际工程里经常是两种方式组合:DNS层做粗粒度的区域分流,调度层做细粒度的节点选择。
三、BIND的view配置实战
下面用一个典型例子演示BIND的多视图配置。假设域名example.ipipp.com在电信机房有服务器1.1.1.1,在联通机房有服务器2.2.2.2,默认线路指向3.3.3.3。
# named.conf 关键配置
acl "telecom" {
58.0.0.0/8;
61.128.0.0/10;
219.128.0.0/12;
};
acl "unicom" {
60.0.0.0/8;
61.128.192.0/18;
220.192.0.0/12;
};
view "tel_view" {
match-clients { "telecom"; };
zone "example.ipipp.com" IN {
type master;
file "example.tel.zone";
};
};
view "uni_view" {
match-clients { "unicom"; };
zone "example.ipipp.com" IN {
type master;
file "example.uni.zone";
};
};
view "default_view" {
match-clients { any; };
zone "example.ipipp.com" IN {
type master;
file "example.def.zone";
};
};# example.tel.zone 文件示例
$TTL 300
@ IN SOA ns1.example.ipipp.com. admin.example.ipipp.com. (
2024010101 ; 序列号,修改记录后必须加一
3600 ; 刷新
900 ; 重试
604800 ; 过期
300 ) ; 否定缓存
@ IN NS ns1.example.ipipp.com.
ns1 IN A 3.3.3.4
www IN A 1.1.1.1
@ IN A 1.1.1.1有几个容易踩坑的地方要特别说明。第一,view是按配置文件中的顺序匹配的,match-clients { any; }的默认视图必须放在最后,否则后面的视图永远不生效。第二,启用view之后,所有zone都必须放在某个view内部,包括localhost、 hints等内置区域,否则BIND启动会报错。第三,TTL不要设太长,策略调度的意义在于灵活切换,TTL设成600秒以上会让客户端缓存旧结果很久,一般建议60到300秒。
四、轻量场景与常见问题的处理
不是所有场景都需要BIND这套重量级方案。内网办公环境里,如果只想让不同网段解析到不同的内网服务器,dnsmasq就够用了。它支持按源地址返回不同结果,配置非常短:
# dnsmasq.conf # 来源为192.168.10.0/24的查询,走独立的解析配置 server=/intranet.ipipp.com/192.168.1.53 # 结合iptables做源地址判断时,可用 dnsmasq 的 address 配合多实例 # 实例一监听10.0.0.53,实例二监听10.0.1.53,各自返回不同记录 address=/app.ipipp.com/192.168.10.20
如果来源维度超过两三个,更省事的办法是跑多个dnsmasq实例,每个实例监听不同IP,上游交换机或防火墙按策略路由把不同网段的DNS请求导向不同实例,结构清晰且排查简单。
策略上线后的验证和排错也有一套方法。用dig @权威IP 域名 +subnet=用户IP/24可以模拟指定来源的ECS查询,直接观察策略是否命中;用dig +trace可以确认各级委托是否正常。最常见的故障是公共DNS用户调度错误,原因是权威侧没有ECS支持或地址库过期,表现为“本地用户被解析到外地节点”,这时应优先升级支持ECS的版本并更新地址库,而不是盲目调大调小TTL。
总结一下,基于用户来源的DNS解析策略本质上是在权威解析层引入“来源感知”能力:静态ACL适合线路固定、规模可控的场景,GeoIP动态判断适合追求精度的生产环境,DNS加调度层的组合则是CDN级别的标准做法。无论哪种方案,地址库的持续更新和ECS的兼容性都是决定实际效果的生命线,配置完成后务必用dig的subnet参数从多个来源维度回归验证。