日志时间对不上、证书突然报 not yet valid、Token 刚发就说已过期 ------ 很多时候不是程序有 bug,是机器的时钟悄悄漂了。这篇按五步把时间问题查清楚。
导语
时间是最容易被忽略的基础设施。业务代码没人会去读 date,但 TLS 握手、JWT 校验、MySQL 主从、分布式锁、日志排序,全都在拿系统时钟当基准。一旦偏差超过容忍度,故障现象会四处开花,却都不指向"时间"两个字。
排查思路只有一条:先分清是时区错还是时钟偏,再看同步源通不通,最后看校准策略对不对。
第一步:分清"时区错"和"时钟偏"
这两个故障现象完全不同,处理方式也不同。一条命令先看全貌:
bash
timedatectl
# Local time: Tue 2026-10-20 09:12:33 CST
# Universal time: Tue 2026-10-20 01:12:33 UTC
# RTC time: Tue 2026-10-20 01:12:33
# Time zone: Asia/Shanghai (CST, +0800)
# System clock synchronized: yes
# NTP service: active
判读要点:
- Universal time 对、Local time 差 8 小时 → 纯时区问题,
timedatectl set-timezone Asia/Shanghai即可,不用碰 NTP。 - UTC 本身就不对 → 真的时钟漂移,继续往下查。
System clock synchronized: no→ 同步已断,不管显示多准都不可信。
再看偏差到底多大:
bash
chronyc tracking
# Reference ID : C0A80101 (10.168.1.1)
# System time : 0.000041231 seconds slow of NTP time
# Last offset : +0.000038912 seconds
# RMS offset : 0.000102334 seconds
# Frequency : 12.431 ppm slow
# Residual freq : +0.002 ppm
# Skew : 0.041 ppm
# Root delay : 0.021345 seconds
# Leap status : Normal
System time offset 是当前偏差:毫秒级正常,秒级说明同步质量差,分钟级基本可以判定 chronyd 没在正常工作。
第二步:看时间源到底通不通
bash
chronyc sources -v
# MS Name/IP address Stratum Poll Reach LastRx Last sample
# ^* 10.168.1.1 2 6 377 31 -12us[ -9us] +/- 1.2ms
# ^+ ntp.aliyun.com 2 6 377 29 +21us[ +25us] +/- 12ms
# ^? 203.0.113.10 0 6 0 - +0ns[ +0ns] +/- 0ns
状态位是第一判据:
^*当前同步源,正常。^+备用源,可用但没被选中。^-被算法排除(通常是偏差太大)。^?不可达或尚未采样成功,Reach 为 0 就是根本没收到回包。
Reach 是八进制位图,377 表示最近 8 次全部成功,0 表示一次都没通。此时按顺序排查:
bash
# 1. DNS 能否解析
getent hosts ntp.aliyun.com
# 2. UDP 123 出方向是否被拦(安全组/iptables/云防火墙)
tcpdump -ni any udp port 123 -c 10
# 3. 直接问一次,看有没有响应
chronyc -n sources
UDP 是无连接的,nc -z 测不出真假,最可靠的是 tcpdump 看有没有回包、chronyc sources 看 Reach 有没有涨。国内云主机常见坑是安全组只放了 80/443,UDP 123 出方向被默认拒绝。
第三步:看服务和配置是否真的生效
源没问题但不校时,多半是服务层出了岔子。
bash
systemctl status chronyd
ss -ulnp | grep ':123'
ps -ef | grep -E 'chronyd|ntpd' | grep -v grep
三类高频问题:
- chronyd 和 ntpd 同时装、同时跑 ,抢 123 端口,后起的那个直接起不来,
systemctl status里能看到Address already in use。 - 配置写了但没重启 。
chrony.conf里的server/pool行是启动时读的,改完只reload有一部分参数不生效,稳妥做法是systemctl restart chronyd,再chronyc sources确认新源已出现在列表里。 - 容器里校时 。容器与宿主机共享内核时钟,在容器里跑 chronyd 或
date -s都是白费功夫,只会污染宿主机时间。要校就进宿主机校。
第四步:分清"慢调"和"快调",大步偏差必须 makestep
这是最容易踩的坑。chronyd 默认只做 slew(缓慢调整),速率上限只有 500ppm,也就是每秒钟最多纠正 0.5 毫秒。偏差 3 分钟的话,理论上要跑将近 100 小时才追平,这期间你看到的还是错的时间。
bash
# 确认当前策略(默认只在前 3 次更新、且偏差 >1s 时允许跳变)
grep -n makestep /etc/chrony.conf
# 立即跳变纠正
chronyc makestep
# 再确认偏差已归零
chronyc tracking | grep -E 'System time|Last offset'
如果确实要允许首启动大步纠正,在 chrony.conf 里加:
makestep 1.0 3
意思是前 3 次时间更新时,偏差超过 1 秒就直接 step。注意:线上机器做 step 会让时间瞬间跳变,可能触发定时任务重复执行、监控出现负值毛刺,业务敏感机器建议放在维护窗口做。
顺带看一个指标:Frequency 是晶振的实际漂移率。几十 ppm 属正常;几百 ppm 说明这颗"表"本身很差,多见于虚拟机宿主负载高、CPU 降频,或者老硬件电池没电。
第五步:落到硬件时钟与长期巡检
软件时钟校准完,还要确认重启后不会退回旧时间,以及给时间加一条监控。
bash
# 时区与 RTC 规范:RTC 统一用 UTC 存,避免双系统差 8 小时
timedatectl set-timezone Asia/Shanghai
timedatectl set-local-rtc 0
hwclock --systohc
# 巡检脚本:偏差超过 0.5 秒告警
offset=$(chronyc tracking | awk '/System time/{print $4}')
awk -v o="$offset" 'BEGIN{ if (o+0 > 0.5) { print "CLOCK_DRIFT " o; exit 1 } }'
时间不准会引发的连锁故障,遇到时可以反查时钟:TLS 报 certificate is not yet valid、JWT/Kerberos 认证莫名失败、MySQL 主从 Seconds_Behind_Master 忽大忽小、分布式锁提前过期、日志按时间排序乱序、定时任务对不上点。
避坑清单
- 别用
date -s手动改时间当"修复",改完没解决漂移根因,第二天照样偏。 chronyc tracking的 offset 是"当前偏差",sourcestats的 offset 是"估计偏差",两者不要混着看。- 内网自建 NTP 服务器要配
allow 10.0.0.0/8,否则客户端永远 Reach 0。 - 虚拟机建议开宿主的时钟同步(如 VMware Tools / cloud-init),否则快照恢复后时间会大幅回退。
- 校时前确认业务有没有依赖"时间单调递增",有就选低峰期做 step。
ntpdate已废弃,且必须先停 chronyd 再用,混用会互相打架。- 监控只盯"当前偏差"不够,要同时盯
System clock synchronized是否翻成 no ------ 那才是同步断了的早期信号。
一图总结
时间故障排查五步,顺序不能乱:
- 第一步 分性质:UTC 对不对,先分清时区错还是时钟偏
- 第二步 看源头 :
chronyc sources的^*与 Reach 是否正常 - 第三步 看服务:进程在不在、123 端口有没有被抢、配置有没有真生效
- 第四步 看策略 :大偏差要
makestep,只 slew 永远追不上 - 第五步 看持久:RTC、时区、巡检告警三件套补齐
五步走完,时间问题基本就无处可藏了。