返回资讯中心
外部精选
云计算
#边缘网络#Deep Dive#DNS#Kafka#Quicksilver

Cloudflare优化DNS构建:全量构建减少40%,峰值内存降85%

来源标题:How we halved DNS build delay and cut memory use by 85%

Cloudflare通过引入规划器选择最小安全构建方案,将DNS全量构建次数减少40%,峰值内存降低85%。此前区域设置变更会触发整个DNS区域重建,部分区域记录数已超2000万条。新方案只重建实际变更的部分,从而大幅缩短构建延迟。

Cloudflare BlogAlex Fattouche2026/11/2 · 今天30 分钟阅读

以下正文由 AI 翻译自 Cloudflare Blog(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

我上次在 2022 年写我们的 DNS 传播流水线时,Zone Builder 每秒大约接收 250 条 DNS 记录变更。如今它每秒接收约 700 条,接近原来的三倍,此外还有约 100 条其他事件,例如 zone 设置变更。然而,事件数量从来都不是真正的问题。问题在于其中一些事件需要做的那类工作,以及这类工作如何随着 zone 的规模增长而增长。现在有些 zone 已拥有超过 2000 万条记录,我们预计它们还会继续增长,达到数亿条。

每个事件都会启动一个计时器,用来衡量我们把该变更转化为权威 DNS 服务器可用的数据需要多长时间。我们把这部分工作称为一次 build。一次 build 会读取当前数据库状态,将其转换为边缘使用的格式,并把所有变更写入 Quicksilver。

2022 年时,按记录构建平均比全量构建快 150 倍。在一个拥有 100 万条记录的测试 zone 上,按记录构建快了 4,250 倍。我们在那篇文章结尾给出了显而易见的下一步:对 zone 设置做同样的事。

为什么 Zone Builder 不直接重建发生变更的那个设置?

它本应如此,而这也正是我们最终采用的做法。然而,证明何时这样做是安全的,占了大部分工作。记录事件能指明具体变更了什么。而 zone 事件可能来自许多 Cloudflare 产品,可能不会说明 zone 为什么发生变化,而且可能改动多个同名 zone 共享的数据。选择过多工作会浪费容量。选择过少则可能在边缘留下过期或不正确的 DNS 数据。

我们构建了一个有针对性的 build 规划器,它选择我们能证明安全的最小 build,还构建了一个新的调度器,把同一 zone 的工作合并起来。zone 设置变更不再重建拥有数千万条记录的 zone。两者共同将峰值内存降低了 85%,而额外腾出的余量让我们能够安全地缩短流水线一直需要的、用于保障弹性的等待时间,从而将从 DNS 变更到其数据写入 Quicksilver 的时间减半。

DNS 变更如何到达边缘

DNS zone 是某个域名的记录和设置的集合。Cloudflare 使用多阶段流水线,把对该 zone 的一次变更转化为可在我们全球网络中使用的数据。

  • 客户通过 DNS Records API 或仪表板更改 DNS 记录,或者另一个 Cloudflare 服务更改 zone 设置。
  • 该变更被存储在数据库中。
  • 数据库变更或另一个系统产生一条 Kafka 事件。
  • DNS Kafka Aggregator 合并来自多个 Kafka topic 的事件,并将它们发送给 Zone Builder。
  • Zone Builder 读取所需的 zone 数据,并将其转换为我们的权威 DNS 服务器能够理解的 key。
  • Zone Builder 将这些 key 与 Quicksilver(我们的分布式键值存储)进行比较。它只写入发生变化的内容,然后 Quicksilver 会将这些变更传播到 Cloudflare 的整个网络。

这是一个简化视图。Zone Builder 不会直接把数据库行复制到边缘。它会将记录与 zone 设置组合起来,应用 DNS 服务规则,并组织结果,使我们的权威 DNS 服务器能够快速读取。

为什么全量构建不再可扩展

最初的 Zone Builder 有一个有用的特性:它不需要知道 zone 为什么发生变化。它会根据当前数据库状态重建期望状态。

  • 读取当前 zone 及其设置。
  • 读取该 zone 中的每一条 DNS 记录。
  • 将这些记录转换为 Quicksilver key。
  • 将新的 key 与已存储的 key 进行比较。
  • 删除过期的 key,写入发生变化的 key,并更新 zone 元数据。

最终写入只包含发生变化的 key。然而,全量 build 仍然需要读取、编码并比较每一条记录,并且在这个过程中要把整个 zone 保存在内存中。对于小型 zone 来说这很便宜。对于我们最大的 zone(拥有数千万条记录)来说,zone 根本放不进内存。下面我会解释我们如何处理这种情况。

zone 设置让有针对性的处理变得更困难。Universal SSL 就是一个例子。启用或禁用它都会更新元数据,并且可能更改 zone apex(裸域名,例如 example.com)上的 Certification Authority Authorization(CAA)记录。CAA 记录用于告知证书颁发机构是否可以为此域名颁发证书。

全量 build 只需要知道 zone 发生了变化。而有针对性的 build 必须回答三个问题:

  • 哪些 DNS 名称可能发生变化?
  • 共享元数据或已发布 zone 的列表是否可能发生变化?
  • 必须从 Quicksilver 中移除哪些先前状态?

要安全地回答这些问题,意味着要解决五个问题,本文余下部分将依次讨论:

  • 事件必须说明发生了什么变化。
  • 每个变更都必须映射到仍然正确的最小 build。
  • build 必须考虑每个同名 zone 共享的数据。
  • 数据库可能在事件发生和 build 之间发生变化。
  • 合并工作不能丢失 Kafka 消息。

让事件说明发生了什么变化

我们修改了事件生产者,使其包含发生变化的 zone 字段和设置,并在可用时附带旧值和新值。然后,我们沿着生成 DNS 输出的代码追踪每个设置,并对其可能影响的内容进行分类。

并非每个事件都如此具体。有些数据库函数只更新时间戳,实际上是在说“这个 zone 的某些内容发生了变化”。有时这只是无害的簿记。有时这意味着其他地方的数据发生了变化,必须重新读取。

这些情况在事件中看起来完全相同。在我们能够识别调用方及其目的之前,仅更新时间戳的更新仍会触发全量 build。同样的规则也适用于缺失细节、未知事件,或任何可能影响 zone 中任意位置记录的变更。规划器绝不会把不确定性转化为更少的工作。

选择最小的安全 build

Quicksilver 输出中有两个部分与此相关。记录 key 包含某个 zone 和名称的 DNS 答案。一个较小的共享元数据 key 则告诉我们的权威 DNS 服务器如何使用该 zone,包括在哪里找到特殊记录,例如 nameserver 和 wildcard。有些事件只更改记录 key;另一些则必须同时更新两者。

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →