ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DNS负载均衡原理与TTL故障切换实战指南

DNS负载均衡原理与TTL故障切换实战指南

一位做海外业务的同事某天半夜找我,说某个机房整个挂掉,但用户流量还在源源不断往里打。我让他去查DNS解析记录,他看了一眼说“TTL设了两个小时,还没到期”。就这么一句话,故障影响被硬生生拉长了一个多小时。那天之后,我在所有涉及DNS负载均衡的方案里,都把TTL和故障切换时间当作和机器配置同等重要的参数来对待。

很多做后端、做运维的同学对DNS的理解停留在“域名转IP”这个层面,觉得它就是个寻址工具。但实际上,DNS是互联网最早也最普及的负载均衡手段,而且它工作在用户请求的“最前端”。在整套高可用架构里,DNS解析负载均衡决定了用户流量从哪个入口进入系统,这个决策做得对不对、快不快,直接影响后端所有服务的压力分布、故障恢复速度和最终的用户体验。

这篇文章我会从原理、策略、参数配置到故障排查,把我这些年实际踩坑和验证过的经验完整梳理一遍。不管你是刚接触这块的新人,还是已经在用DNS做全局调度但偶尔被“玄学问题”折磨的老手,这篇文章都能给你一些可以直接落地的参考。

1. 先搞清楚一个核心问题:DNS怎么就成了负载均衡器

1.1 一次日常解析流程里藏着哪些关键角色

要理解DNS负载均衡,得先把DNS解析的一次完整链路看清楚。用户输入一个域名,比如 www.example.com,浏览器不会直接去问“这个域名对应哪个IP”,而是先问配置好的递归解析器(通常是运营商提供的Local DNS)。递归解析器如果自己没有缓存,就会去问根域名服务器、顶级域名服务器,最后找到负责这个域名的权威DNS服务器,从那里拿到IP地址,再返回给用户浏览器。

这里面的关键角色有三个:用户端的递归解析器,负责替用户“跑腿”;权威DNS服务器,负责对外发布这个域名的最终解析结果;还有域名本身记录(A记录、AAAA记录、NS记录等),是负载均衡策略的载体。

DNS负载均衡的核心就藏在这条链路里:当权威DNS服务器上同一个域名配置了多个IP地址时,它通过一定的策略决定每次返回哪个IP给递归解析器。每有一个用户来查询,权威DNS就可以重新做一次调度决策。这就是最基础、也最直白的“按请求分配流量”。

1.2 为什么架构师们喜欢在DNS层做负载均衡

DNS负载均衡在整个高可用体系里占据的位置很特殊,因为它处在用户流量的入口,天然具备几个独特的优势。

第一,它对业务完全透明。用户访问域名时,解析动作发生在TCP连接建立之前,不需要修改应用代码,也不需要在客户端装任何东西,只要改DNS配置就能把流量从一批机器切到另一批机器。

第二,它的调度范围是全网的。无论是机房之间的容灾切换,还是把北京用户指到华北节点、把上海用户指到华东节点,因为每个用户发起解析请求时都会携带一定的网络信息,权威DNS可以根据这些信息做全局调度。这是Nginx、LVS这些四七层负载均衡器做不到的,它们只能调度进入同一入口的流量。

第三,它的成本极低。相比一套完整的负载均衡集群,DNS负载均衡只需要在权威DNS侧做配置,不需要额外的硬件设备,运维成本几乎可以忽略。

但反过来也要清醒认识到DNS负载均衡的短板:它只管“告诉用户去哪”,管不了用户是否真的去了,也管不了用户请求在到达目标IP之后发生了什么。它不感知后端服务的实时状态,除非你自己做了健康检查联动,否则它会把流量解析给一台已经宕机的服务器。它的调度粒度也不是真正的“每请求级”,而是受制于TTL缓存——这也导致它天然不适合做会话保持。

2. 把原理吃透:调度发生的位置和TTL的杠杆效应

2.1 权威DNS的应答过程才是“决策现场”

