Chrony 时间同步与时钟漂移排查实战 - 夜莺博客

Chrony 时间同步与时钟漂移排查实战

很多"查无实据"的灵异故障,复盘到最后根因都是同一个被忽视的维度——时钟。数据库主从复制中断、TLS 证书握手失败、分布式锁提前失效、日志时间轴对不齐,背后常常只是两台机器的时间差了几秒甚至几分钟。本文基于一线运维实战,整理 Chrony/NTP 的选型对比、配置要点、管理命令,以及时钟漂移引发典型故障的排查清单。

NTP 与 Chrony 怎么选

传统 ntpd 设计目标是"长期精密守时",冷启动同步慢(可能几十分钟),对网络抖动敏感;chronyd 是 NTP 协议的现代实现,冷启动可步进校正、断网后靠漂移模型守时、资源占用极低,对虚拟机时钟跳变容忍度高。新部署环境(虚拟化、云、容器)优先选 Chrony;老系统或需要兼容老旧设备时继续用 NTP。

Chrony 配置与常用命令

# /etc/chrony.conf
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst
allow 192.168.1.0/24          # 作为服务器时允许客户端同步
local stratum 10              # 上游全挂时内网仍自洽
makestep 1.0 3                # 偏差超1秒时前3次同步直接步进
rtcsync                       # 定期写回硬件时钟

systemctl start chronyd
systemctl enable chronyd

chronyc sources -v            # 查看时间源状态
chronyc sourcestats           # 同步统计
chronyc tracking              # 查看系统时间偏移(Last offset)

典型故障与排查

  • 时间同步不生效:先 systemctl status chronyd,再 netstat -unlp | grep :123 检查端口,然后 chronyc sources -v 确认上游可达。最常见原因是防火墙挡了 UDP 123 或上游服务器不可达。
  • 时间偏移过大:用 makestep 步进校正;偏差极大时先手动同步一次再启动服务。
  • 虚拟机时间跳跃:宿主机负载过高导致,确认 kvm-clock 时钟源,快照/迁移后主动 chronyc makestep 校准。
  • 容器时间不一致:容器不要各自跑 NTP(共享宿主时钟),只需校准宿主机。

闰秒与避坑底线

闰秒硬跳变会让依赖单调时钟的程序算出负间隔甚至崩溃。现代做法是 闰秒 smear——把 1 秒平滑分摊到数小时(如 Google 的 20 小时线性 smear)。内网时间源配 leapsecmode slew + smoothtime。应用层一律用单调时钟(CLOCK_MONOTONIC)做间隔测量。

监控 chronyc tracking 的 Last offset,超过阈值(如 100ms)告警。生产铁律:任何证书错误、复制中断、诡异定时任务的排查清单里,第一步先看时钟。更多 Linux 排障方法见 Linux 性能排障指南 与 Linux 运维高阶命令实战。

时间的三个层次:为什么"看起来对"其实不对

系统里同时存在三套"时间"。挂钟时间(CLOCK_REALTIME)可被 NTP 调整、可以向后跳;单调时间(CLOCK_MONOTONIC)只增不减、不受校时影响;硬件时间戳(TSC / CLOCK_MONOTONIC_RAW)精度最高但受 CPU 频率伸缩影响。多数"灵异"故障来自把三者混用。工程铁律:测量间隔用单调时钟,记录事件用挂钟时间——前者保证不出现负值,后者保证多机可关联。

Chrony 的工作原理:漂移模型与频率补偿

chronyd 与老 ntpd 的核心差别在于它把时钟当成一个"可建模的系统":它估算本地晶振相对真实时间的频率偏差(frequency),据此持续微调(slew)而不是跳变,从而在长时间断网后仍能靠模型守时。它同时维护根延迟(root delay)与根离散度(root dispersion),误差边界随距离上游的跳数累积。

  • 步进(step):一次性把时间跳到正确值,会打断依赖单调性的程序,因此只在启动阶段或偏差极大时使用。
  • 微调(slew):以略快或略慢于真实时间的速率慢慢追上,安全但慢,适合运行中的系统。
  • 漂移模型:长期观测后,Chrony 知道本机晶振偏快还是偏慢,断网时也能"估算"出正确时间。
# 主配置 /etc/chrony.conf 的关键项
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst
allow 192.168.1.0/24
local stratum 10              # 上游全挂时内网仍自洽
makestep 1.0 3                # 前3次同步偏差超1秒时直接步进
rtcsync                       # 定期写回硬件时钟
driftfile /var/lib/chrony/drift

# 关键状态命令
chronyc tracking              # 系统时间偏移、频率、根延迟/离散
chronyc sources -v            # 每个时间源的状态(^* 已选,^? 不可达)
chronyc sourcestats -v        # 每个源的偏移估计与斜率
chronyc activity              # 有效源数量
chronyc ntpdata 1.2.3.4       # 单个源的分层与往返时延

chronyc tracking 字段逐项解读

字段 含义 异常信号
Reference ID 当前选中的上游源 长期不变才正常,频繁跳变说明源不稳
System time 系统时间相对真实时间的偏移 持续增大 = 未同步或被别的进程改钟
Last offset 上一次同步测得的偏移 超过阈值(如 100ms)应告警
RMS offset 偏移的滑动均方根 波动大说明网络抖动严重
Frequency 本机晶振偏快/偏慢的 ppm 值 数值异常大 = 晶振老化或虚拟化时钟源差
Skew 频率估计的误差 很大说明样本不足,尚未收敛
Root delay 到参考时钟的累计往返时延 很大说明上游太远或多跳
Root dispersion 累计的误差上界 持续增长说明一直在"猜"时间
Leap status 闰秒状态 Normal 之外要关注

