一场“DNS 挂了”的事故,是怎么把 CDN 一起拖下水的#

如果你经历过一次真实的 DNS 故障,就会发现它的表象非常有迷惑性:源站明明还活着,负载均衡也没全部坏,甚至某些地区的用户还能短暂打开页面,但外界感受到的却像是“整个网站突然消失了”。

这里最容易产生的误解是:CDN 在前面,所以只要 CDN 健康,网站就应该继续可用。 但很多情况下并非如此。对绝大多数网站来说,DNS 不是一张静态电话簿,而是把用户请求导向某个 CDN 边缘入口的控制平面。DNS 出问题,用户甚至拿不到“该连哪个边缘节点”的答案,于是 CDN、源站和负载均衡会一起表现成不可达。RFC 1034RFC 1035 对 DNS 的权威数据模型做了最基础的定义,NIST SP 800-81r3 则直接把 DNS 基础设施视为网络架构里的关键依赖。

这篇文章就从这个灾难视角出发:先解释 CDN 和 DNS 到底是怎么配合工作的,再推演几类最常见的失效路径,最后收束到“怎样让网站在 DNS 级事故里争取更强韧性”的设计原则。

CDN 和 DNS 是怎么配合工作的?#

当用户在浏览器里输入一个域名时,请求并不会直接飞到某个 CDN 边缘节点,而是先经过本地 stub resolver,再进入递归解析器,然后由递归解析器去查询权威 DNS。RFC 1034RFC 1035 定义了这条查询链路的基本角色和数据结构。

接入 CDN 的常见做法是:域名所有者把自己的记录通过 CNAME 指向 CDN 提供的入口域,或者直接把 NS 记录委托给 CDN 的权威 DNS。这样会出现两段解析:第一段由域名所有者的权威 DNS 返回 CNAME 或直接委托;第二段由 CDN 的 authoritative DNS 根据实时策略返回当前最合适的边缘入口。Amazon Route 53 的 latency-based routingCloudflare 的 traffic steering 都在第二段做同一件事:基于延迟、健康状态、地理位置或端点可用性,让不同用户解析到不同答案。

有些 CDN 还会参考 EDNS Client Subnet(RFC 7871) 这类扩展,把“发起查询的用户大概来自哪里”带进解析决策里。这样一来,DNS 就不只是“把域名翻译成 IP”,而是在 TCP/TLS 连接建立之前,先完成了一次全局导流。

flowchart LR U[Browser] --> S[Stub Resolver] S --> R[Recursive Resolver] R --> A["Authoritative DNS\n(域名所有者)"] A -->|CNAME 委托| C["CDN Authoritative DNS\n/ Steering Logic"] C --> P[Nearest PoP / Edge] P -->|Cache Miss| O[Origin]

PoP、Anycast、Traffic Steering 分别负责什么#

这几个词经常被混着用,但它们解决的其实不是同一个问题。

PoP(Point of Presence) 是边缘接入与缓存节点——把内容放到离用户更近的数据中心,降低访问延迟并减少回源。AWS CloudFront 用 edge locations 描述同一类基础设施。

Anycast 是路由入口机制。它的核心不是“缓存”,而是让多个边缘入口共享同一组 IP 前缀,由底层路由把流量吸到拓扑上更近的入口。AWS Global Accelerator 明确描述了它的静态 IP 以 anycast 方式从边缘网络对外提供,这使得同一个 IP 能在全球多个位置同时生效,路由器通常会选择路径更优的入口。

Traffic Steering 是控制平面策略。Cloudflare 在 traffic steering 文档 里把它拆成 health check、global steering 和 local steering 三层;IBM NS1 在 Filter Chain 介绍 里则把它解释成按顺序串联的多个过滤器——健康优先、地域优先、成本优先可以自由组合叠加。

一个更容易记住的关系是:PoP 是节点,Anycast 是入口,Traffic Steering 是决策。 只有把三者拆开,后面讨论故障域时才不会把“边缘节点故障”“路由收敛”和“DNS 控制面失效”混成一件事。

故障推演一:权威 DNS 宕机时,哪些用户会先受影响#

权威 DNS 宕机的第一反应通常不是“全球同时一起黑屏”,而是先出现一种很诡异的分裂:有些用户还在正常访问,有些用户刚开始报错,还有些用户一会儿能开、一会儿不行。