想明白DNS负载均衡为什么能调度流量,就要看权威DNS服务器收到查询请求后做了什么。通常,权威DNS拿到一个域名的查询时,先从自己的配置中找到该域名对应的记录列表,然后根据当前选择的调度策略,决定返回哪一个或哪几个IP地址。

这个“决定返回哪个IP”的过程,就是DNS负载均衡的决策现场。决策的依据可以有很多:单纯轮询就是轮流返回;加权轮询就是按权重配比概率返回;基于地理位置的调度则是解析请求来源,对不同区域的递归解析器返回不同的IP列表。

这里有一个容易混淆的概念:一个域名可以配置多条A记录,返回的IP不止一个。但浏览器通常只会使用返回列表中的第一个或随机一个IP发起连接。所以,如果你在DNS侧看到返回了多个IP,不能想当然地认为用户会“自动负载均衡”地使用这些IP。真正有效的调度,就是要尽量保证“不同用户拿到的首选IP不同”,或者在返回列表时调整顺序。

2.2 TTL是DNS负载均衡的“隐形开关”

TTL(Time To Live)决定了每条DNS记录可以被递归解析器和客户端缓存多久。它直接影响两件事:一是DNS负载均衡调度策略生效的速度,二是故障转移的响应时间。

举个例子,如果一条A记录的TTL是600秒,那么某个递归解析器拿到这条记录后,会在未来10分钟内直接使用缓存的IP,不再向权威DNS发送查询。也就是说,即使权威DNS在这期间修改了解析结果,那些已经缓存了旧记录的递归解析器依然会继续把用户引向旧IP。

TTL越短,调度策略越“灵敏”,用户越容易拿到最新结果。但TTL太短也有代价——递归解析器的缓存形同虚设,每个用户每次解析都要穿透到权威DNS,权威DNS的QPS和延迟压力直线上升,同时用户整体的解析耗时可能变长。

在我的实践里,做全局负载均衡的域名,TTL通常设置在30到120秒之间。如果涉及跨机房容灾切换,TTL再低我也不觉得心疼,因为相比“故障半小时无人察觉”,多承担一些解析量是完全值得的。

这里公式很简单:故障切换的最坏生效时间 = 健康检查发现故障耗时 + 权威DNS剔除故障IP耗时 + TTL剩余时间 + 递归解析器本地额外缓存时间。你会发现,前三项加起来往往不是大头,真正坑人的是很多递归解析器根本不管TTL,自行强制缓存更长时间。这个后面在排查章节细说。

2.3 解析链路中的EDNS参数能改变调度精度

要精确到“用户来自哪个城市”的调度,靠传统的DNS查询是不够的,因为权威DNS收到的是递归解析器的IP,而不是用户真实IP。如果某个地区所有用户都共用同一个运营商递归DNS,那么基于“来源IP”判断地理位置时,权威DNS看到的所有用户都来自一个IP,调度到同一个节点,就失去了负载均衡的意义。

这时候就要靠EDNS Client Subnet(简称ECS)这个扩展协议。它允许递归解析器在向权威DNS转发查询时,附带客户端的IP地址段信息。权威DNS可以基于这段真实客户端IP做更精细的地理区域判断和流量调度。不过要注意,ECS协议的普及程度并不均匀,不少公共DNS并不支持,这也会造成“配置了地理调度但效果不明显”的假象。

3. 核心策略与实践形态:从轮询到全局智能调度

3.1 轮询和加权轮询:最简单但千万别用错场景

最基础的DNS负载均衡策略就是轮询。假设 domain.com 配置了两条A记录指向IP-1和IP-2,权威DNS对第一个查询返回IP-1,对第二个查询返回IP-2,第三个又返回IP-1,以此类推。理想情况下,大约一半的用户解析到IP-1,另一半解析到IP-2,流量自然就被均分到两台服务器。

轮询唯一的配置成本就是“多写几条A记录”,所以很多小型应用、内部工具都会用这种方式做简单的横向扩容。但它的局限也很明显:一是完全不关心后端服务器的实际负载情况,如果其中一台机器性能差一半,它依然会分到同样多的流量;二是不具备故障转移能力,某台机器宕机,轮询还会把流量继续分给它,直到有人手动删除记录。

