为什么时间同步是运维的隐形地基
很多故障事后复盘,根因都指向一个被忽视的维度——时钟。当集群里两台机器差了几秒甚至几分钟:
- 数据库主从复制:MySQL GTID、PostgreSQL 逻辑复制依赖事务时间戳与有序提交,时钟跳变会导致复制中断或数据回退。
- TLS 证书校验:证书有
notBefore/notAfter,客户端时间慢于签发时间会直接拒绝握手。 - 分布式锁 / 选举:基于租约(lease)的锁(如 etcd、Redis 锁)依赖本地时钟判断过期,时钟回拨会让锁提前失效。
- 日志与链路追踪:跨服务日志对不齐时间轴,排查问题如同拼一幅错位的拼图。
- 定时任务:
cron在错乱时钟下会重复执行或漏执行。
一句经验之谈:能用 NTP 的地方就别信任主机硬件时钟,尤其是虚拟机。
NTP 与 Chrony 怎么选
传统 ntpd 设计目标是"长期精密守时",对网络抖动敏感、冷启动同步慢(可能要几十分钟)。chronyd 是 NTP 协议的现代实现,特点:
| 维度 | ntpd | chronyd |
|---|---|---|
| 冷启动同步 | 慢(逐步校正) | 快(可步进校正) |
| 间歇网络 | 表现一般 | 擅长(断网后仍能靠漂移模型守时) |
| 虚拟机/笔记本 | 易因时钟跳变失步 | 对时钟跳变容忍度高 |
| 资源占用 | 较高 | 极低 |
| 命令交互 | ntpq |
chronyc |
结论:新系统一律用 Chrony,尤其是云上虚机、容器宿主、需要频繁启停的环境。
安装
# CentOS / RHEL / Rocky / Anolis
yum install -y chrony
# Ubuntu / Debian
apt-get install -y chrony
# 开机自启并启动
systemctl enable --now chronyd
架构:内网统一时间源
最佳实践不是让每台机器都直连公网 NTP,而是分层:
公网 NTP (ntp.aliyun.com / time.cloudflare.com / pool.ntp.org)
│
ntpserver01 (内网时间源, 同步公网, stratum 2)
ntpserver02 (冗余时间源)
│
┌────┴────┬────────┬────────┐
Web1 DB1 K8s节点 Redis
(全部指向内网 ntpserver01/02)
好处:内网机器不暴露公网、流量可控、时间源一致、断公网时内网仍自洽。
服务端配置(时间源节点)
/etc/chrony.conf:
# 上游公网时间源(可配多个,自动选优)
server ntp.aliyun.com iburst
server time.cloudflare.com iburst
server pool.ntp.org iburst
# 允许内网网段同步过来
allow 10.0.0.0/8
allow 192.168.0.0/16
# 即使上游不可达,也以本地时钟为权威(避免一直不收敛)
local stratum 10
# 记录时钟漂移,重启后快速恢复
driftfile /var/lib/chrony/drift
# 日志
log measurements statistics tracking
iburst 让初次同步时连发 8 个包,秒级完成校准;local stratum 10 是关键容错——上游全挂时,本机仍可对外提供"近似正确"的时间,避免内网集体失步。
客户端配置(业务机器)
server ntpserver01 iburst
server ntpserver02 iburst
# 不允许客户端把自己当服务器
# (不要写 local / allow)
driftfile /var/lib/chrony/drift
rtcsync # 把系统时间定期写回硬件时钟 RTC
rtcsync 等价于 hwclock --systohc 的自动版,保证重启后硬件时钟不至于漂太远。
日常排障命令(chronyc)
# 1. 跟踪状态:当前偏差、 stratum、已同步的上游
chronyc tracking
# Reference ID : C0A80101 (ntpserver01)
# Last offset : -0.000412 sec <- 当前偏差 0.4ms,健康
# Root delay : 0.001234 sec
# Leap status : Normal
# 2. 看用了哪些时间源、质量如何
chronyc sources -v
# ^* ntpserver01 ... * 表示当前选中并同步
# 3. 源评分(哪些被选、哪些被弃)
chronyc sourcestats -v
# 4. 强制立即同步(谨慎,会步进时间)
chronyc makestep
# 5. 看是否有大的时钟跳变记录
chronyc tracking | grep 'Leap status'
健康信号:Last offset 在毫秒级、stratum 合理(内网源通常 ≤ 3)、Leap status: Normal。
时钟漂移:为什么虚拟机最坑
物理机有稳定的晶振,漂移通常每天几毫秒。虚拟机时钟由宿主机内核模拟( kvm-clock / pvclock / TSC),问题在:
- 休眠 / 快照恢复 / 热迁移后,虚机时钟可能"冻结"再"瞬移",一次性偏掉几秒到几分钟。
- CPU 长期空闲时某些虚拟化时钟源会走慢(漏 tick)。
- 多 NUMA / 不同物理 CPU 的 TSC 不一致,跨核读取时间有偏差。
应对:
- 虚机内务必跑
chronyd,并启用rtcsync;别指望靠 RTC 校准。 - KVM 虚机优先用
kvm-clock(dmesg | grep clocksource确认),避免acpi_pm这类慢时钟源。 - 容器不要各自跑 chrony(共享宿主时钟),只需宿主校准;在容器里跑 NTP 反而会与宿主打架。
- 对时间极度敏感的服务(如金融交易),考虑
chronyd+ PTP(精密时间协议,亚微秒级)硬件方案。
闰秒(Leap Second):定时炸弹
UTC 偶尔插入闰秒,操作系统默认行为(ntp leap smear 或内核 11-minute mode)可能让时间"倒退 1 秒",触发:
- 依赖单调时钟的程序算出负间隔 → 崩溃或死循环。
- 数据库唯一时间索引出现重复/乱序。
现代做法:
- 闰秒 smear(推荐):让时间源在闰秒前后把 1 秒平滑分摊到若干小时(如 Google 的 20 小时线性 smear),对外不出现跳变。内网时间源配
leapsecmode slew+smoothtime。 - 应用层一律用单调时钟(
CLOCK_MONOTONIC/ Go 的time.Now().UnixNano做间隔测量,而非绝对时间做序)。
与 TLS / 数据库的硬性关联
- 证书校验窗口通常 ±5 分钟(有的 CA 更窄)。机器慢了 6 分钟,HTTPS 握手直接失败——表现为"时好时坏",极难定位。
- MySQL 启用了
ssl或binlog时间戳、MongoDBoplog时间排序,都依赖节点时钟一致。 - Kafka 不依赖时钟做顺序(靠 offset),但监控、审计日志仍依赖。
生产铁律:任何证书错误 / 复制中断的排查清单里,第一步先看时钟。
10 条避坑底线
- 新机器默认装 Chrony,别用 ntpd,尤其虚机/云主机。
- 内网统一时间源,业务机不要直连公网 NTP,减少暴露与抖动。
- 时间源至少 2 个且独立(不同公网源 + 不同内网节点),防单点。
- 时间源节点配
local stratum 10,上游全挂时内网仍自洽。 - 业务机配
rtcsync,定期把系统时间写回硬件时钟。 - 虚机务必跑 chronyd + 确认 kvm-clock 时钟源,快照/迁移后主动
chronyc makestep校准。 - 容器不要各自跑 NTP,只校准宿主;共享宿主时钟即可。
- 闰秒用 smear(平滑) 而非硬跳变,应用层用单调时钟做间隔测量。
- 监控
chronyc tracking的Last offset,超过阈值(如 > 100ms)告警。 - 证书失败 / 复制中断 / 诡异定时任务,第一反应查时钟,而不是先改业务代码。
时钟是分布式系统的"地基下的地基"——平时无感,一歪全塌。把 Chrony 配对、监控好偏移,能提前消灭一大类"查无实据"的灵异故障。