原因很简单:DNS 天生带缓存。递归解析器和客户端都可能暂时持有之前的解析结果。这里有两个关键机制:

  1. TTL 到期前:递归解析器会继续使用缓存中的旧答案,很多用户可能暂时无感知。
  2. TTL 到期后(一个缓冲机制):根据 RFC 8767 (Serving Stale Data),现代递归解析器(如 Cloudflare 1.1.1.1 或 Google 8.8.8.8)如果发现权威 DNS 无法响应,会尝试继续返回已过期的缓存记录(Stale Record),只要该记录还没被彻底剔除。这能为网站在 DNS 级故障里争取一段缓冲时间。

这也是为什么你在事故里经常会看到“北京有人能打开,新加坡全部失败”的现象。问题不只是用户位置不同,而是不同递归解析器(ISP DNS vs Public DNS)手里持有的缓存状态和对 Stale Data 的支持策略不同。

故障推演二:为什么多 CDN 也不一定能救你#

很多团队第一次做高可用时会直觉地说:那我上多 CDN 不就好了?这个思路只对了一半。

如果你的多个 CDN 最终还是由同一个 DNS 控制平面统一导流,那么用户能否进入这套“多数据面”架构,仍然取决于那一个权威 DNS 或 steering 服务是否可用。换句话说,多 CDN 解决的是分发层单点,不自动解决解析层单点。

这也是为什么 Route 53 的 DNS failover、Cloudflare 的 traffic steering 和 IBM NS1 的 Filter Chain 虽然都在做“导流”,但它们属于控制平面能力,不等于边缘节点本身的冗余。

故障推演三:为什么服务修好了,用户却不一定马上恢复#

真正折磨人的常常不是“服务挂了”,而是“明明修好了,怎么还有人继续报错”。这时你面对的通常不是新的故障,而是缓存收敛。

RFC 2308 专门讨论了 Negative Caching(负向缓存):当解析器请求权威 DNS 得到一次失败结果(如 SERVFAIL 或被拒绝)后,它会记住这个失败。

  • 谁决定失败缓存多久? 答案是该域名 SOA 记录中的 MINIMUM 字段(通常也是 SOA 自身的 TTL)。如果你这个值设得很大(比如 1 小时),那么即使你修复了权威 DNS,全球解析器可能还要等 1 小时才会重新发起查询。

因此,故障恢复的时间(MTTR)不只取决于你的修复速度,还取决于你在故障发生前对 SOA 记录的配置。TTL 短只能让旧答案更快过期,但只有 SOA 设得合理,才能让故障后的“负向结果”更快消散。

sequenceDiagram participant U as User participant R as Recursive Resolver participant A as Authoritative DNS U->>R: Query example.com R->>A: Resolve example.com A-->>R: Healthy answer R-->>U: Cached answer Note over A: Authoritative DNS outage U->>R: Query after TTL expiry R->>A: Resolve again (Authoritative is DOWN) A--xR: Timeout / Failure alt Resolver supports RFC 8767 (Serve Stale) R-->>U: Return EXPIRED cached answer (Survival) else No Serve Stale support R-->>U: SERVFAIL (Outage) end Note over A: DNS service restored U->>R: Query during convergence alt Negative cache still valid (SOA Minimum) R-->>U: Cached failure (Still failing) else Negative cache expired R->>A: Resolve again A-->>R: Healthy answer R-->>U: Fresh answer end

让网站在 DNS 灾难里争取可用性的设计原则#

更稳妥的做法通常不是依赖某个单点上的“神奇功能”,而是减少共享故障域

1. DNS 与 CDN 解耦:避免单点控制面#

如果同一家服务既承担权威 DNS,又承担主要 CDN 分发和流量调度,那么一旦该厂商的控制面(Control Plane)出问题,解析和分发会同步停摆。

  • 一种常见做法:使用独立的 DNS 厂商(如 Route 53 或 NS1)作为流量指挥官,将流量导向不同的 CDN 供应商(如 CloudFront 或 Akamai)。NIST SP 800-81r3 也强调了这种角色隔离的重要性。

2. 实施 Multi-DNS:主从同步 vs API 双活#

TTL 短只能让旧答案更快过期,但不能解决“没答案可给”的问题。更高的韧性通常来自两个不同的 DNS 平台。

  • 主从模式 (Primary-Secondary):使用 RFC 1995 (IXFR) 协议,当你在主 DNS 修改记录时,次席 DNS 会自动同步。这是最标准、最省心的做法。
  • 双主模式 (Dual-Primary):通过 API 或 Terraform 同时在两个 DNS 厂商处维护同一份数据。优点是能绕开某些厂商不支持 AXFR/IXFR 的限制,缺点是需要保证同步脚本的幂等性。