在轮询基础上发展出的加权轮询,就是给不同IP分配不同的权重,比如三台机器权重分别是3、2、1,那么查询返回这三个IP的概率就对应50%、33%、17%。这个策略适合后端机器规格不一的场景——大机器多给权重,小机器少给权重,先把“木桶效应”从容量规划上抹平。

需要注意的是,权重调整并不是实时的。递归解析器缓存了旧记录之后,权重变化要等缓存过期才会逐步生效。所以如果要做大范围权重调整,尽量选在流量低谷期,并且提前把TTL调低。

3.2 地理位置调度(GSLB):把用户分到最近的站点

如果业务覆盖多个区域,不同区域都有独立的服务集群,比如华东的机房服务华东用户、华南机房服务华南用户、海外机房服务海外用户,那么你需要的就不是简单的轮询了,而是基于来源区域的调度策略。

这就是GSLB(Global Server Load Balancing)的核心价值。权威DNS判断递归解析器的来源IP属于哪个区域,然后返回该区域内节点的IP。用户访问哪个机房,不再由轮询随机决定,而是由“离他最近”的原则决定。

这里有个非常关键的细节:判断“最近”的标准不是网络上的拓扑距离,而是递归解析器的IP地理位置。因为用户实际使用的是运营商Local DNS的出口IP,所以判断的是Local DNS的位置。如果一个city的用户恰好用了另一个省的递归DNS,那么他会被调度到离开他真实位置较远的节点。再加上ECS协议覆盖不全,地理调度会把大量用户都判定为同一个“来源”,导致某些区域节点压力失衡。这些问题在实际维护中几乎一定会遇到,需要结合监控数据不断校准策略。

3.3 动态负载策略:健康检查怎么跟DNS联动

最让人难受的DNS负载均衡体验就是“机器都挂了一半,解析结果还跟没事似的”。解决这个问题,需要让DNS能够感知后端状态,动态调整解析结果。

业内通常的做法是:在DNS服务商一侧配置主动健康检查,定期用HTTP、TCP或ICMP探测后端节点,一旦发现探测失败,平台自动把故障IP从解析结果中剔除,并且通知运维人员。当故障恢复、探测成功后,再把IP加回解析列表。

我自己的经验里,健康检查的探测间隔和失败判定阈值,直接决定了故障转移的时效。探测间隔设成30秒一次,连续3次失败才算宕机,那么从故障发生到平台判定故障大约要90秒以上,加上TTL缓存,用户的恢复等待时间很容易超过两三分钟。如果业务对容灾切换的期望是“分钟级”,这个探测参数还能接受;如果要做到“几十秒内切换”,就得把探测间隔压到5秒到10秒,失败阈值降到2次,同时把TTL控制在30秒甚至更低。

有一件事必须强调:健康检查只解决“从解析结果中剔除故障节点”的问题,不解决“把流量平滑切换到其他节点”的问题。如果整个集群所有节点都在同一个区域,而DNS解析到了几百公里外的备用节点,那用户的访问延迟会显著上升。所以动态调度策略一定要跟容量规划配合——确保每个区域的节点都有足够冗余,单节点故障不会引发“雪崩式跨区域切换”。

3.4 会话保持的无解难题与破解思路

讲原理时提到,DNS负载均衡是“首包路由”级别的调度,用户连上IP之后,后续所有请求都直接发给该IP,DNS已经不参与。这就带来会话保持问题:如果用户在网站登录之后,下一次DNS解析把请求解析到了另一台服务器,而session数据不存在那一台机器上,用户会被强制登出,甚至看到500错误。

理论上,DNS层几乎无法做到完美的会话保持,因为DNS不知道HTTP会话的存在,也不参与会话的建立与销毁。实际架构里解决这个问题通常从两个方向入手:

第一个方向是“下移”——把会话保持的责任交给应用层或负载均衡器。比如在应用层用集中式缓存(Redis)保存会话数据,所有后端节点共享,那么用户无论请求到哪台机器都能读到会话;或者在前置的Nginx/负载均衡器上做IP Hash,让同一个源IP始终转发到同一台后端。这种情况下,DNS只负责入口调度,会话保持完全在下一层解决。

第二个方向是“降低变更概率”——合理设置TTL,让用户在一段时间内解析结果保持稳定,同时尽量避免频繁调整解析策略。但这种方法治标不治本,只适合低要求场景。

我个人的建议很明确:永远不要把会话保持押在DNS层,它就不该管这事。DNS只管好入口调度就行,会话保持永远依赖更底部的会话存储方案。

4. 权威实践:从记录类型到参数配置的落地清单

4.1 记录类型选型:A记录、CNAME还是NS委托

在DNS负载均衡的实践中,首先要决策的就是用什么类型的记录承载调度逻辑。

最常用的是A记录。同一个域名下配置多个A记录,权威DNS根据策略返回其中一个或几个IP。优点是简洁直观,TTL单独可调,适用范围广。缺点是需要自己维护IP列表,变更时等待缓存过期。

CNAME记录本质上是一层间接寻址。把业务域名指向一个“调度域名”,再由调度域名配置A记录。这样的好处是把消费方与真实IP解耦,当你切换服务商或迁移IP段时,只需要修改调度域名的A记录,业务域名本身不动。缺点是CNAME链会增加一次额外的DNS查询,解析延迟会略微上升;并且CNAME不允许和同名的其他记录共存,如果这个域名还要用MX记录收信,就麻烦了。

NS委托的粒度更细。把某个子域或独立域名的解析权委托给专门的DNS服务商,由那套系统(通常自带负载均衡和健康检查)提供最终解析结果。很多GSLB产品就是用NS委托来接管解析权的。这种方式灵活性和功能最强,但代价是解析链路的掌控权分散,出现问题时排查链路更长。

做个表帮你直观比较一下:

记录类型调度灵活性变更成本解析延迟适用场景
多A记录中中低简单轮询、同区域多节点
CNAME链高低中服务商切换、多环境隔离
NS委托高低较高跨区域容灾、GSLB产品

4.2 关键参数配置:TTL、权重与故障切换阈值

无论你用的是哪家DNS服务商,这几个参数是整个DNS负载均衡实践的“命门”。

TTL的配置在前面已经详述过,这里给出一个经验基线:同时运行多个区域的业务域名,TTL设置在60秒到120秒之间比较平衡。做容灾演练或故障切换的前一天,把TTL临时降到30秒,切换完成后等稳定了再恢复正常值。这个操作在行业内已经是很常规的“降TTL变更流程”。

权重配置上要遵循“先小步试水分流,再大步调整到位”的原则。如果你要把20%流量导到新机房,先设一个很小的权重比如2%,观察新机房各项指标一两小时,确认没问题再调到20%。不要一上来配个大权重,新机房扛不住直接被打垮,那就是事故了。

健康检查的参数配置,需要结合后端业务的探测端口来设计。如果后端是HTTP服务,检查路径最好是一个轻量的、不带业务逻辑的接口,例如 /healthz。不要用首页地址做健康检查,因为首页通常包含大量动态渲染逻辑,慢、不稳定,容易产生“误杀”。检查超时建议2秒,重试2到3次再判定故障,避免偶发网络抖动触发切换。

4.3 权威DNS与递归DNS交互时容易被忽略的点

配置了权威DNS之后,我们往往默认“用户来查,权威就答,天经地义”。但现实中递归解析器的行为五花八门,有几个点需要特别留心。

第一个是“负缓存”。当权威DNS临时无法访问,递归解析器不仅会缓存查询失败的记录,还会整整缓存一段时间“这个域名查不到”的负面结果,这个时间通常是SOA记录里的Minimum字段。如果你发现域名解析“恢复不了”,查一下SOA的TTL是不是设得特别长。

第二个是“递归解析器强制缓存”。部分运营商和公共递归DNS对TTL有“下限”和“上限”的约束。例如某运营商强制缓存最短10分钟,那么你就算把TTL配成1秒,也架不住它在本地缓存10分钟。监控一下权威DNS的请求量和解析日志,如果显著低于业务预期,很可能就是这类强制缓存导致的。

