服务器时间差 5 分钟,我的 HTTPS 证书就报错了
这是一个真实发生过的事故。某台 VPS 因为宿主机迁移,BIOS 时间偏了,重启后系统时钟直接跑到了未来。结果是:
- Let's Encrypt 证书校验失败,报 "certificate is not yet valid",站点全红。
- MySQL 主从复制报错,因为 binlog 时间戳乱序。
- 所有日志时间戳全错,出问题时根本没法按时间线排查。
- 搜索爬虫和一部分 API 调用出现签名校验失败。
而时钟偏移这件事,在不重启的机器上会悄悄发生——虚拟化平台的时钟漂移、CPU 节能降频、负载过高导致时钟中断丢失,都会让时钟越走越偏。你可能几个月都没发现,直到某个依赖精确时间的组件(证书、签名、数据库)突然炸掉。
这篇文章讲清楚:现代 Linux 上该用 chrony 还是 ntpd,怎么判断时钟是否可信,以及那些真正会咬人的配置细节。
一、先确认你的服务器时间到底准不准
在折腾任何配置之前,先做一次体检。这几个命令按顺序执行:
# 1. 看当前时间与时区
timedatectl
# 2. 看时间同步服务的实际状态
timedatectl status
# 3. 直接和公共时间源对比,看偏差多少秒
chronyc tracking # chrony 环境
ntpq -p # ntpd 环境
# 4. 用 HTTP Date 头做粗略交叉验证
date -u; curl -sI https://www.google.com | grep -i '^date:'timedatectl 的输出里,重点看三行:
Local time: Sun 2026-09-21 08:30:12 UTC
Universal time: Sun 2026-09-21 08:30:12 UTC
RTC time: Sun 2026-09-21 08:30:11 UTC
Time zone: UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: noSystem clock synchronized: yes 必须是 yes。如果是 no,说明时钟没有和任何可信时间源同步,你看到的系统时间完全来自硬件,可能已经偏了几十秒甚至更多。这是第一优先要修的问题。
RTC in local TZ: no 也必须是 no。硬件时钟应该存 UTC,如果它存的是本地时间,会在夏令时切换或跨时区迁移时产生一小时的跳变。上一条命令里如果看到 yes,用 timedatectl set-local-rtc 0 改回来。
chronyc tracking 的输出更精确,重点字段是 System time:
$ chronyc tracking
Reference ID : A1B2C3D4 (ntp1.example.com)
Stratum : 2
Ref time (UTC) : Sun Sep 21 08:29:58 2026
System time : 0.000023456 seconds fast of NTP time
Last offset : +0.000031278 seconds
RMS offset : 0.000089123 seconds
Frequency : 12.345 ppm slow
Residual freq : +0.002 ppm
Skew : 0.015 ppm
Root delay : 0.021345678 seconds
Root dispersion : 0.000123456 seconds
Update interval : 64.2 seconds
Leap status : Normal判读方法:
System time就是你相对参考源的真实偏差。单位是秒。小于 0.001 秒(1 毫秒)算优秀,0.001~0.05 秒算正常,超过 1 秒要警觉,超过 10 秒基本肯定有硬件或虚拟化问题。Leap status必须是Normal。如果是Not synchronised,说明根本没同步上,检查网络到上游 123/UDP 是否通。Stratum是层级,1 是原子钟直接源,2~4 正常。如果 Stratum 显示 16 或很大的数字,说明上游不可用。Skew是频率估计的误差。持续很高(>100ppm)说明你的时钟晶振不稳,虚拟机上常见。
二、chrony vs ntpd:为什么现代系统应该选 chrony
Debian 9+、Ubuntu 18.04+、RHEL/CentOS 7+ 默认都换成了 chrony。它相比老 ntpd 有实质优势,不是口味问题:
- 收敛快得多。 ntpd 需要几小时甚至一天才能把时钟调准;chrony 通常几分钟内就能收敛到毫秒级。对经常重启的 VPS 和容器来说这是决定性的。
- 能处理断续网络。 ntpd 假设网络持续可用,网络断一段时间后要重新慢慢收敛。chrony 会记录时钟频率,即使长时间断网,也能靠历史数据把时间维持得相当准。
- 频率校正更平滑。 chrony 用更先进的算法估算晶振频率,避免"调一下、过冲、再调回来"的振荡。
- 默认就是好配置。 开箱配置基本可用,不像 ntpd 需要仔细挑选 server 列表。
所以除非你在跑必须用 ntpd 的遗留环境,统一选 chrony。
安装与基础配置
# Debian / Ubuntu
apt update && apt install -y chrony
# 确认服务起来并开机自启
systemctl enable --now chrony
systemctl status chrony --no-pager配置文件在 /etc/chrony/chrony.conf(RHEL 系是 /etc/chrony.conf)。一份适合国内或国际 VPS 的配置:
# 上游时间源。国内机器优先用国内源,延迟低、更稳定
pool ntp.aliyun.com iburst
pool ntp1.aliyun.com iburst
pool time.cloudflare.com iburst
pool pool.ntp.org iburst
# 首次同步后允许时钟跳变(限前几次)
makestep 1.0 3
# 保存时钟频率数据,重启后快速恢复精度
driftfile /var/lib/chrony/chrony.drift
# 开启实时时钟(RTC)的定期同步
rtcsync
# 只允许本机客户端,不给外部提供时间服务
allow 127.0.0.1
# 日志
logdir /var/log/chrony几个参数必须解释清楚,因为配错会导致同步失败或时间跳变:
iburst:启动时快速发包(间隔 2 秒连发 4 次),而不是常规的 64 秒一次。没这个参数,初始同步要等几分钟。这是最容易漏的一个。makestep 1.0 3:前 3 次同步时,如果偏差超过 1 秒,直接跳变校正;之后改为缓慢调整(slew)。这个默认行为非常合理——启动时立刻纠正,运行时避免时间倒退影响应用。rtcsync:让内核定期把系统时间写回硬件时钟。没有它,重启后会从偏掉的 RTC 起步,再等待同步。开了它,重启后起点就基本是准的。pool而不是server:pool 会自动解析出一个域名下的多个 IP 并动态选择质量最好的几个。域名只有一个 IP 时才用 server。
关于 makestep 的运行时行为要小心
如果运行过程中时钟偏差突然超过阈值(比如宿主机被暂停又恢复,时间一次性跳了几十秒),chrony 默认会以极慢的速度(slew,约每秒最多 0.005 秒)逐步纠正——纠正 30 秒需要约 100 分钟。这段时间内时间戳仍然是不准的。
如果你跑的是数据库主从、或者签名的 API,这种慢纠正是灾难。可以这样处理:
# 运行时也允许跳变,但阈值设得较高,避免频繁跳
makestep 1000 1或者需要立即纠正时手动执行:
chronyc makestep对普通个人站来说,默认配置足够了。只有跑 MySQL 主从、Kafka、证书签发服务这类对时序敏感的组件,才需要精心设计这一块。
三、容器与虚拟机的时间陷阱
这是现代部署里最大的坑,而且绝大多数教程不讲。
1. Docker 容器默认继承宿主机时钟
容器没有独立的内核时钟,它读的就是宿主机的系统时钟。所以给容器装 NTP 是没用的(而且通常做不到,因为容器没有 CAP_SYS_TIME 权限)。正确的做法只有一个:在宿主机上把时间同步做好。
# 宿主机上检查
timedatectl
chronyc tracking
# 容器内验证(应该和宿主机一致)
docker run --rm debian:12 date -u
date -u如果容器时间和宿主机不一致,那是别的问题(比如镜像里写了假时间,或时区配置不同)。注意时间戳和时区显示是两回事:容器可能 UTC 正确但显示成别的时区。
2. 时区配置:UTC 还是本地时区
强烈建议服务器统一用 UTC。理由:
- 日志跨机器对比时不会错乱(不同机器设不同时区是排查噩梦)。
- 夏令时切换不会导致时间跳变和定时任务重复/缺失执行。
- 数据库、日志、监控全用 UTC,只在展示层做时区转换。
timedatectl set-timezone UTC
# 容器里如果需要特定时区
docker run -e TZ=Asia/Shanghai --rm debian:12 date但有一个具体场景必须注意:如果你的 crontab 是按"北京时间 8 点"写的,服务器改成 UTC 后会变成凌晨 0 点执行。 改时区前一定要检查所有 crontab 并换算。
3. 虚拟化平台的时钟漂移
KVM/Xen/VMware 上的虚拟机,在宿主机高负载时时间会明显滞后。除了跑 NTP,还应该确认装了虚拟化增强工具:
# KVM/Hyper-V 检查 kvm-clock
dmesg | grep -i -E 'kvm|clocksource'
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
# 应该优先使用 kvm-clock / tsc,而不是 hpet / acpi_pm
如果 current_clocksource 是 hpet 或 acpi_pm,那你的时钟精度天生就差。可以尝试切换到 tsc:
echo tsc > /sys/devices/system/clocksource/clocksource0/current_clocksource
# 持久化需在 grub 加 clocksource=tsc注意:只在确认 CPU 支持 invariant TSC 且已装 virtio 驱动时改,否则可能反而更不稳。改完要观察 24 小时的 chronyc tracking。
四、把时间纳入监控
最好的做法不是"出问题时去查",而是让偏移在超阈值时主动告警。chrony 自带的 chronyc 输出很容易接进监控系统。
用一行 shell 做简单监控
#!/bin/bash
# /usr/local/bin/check-clock.sh
# 返回非零表示时钟偏移超阈值
MAX_OFFSET=0.5 # 秒
offset=$(chronyc tracking | awk '/System time/ {print $4}')
# 取出绝对值
abs=$(echo "$offset" | sed 's/^-//')
if command -v bc >/dev/null 2>&1; then
over=$(echo "$abs > $MAX_OFFSET" | bc)
else
over=$(awk -v a="$abs" -v m="$MAX_OFFSET" 'BEGIN{print (a>m)?1:0}')
fi
if [ "$over" = "1" ]; then
echo "CLOCK OFFSET DANGER: ${offset}s (limit ${MAX_OFFSET}s)"
exit 1
fi
echo "clock ok: ${offset}s"
exit 0挂到 crontab 里每小时跑一次,异常时由已有告警通道发出:
0 * * * * /usr/local/bin/check-clock.sh || /usr/local/bin/alert.sh "时钟偏移异常"Prometheus 环境
用 node_exporter 的 --collector.ntp(或 chrony_exporter),关注这两个指标:
node_ntp_offset_seconds # 偏移量,告警阈值建议 0.5
node_ntp_sanity # 必须为 1,表示同步正常运行规则示例:
alert: ClockOffsetTooLarge
expr: abs(node_ntp_offset_seconds) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "服务器时钟偏移超过 0.5 秒"五、故障速查表
把常见症状和对应的动作列在一起,出问题时直接对号入座:
System clock synchronized: no→ 检查 123/UDP 是否被防火墙/云安全组封了。很多云厂商默认封 UDP 123 出站。chronyc sources -v看上游状态,^?表示不可达。- 上游全部显示
^?→ 网络或防火墙问题。临时测试:chronyd -Q 'server ntp.aliyun.com iburst'(-Q 表示只查询一次然后退出,用于排错最方便)。 - 时间偏移巨大且缓慢纠正 → 手动
chronyc makestep立即跳变。但要先确认没有依赖单调递增时间的应用在运行。 - 时间在重启后回到过去 → 检查
rtcsync是否开启,以及 BIOS/RTC 是否走偏(hwclock --show)。 - 定时任务重复执行或漏执行 → 时钟被向后调过。这是为什么禁止在运行中大幅回调时间,必须用 makestep 一次跳到位。
- 证书报 "not yet valid" → 时钟跑到了未来。先修时间,再重新签证书。注意这种情况下签发的证书本身可能是错的,必须重签而不是重试。
- 容器时间不对 → 别在容器里装 NTP,去宿主机修。容器改时间需要
--privileged且会影响宿主机全局,风险极大。
小结
时钟同步是一件"做好了两三年都不会想起它,没做好就到处出怪事"的基础设施。它同时牵扯 TLS、数据库、日志、分布式协调和爬虫交互,是最典型的隐形依赖。
行动清单只有四条:安装 chrony;确认 timedatectl 里 synchronized 是 yes 且 RTC 走 UTC;把服务器时区统一成 UTC 并检查 crontab;最后加一条偏移量监控。花不到二十分钟,能省掉未来某天深夜排查"为什么证书突然无效"的一整个晚上。