首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Facebook如何构建满足规模需求的精确时间服务?

互联网上的设备要保持正常运行需要内置时钟运行精准,像 Facebook 这种规模体量的互联网公司对设备时钟的精准度要求会更高。本文介绍了 Facebook 在搭建自己的精确时间服务方面的探索实践,最终给我们提供了测量系统时间误差的方法以及对 chrony 的应用实践。

数十亿连接到互联网的设备几乎都有内置时钟,为了保证设备的正常运行,这些时钟必须保持精确。因为许多时钟的内部振荡器并不精准,所以可能会导致每天数秒的误差,这就需要定期校正。因为不准确的时间会导致一些问题,比如错过重要的提醒时间,甚至导致宇宙飞船发射失败。全世界的设备都依赖网络时间协议 (NTP),通过包交换、可变延迟的数据网络来保持与更精确时钟的同步。

随着 Facebook 基础设施的增长,系统中的时间精度变得越来越重要。我们需要知道数据中心中两个随机服务器之间的准确时间差,以便数据存储写入数据时不会打乱事务顺序。我们需要以亚毫秒级的精度同步多个数据中心的所有服务器。为此,我们测试了 chrony,这是一个功能丰富的现代 NTP 服务器实现。在测试期间,我们发现与以前使用的服务 ntpd 相比,chrony 具有更高的精确性和可伸缩性,这就让我们很安心的用 chrony 替换了基础设施中的 ntpd。Chrony 也是 Facebook 公共 NTP 服务 time.facebook.com 的基础。在这篇文章中,我们将分享将时间精度从 10 毫秒提高到 100 微秒所做的工作,以及我们如何在计时实验室验证这些结果的。

闰秒

在深入了解 NTP 服务细节之前,我们需要了解一种称为闰秒的现象。由于地球不规律的自转,我们有时需要增加或减少一秒,或 一闰秒。对于人类来说,增加或减少一秒在看钟表的时候几乎察觉不到。然而,这种事情发生在服务器上,就可能导致大量事务或事件丢失,甚至发生严重的软件故障。解决这一问题最通常的做法是 “抹去”闰秒,也就是说每过几个小时稍微修改一下时间。

规模建设 NTP 服务

Facebook 的 NTP 服务分为四个层次:

  • 第 0 层是一层拥有极其精确原子钟的卫星,这些原子钟来自全球导航卫星系统 (GNSS),如 GPS、GLONASS 和 Galileo。
  • 第 1 层是 Facebook 的原子钟,与 GNSS 同步。
  • 第 2 层是一个与层 1 设备同步的 NTP 服务器池。在这一层,闰秒开始出现。
  • 第 3 层是更大规模配置的服务器层。他们接收被处理过的时间,这一层就感知不到闰秒了。

在某些系统中,可能有多达 16 个层来分配工作,对层数的需求取决于系统规模和精度要求。

当我们开始构建 NTP 服务时,我们测试了以下后台程序使用的时间:

  1. Ntpd: 一个通用的后台程序,Ntpd 过去常常应用在大多数类 Unix 操作系统中。多年来,它一直是稳定的解决方案,现在的大多数计算机上依旧运行着 Ntpd。
  2. Chrony:一个比较新的后台程序,它具有丰富的特性,并且可以为 NTP 提供精确的时间同步。Chrony 还提供了可扩展控制协议,理论上可以将精度降低到纳秒。从资源消耗的角度来看,我们发现 ntpd 和 chrony 非常相似,chrony 消耗的内存甚至稍微少一些 (大约有 1 MiB 的差异)。

评估后台程序

无论系统是使用 ntpd 还是 chrony,每个系统都提供了一些评估的度量方法。通过使用这些后台程序的命令行工具可以进行评估。这些评估是基于一定的假设为前提的,例如:

  • 客户机和服务器之间的网络路径要有对称性。
  • 当时间戳被添加到 NTP 包并调用 send() 时,操作系统会立即发送它。
  • 振荡器的温度和输入电压是恒定的。

Ntpd 包含 ntpq 命令行工具,可以显示时间没有同步的状态:

代码语言:javascript
复制
[user@client ~]# ntpq -p
remote           refid      st t when poll reach   delay   offset  jitter
=========================================================================
+server1          .FB.       2 u  406 1024  377    0.029    0.149   0.035
+server2          .FB.       2 u  548 1024  377    0.078    0.035   0.083
*server3          .FB.       2 u  460 1024  377    0.049   -0.185   0.114