第三个是“记录轮转顺序”。有些浏览器不会使用DNS返回的第一个IP,而是自己随机选一个。这会导致在多A记录场景下,确实有一部分用户会命中非首选IP,增加后端请求的不确定性。所以尽量让所有IP在后端都是可用的,不要依赖“首选IP就是主节点”这种假设。

4.4 实战验证:一次典型的多机房DNS调度配置

说了这么多理论和参数,我用一个典型的配置过程做完整演示。假设业务目前有两个机房:机房A和机房B,域名是 www.sample-case.com,A机房承载主要流量,B机房作为容灾和分担节点。

第一步,确认权威DNS服务商支持GSLB或健康检查策略。如果不支持,只能退回到多A记录加手动切换的方案。

第二步,添加两条A记录:IP-1(机房A)权重设为3,IP-2(机房B)权重设为1。初始TTL设置120秒,健康检查选择TCP探测8080端口,间隔设为10秒。

第三步,把域名切换分为两步:先在DNS配置里把TTL降为30秒,等待上一轮TTL过期,再正式添加A记录并启动健康检查。如果服务商支持“平滑发布”,这一步会更快。

第四步,观察权威DNS的解析日志和两个机房的入口流量监控,确认流量配比接近3:1。如果B机房流量明显偏低,大概率是运营商递归缓存导致旧记录未过期,等几分钟再看。如果偏高,检查是否监控统计口径包含了健康检查产生的探测流量。

第五步,等稳定运行半天后,把TTL恢复到120秒,完成配置。

这套流程我在不同服务商下都跑过,核心逻辑一致,只是控制台的操作位置有差别。

5. 常见问题与排查实录

5.1 故障转移为什么迟迟不生效

这类问题几乎成了DNS负载均衡的“招牌坑”。现象是A机房宕机,DNS也配置了健康检查,但用户仍然访问到A机房的IP很久。

首先要依次检查健康检查的状态。在DNS服务商的控制台里,看故障节点是否已经被标记为“DOWN”。如果标记了但用户还能访问,问题大概率出在缓存层——递归解析器的缓存里还留着旧IP。你可以在受影响网络环境里执行“nslookup”或“dig @指定递归DNS域名”去核对解析结果,看看返回的还是不是故障IP。

如果健康检查状态是“UP”而业务实际已经故障,那可能指健康检查探测的是某个不依赖业务逻辑的端口或路径,探测成功但业务已经不对外提供服务。这也是健康检查设计容易忽略的:检查的应该是“业务是否可用”,而不只是“进程是否还活着”。把检查路径换成业务核心接口,比如一个会访问数据库的状态接口,可以显著减少这类“假活”情况。

如果健康检查“DOWN”了,但权威DNS没有剔除记录,要看一下服务商的处理策略。有些平台只在解析时过滤故障IP,而不是直接删除记录,这会导致某些存量递归DNS拿到旧记录后继续转发请求。很多公共DNS系统为了节省查询开销,会主动缓存解析响应中的多个IP,完全忽略TTL,这种场景你只能把故障IP从配权中彻底摘除,才能强制那些不守规矩的递归解析器放弃旧记录。

5.2 配置没问题但流量倾斜严重

另一个高频问题:同一个域名下多个IP,配置了相同的权重,但实际流量严重偏向某个节点,另外的节点几乎没有请求。

第一嫌疑依然是递归解析器的缓存集中。如果大部分用户默认使用某个大运营商DNS,该DNS处理公共域名的缓存策略往往比较激进,导致一段时间内所有用户从缓存中拿到同一个IP,等缓存过期后又全场切换。

第二嫌疑是ECS和地域路由导致的不均匀调度。你以为用户分散在各个区域,实际权威DNS根据递归DNS的IP把所有用户都归类到一个大区域,从而都调度到同一个节点。

第三嫌疑埋在更底层:某个机房到用户网络链路的稳定性与速度差异。即便DNS进行了合理的分配,用户在连接阶段感受到了明显延迟或失败,会自动重试新的DNS查询,导致那些“较远”的节点用新一轮查询替换掉刚分配的IP,形成“偏斜”。