实测口径:内网同机房 NTP 同步通常能把 Last offset 稳定在 1 毫秒以内;跨地域公网源在几十毫秒量级属正常;虚拟机建议整机偏移控制在 10 毫秒以内,超过 100 毫秒就应视为异常。

把内网 NTP 服务器做对:分层时间源设计

让每台机器各自去公网找时间源,等于把规模和可用性都交给了公网。正确做法是分层:

  • Stratum 1 边界服务器:机房内 1-2 台机器用 GPS/GNSS 或 PTP 作为上游,直接提供参考时间,不直接对公网。
  • Stratum 2 内网服务器:多台,从 Stratum 1 同步,通过 allow 向客户端提供时间。
  • 客户端:只指向内网服务器,配置 iburst,并保留 local stratum 10 作为兜底。
# 内网服务器侧
server gps-clock.internal iburst trust
allow 10.0.0.0/8
local stratum 10
# 客户端侧
server ntp1.internal iburst
server ntp2.internal iburst
maxpoll 10                    # 最多每1024秒同步一次,减小开销

分层的好处:公网抖动被边界服务器吸收,内网各机器保持一致,且全网断网时仍能靠本地模型维持相对同步。PTP 与 NTP 的取舍详见PTP 与 NTP 对比。

NTP 安全:NTS、restrict 与放大攻击

NTP 是经典的 UDP 放大攻击放大器,一台开放查询的服务器可能被用来向受害者反射流量。生产环境必须收口:

# 只允许内网客户端,拒绝一切查询
restrict default kod nomodify notrap nopeer noquery
restrict 127.0.0.1
restrict 10.0.0.0/8 nomodify notrap
# 严格限制可访问的服务
# 从防火墙侧只放行可信源到 UDP 123

如果上游支持,启用 NTS(Network Time Security)为时间同步加上认证,防止中间人伪造时间——时间一旦被伪造,TLS 证书校验、Kerberos 票据与 TOTP 验证码会一并旁路,这正是攻击者偏爱打时间的原因。

虚拟机、容器与云环境的时钟陷阱

  • 时钟源:确认内核用的是 kvm-clock 或 xen 半虚拟化时钟,而不是默认的 TSC;dmesg | grep -i clocksource 可查。
  • CPU 偷取:宿主机超卖导致 steal 飙高时,虚拟机里的时钟会漂移;监控 top 的 st 列或 Prometheus 的 node_cpu_seconds_total{mode="steal"}。
  • 快照与迁移:虚拟机从快照恢复或热迁移后,时间是"冻结"再"跳变"的,恢复后应主动执行 chronyc makestep。
  • 容器:容器共享宿主内核时钟,不要在容器里各自跑 NTPD/chronyd,要校准的是宿主机;Kubernetes 节点时间不一致会让 etcd 租约与 token 校验出问题。

一线故障案例库

  • 数据库主从复制中断:MySQL 半同步依赖时间戳判断超时,两台机器相差数秒就会让从库误判主库已死,频繁切换。
  • TLS 证书握手失败:客户端时间落后于证书的 notBefore,或超前于 notAfter,都会报"证书无效/未生效"。证书链排障可参考OpenSSL 证书链排查。
  • 分布式锁提前失效:Redis/etcd 的锁基于 TTL,锁持有者与检查者时钟不一致会导致同一临界区被两个进程同时进入。
  • Kerberos 认证失败:票据有效期默认 5 分钟,时间偏差超过它直接拒绝,报 KRB_AP_ERR_SKEW。
  • 定时任务重复/漏跑:cron 与 systemd timer 依赖挂钟时间,校时跳变会让任务漏触发或瞬时双触发。
  • 日志时间轴错乱:多机日志聚合后顺序无法还原,根因常是某一台机器偏移,集中日志架构本身也要保证各节点时间对齐。

监控与告警落地

被动等故障上门不如主动监控偏移。node_exporter 暴露的 node_timex_offset_seconds 与 node_timex_sync_status 足以覆盖绝大多数场景:

# 偏移超过 0.1 秒即告警
abs(node_timex_offset_seconds) > 0.1

# 时间未同步(0 = 未同步)
node_timex_sync_status == 0

# 与上游失联:长时间没有有效源
chronyc activity        # 由 ssc 脚本采集,无源时告警

除了指标,还要监控"时间源本身":SLA 类业务应对 Stratum 1 服务器的 GPS 锁定状态单独告警,避免所有客户端一致地跑偏却没有任何信号。

排障速查清单

  1. systemctl status chronyd —— 服务是否在跑、是否报错。
  2. ss -unlp | grep :123 —— 是否在监听 UDP 123,防火墙是否放行。
  3. chronyc sources -v —— 上游是否可达、是否被选中(^*)。
  4. chronyc tracking —— Last offset、频率、根延迟是否正常。
  5. timedatectl —— 时区、NTP 同步开关、内核是否"等待同步"。
  6. 核对是否还有别的进程在改钟(老 ntpd 与 chronyd 同时运行是常见冲突)。

原文链接:https://www.scrcx.cn/posts/chrony-time-sync