然而,这些数据可信么?如果 ntpd 报告时间差了 0.185 ms,是否准确?答案是否定的。服务器根据包中的多个时间戳估计偏移量,而实际值应该在一个 10 倍大的窗口内。换句话说,差 0.185 毫秒的结果意味着偏差可能在 +/-2 毫秒内 (总共 4 毫秒)。我们的测试表明,ntpd 的准确性一般在 10 毫秒以内。

我们有更高精度的技术要求。例如,多主数据库将微秒甚至纳秒的精度直接转换为理论吞吐量。另一个需要中等精度的示例是日志记录,为了在分布式系统的节点之间匹配日志,通常需要毫秒级的精度。

下面让我们看看如果用 chrony 替换 ntpd,效果会怎样:

代码语言:javascript
复制
[user@client ~]# chronyc sources -v
210 Number of sources = 19
  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current synced, '+' = combined , '-' = not combined,
| /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||      Reachability register (octal) -.           |  xxxx = adjusted offset,
||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
||                                \     |          |  zzzz = estimated error.
||                                 |    |           \
MS Name/IP address       Stratum Poll Reach LastRx Last sample
===============================================================================
^+ server1                     2   6   377     2    +10us[  +10us] +/-  481us
^- server2                     2   6   377     0  -4804us[-4804us] +/-   77ms
^* server3                     2   6   377    59    -42us[  -46us] +/-  312us
^+ server4                     3   6   377    60    +11ns[-3913ns] +/-  193us

注意最后三个数字。在最后,从右向左看:

  • 最后一个数字是估计误差。它的前缀是 +/-。它表示 chrony 的最大误差范围。有时是 10 毫秒,有时是 100 微秒 (100 倍的差异)。这是因为当 chrony 与另一个 chrony 同步时,使用 扩展的 NTP 协议,这极大地提高了精度。效果还不错。
  • 下一个是方括号中的数字。它显示了测量的偏移量。除了 server4(我们稍后将讨论这个),其它的我们看到也大约有 100 倍的差异。
  • 方括号左边的数字显示的是最初的测量值,经过调整后,允许自第一次测量以来的任何 slews 应用于本地时钟。同样,我们可以看到 ntpd 和 chrony 之间的差异也是 100 倍。

请看下图:

蓝色竖线表示 ntpd 被 chrony 替换的时间点。对于 ntpd,其范围是 +/-1.5 ms。用了 chrony 后,变动范围在微秒内。更重要的是,估计误差 (窗口) 下降到了 100 微秒的范围,这些是可以通过实验室测量来确认的 (下面会有更多的介绍)。但是,这些值是用后台程序估算的。实际上,实际时间可能完全不同。我们如何验证这些数字的准确性呢?

每秒脉冲数 (1PPS)

我们可以从原子钟中提取模拟信号 (实际上是来自层 1 设备的内部计时电路)。这个信号叫做 1PPS,意思是每秒 1 个脉冲;它在每一秒的开始都会在同轴电缆上产生一个脉冲。这是一种主流的、精确的同步方法。我们可以在 NTP 服务器上生成相同的脉冲,然后比较各个阶段的差异。这里有个困难点就是,并非所有服务器都支持 1PPS,因此需要专门的网络扩展卡。

我们第一次测试是手动完成的,使用了一台显示相移的示波器。

通过 10 分钟的测量,我们估算出 ntpd 的偏移约为 3.5 毫秒,有时会跳到 10 毫秒。

这个测试在 Facebook 大规模服务器上实现起来是极其困难并且也不切实际。更好的测试方法是将测试服务器的 1PPS 输出连接到层 1 设备本身的 1PPS 输入,并监控其差异。

这种方法可以实现上一个测试方法中所有优点,同时也不需要在数据中心使用示波器。如果使用该方法,我们能够在任何时间点进行测量并验证真实的 NTP 偏移量。

通过这两个测量结果,我们对实际的 NTP 偏移有了很好的了解,使用 1PPS 误差估计在纳秒内,这主要是由电缆长度导致的。

这些方法的不足之处是:

  • 电缆铺设:做这样的测试需要同轴电缆。在不同的数据中心进行测试需要更改数据中心的布局设计,这是比较困难的。
  • 定制硬件:不是所有网卡都有 1PPS 输出。这样的测试需要特殊的网卡和服务器。
  • 层 1 设备需要 1PPS 输入。
  • 服务器上需要安装 1PPS 软件:为了运行测试,我们必须在我们的测试服务器上安装 ntpd。这个后台程序可能会导致意外错误,因为它在用户空间中工作,并会被 Linux 调度。

专用测试设备

市场上有一些设备可以进行准确性测试。它们包含 GNSS 接收器,一个原子钟,多个 1PPS 和网络接口,还可以充当 NTP 客户端。这就让我们可以直接使用 NTP 协议执行相同的测试。接收到的 NTP 包由原子钟或 GNSS 接收器以非常精确的时间戳记录。

以下就是常见的装备:

上面的照片是我们最初临时用的测试设备。使用该设备进行测量有以下几个优点:

  • 它不需要额外的 1PPS 电缆。我们虽然仍然需要约束原子钟,但这可以通过使用 GNSS 或层 1 设备本身和一根短电缆来实现。
  • 它使用原子钟的数据来标记传输和接收的网络数据包。这使得操作系统的影响可以忽略不计,误差率只有几纳秒。
  • 它同时支持 NTP 和 PTP(精确时间协议)。
  • 该设备是便携式的,我们可以在不同地点之间移动它来执行测试。
  • 设备使用自己的数据点集格式,但它可以将数据导出到 CSV,也就是说我们可以按我们的数据标准导出数据。

NTPD 测量

结果与我们在后台程序评估和 1PPS 测量中看到的非常相似。首先,我们发现有一个 10 毫秒的下降,然后慢慢地修正为 +/-1 毫秒。有趣的是,这个 10 毫秒的下降是相当稳定的,并且在每次重启后都会出现。

Chrony 测量

这看起来与 chrony 后台程序的估算非常相似,它比 ntpd 好 10 到 100 倍。

Chrony 的硬件时间戳

Chrony 极大地改善了偏移量,这可以从 1PPS 的估算结果和实验室设备值中看出。不过 Chrony 还支持硬件时间戳。根据文档,Chrony 声称可以将精度提高到几百纳秒以内。

让我们来看看网络上 NTP 客户机 - 服务器通信中的 NTP 包结构。初始客户端的 NTP 包包含传输时间戳字段。

服务器填充其余的字段 (例如,接收时间戳),将客户机的传输时间戳保存为原始时间戳,并将其发送回客户机。

可以使用 tcpdump 验证此行为:

代码语言:javascript
复制
08:37:31.489921 IP6 (hlim 127, next-header UDP (17) payload length: 56) client.36915 > time1.facebook.com.ntp: [bad udp cksum 0xf5d2 -> 0x595e!] NTPv4, length 48
    Client, Leap indicator: clock unsynchronized (192), Stratum 0 (unspecified), poll 3 (8s), precision -6
    Root Delay: 1.000000, Root dispersion: 1.000000, Reference-ID: (unspec)
      Reference Timestamp:  0.000000000
      Originator Timestamp: 0.000000000
      Receive Timestamp:    0.000000000
      Transmit Timestamp:   3783170251.489887309 (2019/11/19 08:37:31)
        Originator - Receive Timestamp:  0.000000000
        Originator - Transmit Timestamp: 3783170251.489887309 (2019/11/19 08:37:31)
...
08:37:31.490923 IP6 (hlim 52, next-header UDP (17) payload length: 56) time1.facebook.com.ntp > server.36915: [udp sum ok] NTPv4, length 48
    Server, Leap indicator:  (0), Stratum 1 (primary reference), poll 3 (8s), precision -32
    Root Delay: 0.000000, Root dispersion: 0.000152, Reference-ID: FB
      Reference Timestamp:  3783169800.000000000 (2019/11/19 08:30:00)
      Originator Timestamp: 3783170251.489887309 (2019/11/19 08:37:31)
      Receive Timestamp:    3783170251.490613035 (2019/11/19 08:37:31)
      Transmit Timestamp:   3783170251.490631213 (2019/11/19 08:37:31)
        Originator - Receive Timestamp:  +0.000725725
        Originator - Transmit Timestamp: +0.000743903
...

客户端会获得数据包,附加另一个接收时间戳,并使用下面这个 NTP RFC #958 中的公式计算偏移量:

代码语言:javascript
复制
c = (t2 - t1 + t3 - t4)/2

然而,Linux 不是一个实时操作系统,它要运行的进程不止一个。因此,当传输时间戳被填满并调用 Write() 时,并不能保证立即通过网络发送数据:

另外,如果机器没有足够的流量,在发送 NTP 包之前可能需要一个 ARP 请求,ARP 请求是不可靠的,这会导致估算错误。但 chrony 支持硬件时间戳,使用这些方法,另一端的 chrony 可以高精度地确定数据包何时被网络接口处理。虽然从网卡戳记数据包到它实际离开数据包之间仍然有一个延迟,但小于 10ns。

还记得之前这个 chronyc sources 的输出形式吗?

代码语言:javascript
复制
^+ server4 2 6 377 60 +11ns[-3913ns] +/- 193us

Chrony 报告偏移量为 11 ns。这是因为启用了硬件时间戳的结果。然而,估计误差在几百微秒的范围内。虽然不是所有的网卡都支持硬件时间戳,但是随着它越来越受欢迎,网卡会慢慢支持这个特性。要验证对硬件时间戳的支持,只需运行 ethtool 检查,然后就可以看到硬件传输和硬件接收功能。

代码语言:javascript
复制
ethtool -T eth0
Time stamping parameters for eth0:
Capabilities:
hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)
hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)
hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)

通过使用实验室设备测试相同的硬件,我们就可以得到不同的结果了,ntpd 误差显示在 -10 毫秒和 3 毫秒之间 (13 毫秒的差异),chrony 误差显示在 -200 微秒和 200 微秒之间,而且启用硬件时间戳后,在大多数情况下 chrony 误差都显示在 -100 微秒和 100 微秒之间。这也证实了之前后台程序的估算可能是不准确的。

公共服务

现在,所有测量都在我们内部控制的数据中心网络中进行。下面,我们看看当我们在公共 NTP 服务或者其他一些著名的公共 NTP 服务提供商上测试时,情况是什么样的:

测量的结果好坏在很大程度上取决于网络路径以及网络连接的速度和质量。这些测量并没有受到 Facebook 网络的影响,针对的是来自不同地点、不同 Wi-Fi 和 LAN 网络的服务进行的多次测试。我们可以看到,我们的公共 NTP 服务不仅与其他主流的供应相比有竞争优势,而且在某些情况下,它的性能也更优。

公共 NTP 设计方案

在我们成功地将内部精度提升到亚毫秒级别之后,我们就启动了一个公共 NTP 服务,它可以通过设置 time.facebook.com 作为 NTP 服务器来使用。我们在我们的网络入网点(PoP)上运行这个公共 NTP 服务。为保障私隐,我们不会按 IP 地址为设备设定指纹。为了在即使请求网络路径失败的情况下也能提供更好的服务,我们在五个不同的地理位置都设置了端点。我们的五个端点如下:

  • time1.facebook.com
  • time2.facebook.com
  • time3.facebook.com
  • time4.facebook.com
  • time5.facebook.com

每个端点都在不同的地理位置上,这对可靠性和时间精度都有积极的影响。

time2.facebook.com 的网络路径如下:

time3.facebook.com 的网络路径如下:

抹掉闰秒

Ntpd 一直使用一种预发布的 闰秒列表 文件。有了这个文件,Ntpd 就可以提前进行时间修正,当闰秒实际发生时,也能保证时间是正确的。

Chrony 依赖于 GNSS 提前几小时发布的闰秒指示器。当闰秒事件在 UTC 00:00 实际发生时,在指定的时间段内它就会被抹去。有了 Facebook 公共 NTP 服务,我们决定采用更精确的方法,在事件结束后的大约 18 个小时内抹去闰秒。

因为涂抹操作要在层 2 的许多服务器上同时进行,所以保持操作尽可能的相似是很重要的。按照平滑的正弦曲线规则进行操作对应用程序来说是安全的选择。

经验总结

测量时间是非常具有挑战性的。ntpd 和 chrony 提供的估算方法在一定程度上都是正确的。一般来说,如果您想要监控真实的偏移量,我们建议使用 1PPS 或带有 GNSS 接收器和原子钟的外部设备。

在比较 ntpd 和 chrony 时,我们的测量结果表明 chrony 要精确得多,这就是我们将基础设施迁移到 chrony 并启动公共 NTP 服务的原因。结果证明,将精度从几十毫秒提高到几百微秒是值得的。

使用硬件时间戳可以进一步将精度提高两个数量级。尽管 NTP 已经有所改善,但它也有自己的局限性,因此对 PTP 的研究使用才有可能将您的精度提升到下一个级别。

英文原文:

https://engineering.fb.com/production-engineering/ntp-service/

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/gL6CrvIAW9UAaKO7hisB
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券