处理这类问题的思路不是一味调权重,而是要追根因:先看权威DNS记录的查询量分析,再对比各节点入口带宽和连接数,确认是不是DNS层的问题,还是传输链路干扰了用户行为。

5.3 监控与验证方案:怎么确认真实调度效果

DNS负载均衡的验证一直比较“玄学”,因为你看不到实时的“调度指令”,只能从侧面观察。我从实操中总结了一套“三层验证法”。

第一层,验证解析结果本身。用dig命令指定不同区域的公共DNS,反复查询同一个域名,对比返回的IP列表是否均匀分布。重点关注TCP/UDP 53端口响应时间和返回顺序。

第二层,验证后端流量。从后端入口处按分钟维度统计各节点连接数、带宽和请求量,与权威DNS返回的IP配比做拟合。只要趋势接近权重大方向,就可以认为调度是有效的。

第三层,验证真实用户体验。用多点拨测等手段,从不同运营商、不同地区发起真实访问,记录解析到的IP和访问耗时,侧证调度结果与目标策略是否吻合。

拨测时我习惯挑选一些冷门公共DNS验证ECS效果。比如用几个不同区域的公共DNS,看能否解析出对应区域的IP;再比较真实用户IP信息与拨测结果是否一致,这能帮你摸清服务商的ECS覆盖情况。

5.4 与HTTP重定向/CDN等服务的选择边界

DNS负载均衡并不是唯一的“入口调度”手段。很多业务纠结于是用DNS解析指向多IP,还是用HTTP 302重定向,或者直接上CDN。我的建议是先分清各自的边界。

DNS负载均衡的优点是对所有应用透明,无论是HTTP、数据库还是长连接,都能用。缺点是调度粒度只到IP级别,不能根据URI、路径或协议类型做细粒度分发。如果你的业务只关心“这台服务器还是那台服务器”,它完全够用。

HTTP 302重定向可以把调度做到“路径级”或“用户级”精细度,但代价是每次重定向都要多一次HTTPS往返,用户感知延迟明显上升。它更适合用来做灰度发布、A/B测试,不适合做大规模基础流量分发。

CDN实际上多采用DNS解析加边缘缓存的组合方案,在全球范围通过GSLB实现就近接入,同时具备缓存、加速、屏蔽、回源优化等能力,是面向用户场景最全面的方案。但它毕竟是有成本的服务,而且CDN只擅长处理HTTP协议,如果是自建TCP私有协议,还是得回到DNS自建方案里。

在我接触过的多数业务里,性价比最高的组合是:DNS负载均衡负责全局入口调度,自建网关或负载均衡器负责区域内高可用与流量分发,应用层缓存负责会话保持,三层各司其职。把DNS调度做成了,就不会出现“所有流量都集中到一台机器”这种事故;把它做精,就能在一个机房故障时做到分钟级切换,用户无感知或只有轻微卡顿。

6. 结尾:多说几句备用的经验

做了这么多年稳定性保障工作,DNS负载均衡在我眼里从来不是一道配置题,而是一道持续调优题。很多团队上线时把权重、TTL配好就再也不看了,等到出问题才想改,结果被缓存死死卡住,只能眼睁睁看着流量灌进故障机房。比较务实的做法是每季度做一次容灾演练,把每个节点的健康检查触发阈值、TTL参数、权重配置一起验证一遍,顺手记录下故障切换的真实耗时。

另外再建议维护一份“DNS调度速查表”,记录域名对应哪些节点、权重比例、健康检查路径、TTL大小和最近的变更记录。业务本身迭代快,很多当年合理的调度策略等到流量结构变化后就不再适用,数据表反而是最容易被遗忘却最有价值的资产。

DNS解析负载均衡这件事,做好了用户无感,做不好线上事故。希望这篇文章能帮你把决策链路、参数杠杆和缓存陷阱同时重视起来。如果你在落地过程中遇到什么奇怪现象,欢迎带着具体现象和数据来聊,有些问题真的是环境差异造成的,需要现场看才能定位。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进