3. 利用 Stale Cache 与应急降级#

对只读内容较多的网站来说,配置好 stale-if-error 或边缘持久化(Edge Stale)能显著降低 DNS 波动的影响。同时,准备一个托管在完全不同 TLD(如 .net 之于 .com)下的 Break-glass Hostname,能让你在主域名彻底“失联”时仍有入口进行紧急维护。

flowchart TB U[Users] D1[Authoritative DNS A] D2[Authoritative DNS B] P[DNS Response Logic / Steering Policy] C1[CDN Provider A] C2[CDN Provider B] O[Origin / Load Balancer] B[Break-glass Hostname] U --> D1 U --> D2 D1 -.->|shares steering policy| P D2 -.->|shares steering policy| P %% P represents the answer-selection logic used by each authoritative DNS when composing responses; it is a control-plane policy, not a separate network hop. P -->|answers with IP of| C1 P -->|answers with IP of| C2 C1 --> O C2 --> O U -. emergency access .-> B B --> O

厂商能力对比:你应该关注哪个维度?#

从故障域(Failure Domain)角度看,这几家厂商的侧重点并不相同:

厂商 核心定位 故障域特征 建议用法
Cloudflare DNS+CDN 一体化 如果解析与 CDN 都压在同一平台,控制面故障半径会更集中。 适合追求集成度和抗 D 能力的场景,但需补齐备份解析路径。
Route 53 纯控制面/流量管理 全球 Anycast 分布,与 CDN 解耦,常被拿来做独立控制面。 适合作为 Multi-CDN 架构的“指挥塔”。
IBM NS1 智能流量编排 (Filter Chain) 逻辑比较灵活,侧重于复杂的调度决策。 适合需要按成本、负载、地域进行精细化切流量的系统。
Akamai 网络植入级 CDN 节点分布较广,但 DNS 往往作为其服务套件的一部分。 适合对分发性能要求较高的大型内容分发场景。

先问你的故障域在哪里,再决定该用哪一层能力。 如果你的 DNS 和 CDN 已经来自同一家,那么第一步不是换产品,而是配置一个独立的、不依赖该厂商控制面的应急路径。

可执行 Checklist:提升你的网站韧性#

个人站点 / 小团队#

  • 多 TLD 容灾:准备一个独立后缀(如 .net.io)的应急域名,直连源站 IP 或简单负载均衡。
  • 检查 SOA 记录:确保你的 SOA MINIMUM 字段在合理范围内(建议 5-15 分钟),避免负向缓存导致的“持久化故障”。
  • Public DNS 验证:在本地修改 Hosts 模拟权威 DNS 宕机,观察 8.8.8.8 或 1.1.1.1 是否能触发 Serve Stale 机制。

中型系统 / 企业应用#

  • 实施 Multi-DNS:配置第二权威 DNS,并验证主从同步(AXFR/IXFR)或双主 API 同步的正确性。
  • 配置健康检查驱动的 Failover:利用 Route 53 或 NS1 的健康检查逻辑,在主要 CDN 入口异常时自动切向备份入口。
  • 故障半径评估:画出你的请求链路图,圈出所有共享同一个厂商控制面的环节,并针对性地引入冗余。

高可用生产系统#

  • Multi-DNS + Multi-CDN 组合拳:在控制面和数据面同时消除单点。
  • 自动化回切演练:将 DNS 故障切换纳入常规 Chaos Engineering 流程,而不是依赖人工在深夜紧急修改 NS 记录。
  • 监控负向结果:监控 503/SERVFAIL 的占比,并将其作为自动切换控制面逻辑的输入信号。

总结#

回到最初那个问题:为什么 DNS 一挂,CDN、源站和负载均衡看起来会一起死?答案不是“它们真的同时坏了”,而是用户根本拿不到进入这套系统的“通行证”。

更稳妥的做法往往不是依赖某个厂商的“黑科技”,而是架构上的职责隔离

  1. 拆分职责:把 DNS 解析(指挥官)、CDN 分发(先遣队)和源站服务(大本营)的职责边界划清。
  2. 消除共享单点:拒绝 DNS 和 CDN 盲目捆绑,优先保证控制面(Control Plane)的多样性。
  3. 利用协议特性:配置好 SOA 字段和 Stale Cache 策略,让基础设施具备“自我呼吸”的能力。

如果只能记住一句话,我更倾向于把它写成:短 TTL 更像调参,控制面去单点化往往才是 Survivability 设计里更关键的部分。

参考资料#