两台服务器时间差了 8 分钟:一次 Linux 时间同步与 NTP 排查复盘

一套分布式系统跑在两台机器上,定时任务偶尔重复跑、多机日志顺序错乱,一查两台机器系统时间差了将近 8 分钟,可时区都对、手动 date -s 改对后过几天又慢了。排查梳理:时间不是配置而是会自己流动且流得不准的东西,机器靠石英晶振计时必然会漂,一台机器时间准不准取决于有没有东西持续校它;date -s 是一次性的还会造成时间跳变甚至倒退,让定时任务漏跑重跑;机器上有系统时钟和硬件时钟 RTC 两个钟,只改系统时钟不动 RTC 一重启就打回原形;时区只是显示规则不改变底层 UTC,差整小时多半是时区差零碎分秒才是真漂了;正确解法是装并启用 chrony 让它持续平滑校时、自动写回 RTC,配可靠 NTP 源加 iburst,偏差大用 chronyc makestep 立即校,分布式系统多机必须同步同一个源,以及一套时间同步排查纪律。

2024 年,我维护的一套系统跑在两台服务器上,A 机和 B 机。某天开始,一个本该每天只跑一次的定时对账任务,偶尔会"跑两次";更怪的是,把两台机器的日志按时间排到一起看,事件的先后顺序是乱的——B 机上明明后发生的事,时间戳却比 A 机早。我第一反应是程序有并发 bug,查了半天代码,逻辑没问题。后来我把两台机器的时间摆在一起一看,愣住了:A 机和 B 机的系统时间,差了将近 8 分钟。我心想这还不简单,时区配错了呗,timedatectl 一看,两台都是 Asia/Shanghai,时区一个字没错。我又猜是哪台机器时间被人手动改过,于是 date -s 把 B 机的时间硬掰回来,跟 A 机对齐,心想这下总好了。结果过了几天,B 机的时间又慢了——这次慢了 3 分钟。我盯着这个"改了又慢、慢了再改"的循环想了很久,最后才反应过来:我一直把"时间不对"当成一个"被改错了的配置",所以我才会一遍遍地去"改回正确值"。可时间根本不是配置——它是一个会自己流动、而且会流得不准的东西。这件事逼着我把 Linux 的系统时钟、硬件时钟、时区、NTP 时间同步这一整套彻底理清了。本文复盘这次实战。

问题背景

环境:CentOS 7,两台服务器 A 机、B 机,跑同一套分布式系统
事故现象:
- 每天只该跑一次的定时任务,偶尔跑两次
- 两台机器日志混排,事件先后顺序错乱
- ★ 一查:A 机和 B 机系统时间差了将近 8 分钟
- 时区都是 Asia/Shanghai,没配错;手动改对后过几天又慢了

现场排查:
# 1. 对一下两台机器的时间
$ ssh A 'date'; ssh B 'date'
A: Thu May 16 14:23:07 CST 2024
B: Thu May 16 14:15:11 CST 2024              # ★ B 机慢了约 8 分钟

# 2. 时区是不是配错了
$ timedatectl
      Time zone: Asia/Shanghai (CST, +0800)   # ★ 时区没错
# 两台一看都是 Asia/Shanghai

# 3. ★ 看有没有时间同步服务在跑
$ systemctl status chronyd
   Loaded: ... chronyd.service; disabled       # ★ 没启用!
   Active: inactive (dead)                      # ★ 根本没跑
$ timedatectl | grep -i ntp
   NTP synchronized: no                         # ★ 时间没在同步

# 4. ★ 看硬件时钟,和系统时钟也对不上
$ hwclock --show
2024-05-16 14:11:50                              # ★ 硬件时钟更慢

根因(后来想清楚的):
1. ★ 时间不是"配置",是会自己流动的东西。机器靠
   石英晶振计时,晶振有误差,跑久了系统时间必然
   会【漂】—— 这台快一点、那台慢一点,天经地义。
2. B 机慢 8 分钟,不是被谁改错了,是它的时钟【漂】了
   —— 长时间没有任何东西帮它校准。
3. ★ date -s 手动改时间,是【一次性】的:改完那一刻
   对了,之后它继续按自己的速率漂,几天后照样不准。
4. 真正该做的,是装一个【时间同步服务】(chrony),
   让它【持续地、平滑地】把系统时间往标准时间校。
5. 这台机器的 chronyd 压根没启用 —— 没有任何东西
   在帮它校时,它只能一个人越漂越远。
6. ★ 时区(timezone)和时间(UTC 时刻)是两回事:
   时区对,只代表"显示"对;真正错的是底层的 UTC。
时间会漂是常态,不校时才是故障。

修复 1:时间为什么会"自己变慢"——时钟会漂

# === ★ 先纠正最核心的误解:时间不是"配置" ===

# === 我以为的时间 vs 真实的时间 ===
# 我以为:系统时间是一个【设定值】,设对了就一直对,
#   它要是不对,一定是被谁"改错"了。
# ★ 真相:机器没有"绝对准"的时间源。它靠主板上一颗
#   【石英晶振】按固定节拍计数来推算时间。晶振的频率
#   有【固有误差】,还受温度影响 —— 于是机器的时间
#   【一定会漂】:每天慢几秒或快几秒,日积月累,
#   几周就能差出好几分钟。

# === ★ "漂"是常态,不是故障 ===
# 两台一模一样的服务器,放在那儿不管,
#   ★ 它们的时间【必然】会慢慢分开 —— 一台偏快、
#   一台偏慢。这不是哪台"坏了",这是物理规律。
# 所以我那个问题问错了:不该问"B 机时间为什么不对",
#   该问"B 机的时间,为什么【没有人帮它校】"。

# === 看这台机器漂得有多快 ===
# 如果装了 chrony,可以直接看它估算的漂移率:
$ chronyc tracking | grep -i 'freq\|skew'
Frequency       : 12.3 ppm slow                 # ★ 慢 12.3 ppm
# ppm = 百万分之一。12.3 ppm 意味着:
#   每 100 万秒,就慢 12.3 秒 —— 约【每天慢 1 秒】。
# ★ 别小看每天 1 秒,几个月不管就是几分钟。

# === 没装 chrony 时,粗略估漂移 ===
# 和一个可信时间源比一下当前差多少:
$ date; curl -sI https://www.baidu.com | grep -i '^date:'
# ★ 本机时间 和 HTTP 响应头里的 Date 一对比,
#   差多少一目了然。

# === ★ 一句话认知 ===
# 时间会漂,是每台机器的天性。
#   ★ 一台机器时间准不准,取决于"有没有东西在持续
#   校它",而不取决于"它一开始设得对不对"。

修复 2:date -s 手动改的坑——一次性,还会让时间"跳变"

# === ★ 第二个认知:date -s 改时间,治标且有害 ===

# === 坑 1:date -s 是"一次性"的 ===
$ date -s '2024-05-16 14:23:07'
# ★ 这条命令只做一件事:把系统时间【此刻】掰到这个值。
#   做完,机器就继续按它自己那只【会漂的晶振】走。
# 所以我那个"改了又慢"的循环说得通了:我每次 date -s
#   只是把漂移【清零】了一次,可漂移的【源头】还在,
#   过几天它自然又漂回去。★ 手动改,永远改不完。

# === ★ 坑 2:date -s 会造成时间"跳变",这很危险 ===
# 正常的时间,是【单调、连续】往前走的。
# date -s 会让时间【瞬间跳一下】 —— 往前跳,或者
#   ★【往后跳】(把快了的机器调慢,时间就倒退了)。
# 时间一旦【倒退】或【大跳】,很多程序会出问题:
#  - 定时任务:可能【漏跑】或【重复跑】(我就中了)
#  - 基于时间的缓存/令牌:过期判断瞬间错乱
#  - 数据库/日志:时间戳非单调,事件顺序乱套
#  - 有些程序甚至会因为"时间倒流"直接崩溃

# === ★ 对比:平滑校时(slew)vs 跳变校时(step)===
# 时间同步服务(chrony)校时,默认用的是【平滑校时】:
#  - 不直接跳,而是【悄悄地把时钟走快一点点 / 慢一点点】,
#    让它在一段时间内【平滑地】追上标准时间。
#  - ★ 时间始终单调向前,程序完全无感。
# 跳变校时(step):像 date -s 一样直接跳。
#  - chrony 只在【一开机、偏差太大】时才用跳变,
#    平时一律用平滑校时。

# === 结论 ===
# ★ date -s 只该用在【应急救火】:偏差大到必须立刻纠正。
#   它不是"校时方案",日常校时必须交给 chrony。
# 真要手动同步一次,用这个比 date -s 温和:
$ chronyc makestep        # 让 chrony 立即校一次(装了 chrony 才行)

修复 3:系统时钟 vs 硬件时钟——机器上其实有两个钟

# === ★ 一台机器上,其实有【两个】时钟 ===

# === 两个时钟分别是谁 ===
# 1. 系统时钟(System Clock):
#    - 操作系统【运行时】用的时间,date 看到的就是它。
#    - 它存在【内存】里,由内核维护,机器一关机就没了。
# 2. ★ 硬件时钟(Hardware Clock / RTC):
#    - 主板上一个【独立的小钟】,有纽扣电池供电。
#    - 机器【关机、断电】时,靠它默默走着、记着时间。

# === ★ 两个时钟怎么交接 ===
# - 开机时:系统从【硬件时钟 RTC】读一个初始时间,
#   作为系统时钟的起点。
# - 关机时:系统通常会把【系统时钟】写回 RTC。
# - 运行中:两个钟【各走各的】,会慢慢产生偏差。

# === 分别查看这两个时钟 ===
$ date                       # 系统时钟
Thu May 16 14:23:07 CST 2024
$ hwclock --show             # ★ 硬件时钟 RTC
2024-05-16 14:11:50          # ★ 和系统时钟差了 11 分钟

# === ★ 为什么这事重要 ===
# 如果你只用 date -s 改了【系统时钟】,没动 RTC,
#   那么机器一【重启】,系统又从那个【还是错的 RTC】
#   读时间 —— ★ 你白改了,一重启就打回原形。

# === 手动让两个时钟互相对齐 ===
$ hwclock --systohc          # 把系统时钟 -> 写进硬件时钟
$ hwclock --hctosys          # 把硬件时钟 -> 灌进系统时钟

# === ★ 好消息:用 chrony 就不用操心这个 ===
# chrony 在持续校准【系统时钟】的同时,会【定期】
#   把校准好的时间【自动写回 RTC】。
# ★ 所以只要 chrony 在跑,这"两个钟"的事你基本
#   不用管 —— 它都替你对齐了。
$ timedatectl | grep -i 'rtc'
RTC time: Thu 2024-05-16 14:23:05               # 装了 chrony 后,RTC 也准了

修复 4:时区不是时间——timezone 和 UTC 要分清楚

# === ★ 把"时区"和"时间"彻底分开 ===

# === 它们是两个层次的东西 ===
# 1. UTC 时刻:这是【真正的时间】,全球统一的那个
#    绝对时刻。机器内部、时间戳、文件 mtime,本质上
#    都是用 UTC 在记。
# 2. ★ 时区(timezone):只是一个【显示规则】 ——
#    "把那个 UTC 时刻,加 8 小时显示给中国用户看"。
# ★ 时区,改的是【怎么把时间显示出来】,
#   它【根本不改变】底层那个真正的 UTC 时刻。

# === 一个例子说清楚 ===
# 同一个 UTC 时刻 06:23:07:
#  - 时区设 Asia/Shanghai -> 显示 14:23:07
#  - 时区设 UTC           -> 显示 06:23:07
# ★ 显示的数字不同,但它们是【同一个时刻】。
#   改时区,不会让你"穿越";改 UTC,才是真改了时间。

# === ★ 于是要分清两类故障 ===
# 故障 A:时区错了。比如时区被设成了 UTC。
#  - 现象:date 显示的时间,比"墙上的钟"差了 8 小时。
#  - ★ 本质:时间是【对的】,只是显示规则错了。
#  - 修:timedatectl set-timezone Asia/Shanghai
# 故障 B:★ UTC 时刻错了。就是我这次的情况。
#  - 现象:差几分钟、十几分钟这种【不整齐】的数。
#  - ★ 本质:底层真实时间就是错的 —— 这才是大问题。
#  - 修:靠 chrony 同步。

# === ★ 一个快速判断法 ===
# 时间差,如果是【整 8 小时】(或别的整小时)
#   -> 八成是【时区】问题。
# 时间差,如果是【几分几秒】这种零碎值
#   -> 八成是【UTC 时间漂了】,是真·时间不准。
# 我这次差 8 分钟,零碎值 —— 一开始就该想到不是时区。

# === 查时区 / 改时区 ===
$ timedatectl                       # 看当前时区
$ timedatectl list-timezones | grep Shanghai
$ timedatectl set-timezone Asia/Shanghai     # 设时区(只影响显示)

修复 5:正确解法——装 chrony,让它持续平滑校时

# === ★ 真正的解法:交给时间同步服务 chrony ===

# === 为什么是"服务",而不是"命令" ===
# 时间会【持续地漂】,所以校时也必须是【持续地做】。
#   一条命令只能校一次,漂移立刻又开始。
# ★ 只有一个【常驻后台的服务】,不停地和标准时间源
#   对比、不停地微调,才能让时间【一直】准。
#   这个服务,在 CentOS 7+ 上就是 chrony。

# === 第一步:装上并启用 chrony ===
$ yum install -y chrony
$ systemctl enable --now chronyd          # ★ 开机自启 + 立即启动
$ systemctl status chronyd
   Active: active (running)                # ★ 跑起来了

# === ★ 第二步:配置时间源(NTP 服务器)===
$ vi /etc/chrony.conf
# 把同步源指向可靠的 NTP 服务器(国内用阿里云的):
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst
server ntp3.aliyun.com iburst
#  - iburst:★ 启动时【连发几个包】快速完成首次同步,
#            不然首次对时要等好几分钟。
$ systemctl restart chronyd

# === ★ 第三步:验证同步状态 ===
$ chronyc sources -v
^* ntp1.aliyun.com    2  6  377  23  +1.2ms ...   # ★ ^* = 正在用这个源
# ★ 行首符号:^* 当前同步源 / ^+ 备选可用 / ^? 不可用
$ chronyc tracking
Reference ID    : ntp1.aliyun.com
System time     : 0.000041 seconds slow ...        # ★ 偏差已是微秒级
Stratum         : 3
$ timedatectl | grep -i ntp
   NTP synchronized: yes                            # ★ 同步成功

# === ★ 第四步:首次偏差大,让它立刻校一次 ===
# 我这台慢了 8 分钟,偏差太大,chrony 默认平滑校
#   会校很久。让它立即跳一次:
$ chronyc makestep
# ★ makestep:允许 chrony 这次用"跳变"把大偏差一次纠正。
#   之后日常的小漂移,它会自动用平滑校时处理。

# === ★ 内网多台机器:自建一台 NTP server ===
# 大量内网机器都去连公网 NTP,不优雅也不稳。
# 更好的做法:让一台机器做内网 NTP server,
#   其他机器都同步它 —— 这样【所有机器时间一致】,
#   分布式系统最需要的就是"彼此一致",哪怕都偏一点点。
# (在那台 server 的 chrony.conf 里加 allow 网段即可)

# === ★ 关键认知 ===
# 装好 chrony 这一刻,前面修复 2/3/4 的麻烦【全消失】:
#  - 不用再手动 date -s(它持续校)
#  - 不用管 RTC(它自动写回)
#  - 多台机器时间天然一致(都同步同一个源)

修复 6:时间同步排查纪律

# === 这次事故暴露的认知盲区,定几条纪律 ===

# === 1. ★ 时间会漂是常态,不校时才是故障 ===
# 别问"时间为什么不对",要问"为什么没东西在校它"。

# === 2. ★ 每台服务器都必须跑时间同步服务 ===
$ systemctl is-enabled chronyd            # 必须是 enabled
$ timedatectl | grep -i ntp               # NTP synchronized: yes

# === 3. date -s 只用于应急,不是校时方案 ===
# 它一次性、还会造成时间跳变,日常校时交给 chrony。

# === 4. ★ 分清时区问题 和 UTC 时间问题 ===
# 差整小时 -> 多半时区错;差零碎分秒 -> 真·时间漂了。

# === 5. 记住有两个时钟:系统时钟 和 硬件时钟 RTC ===
$ date ; hwclock --show                   # 两个都看一眼

# === 6. ★ 分布式系统:多机时间【必须一致】 ===
# 让所有机器同步【同一个】时间源,内网最好自建 NTP。

# === 7. 时间敏感的程序,别假设时间单调/绝对准 ===
# 算时间间隔,优先用单调时钟(CLOCK_MONOTONIC),
#   它不受校时跳变影响。

# === 8. 排查"机器时间不对"的步骤链 ===
$ date                                     # ① 看当前时间差多少
$ timedatectl                               # ② 看时区 + NTP 同步状态
$ systemctl status chronyd                  # ③ 时间同步服务在跑吗
$ chronyc sources -v                        # ④ 同步源连上了吗
$ chronyc tracking                          # ⑤ 看实际偏差和漂移率
$ chronyc makestep                           # ⑥ 偏差大就立即校一次
# 按这个顺序,时间不准基本能定位、能根治。

命令速查

需求                        命令
=============================================================
看系统时间                  date
看时区和 NTP 同步状态        timedatectl
看硬件时钟 RTC               hwclock --show
看时间同步服务状态          systemctl status chronyd
装并启用 chrony              yum install -y chrony; systemctl enable --now chronyd
看 chrony 同步源            chronyc sources -v
看实际偏差和漂移率          chronyc tracking
偏差大时立即校一次          chronyc makestep
改时区(只影响显示)        timedatectl set-timezone Asia/Shanghai
系统时钟写回硬件时钟        hwclock --systohc

口诀:时间会漂是常态,不校时才是故障;date -s 只应急别当方案
      装 chrony 持续平滑校时,差整小时多半是时区差零碎分秒是真漂了

避坑清单

  1. 时间不是配置而是会自己流动的东西,机器靠石英晶振计时,有误差就必然会漂
  2. 一台机器时间准不准,取决于有没有东西在持续校它,而非一开始设得对不对
  3. date -s 是一次性的,改完时钟继续按原速率漂,几天后照样不准,治标不治本
  4. date -s 会造成时间跳变甚至倒退,定时任务漏跑重复跑、缓存令牌判断错乱
  5. chrony 默认用平滑校时,悄悄调快调慢让时间单调向前,程序无感,远比跳变安全
  6. 机器上有两个时钟,系统时钟在内存关机即失,硬件时钟 RTC 有电池关机仍走
  7. 只改系统时钟没动 RTC,一重启又从错的 RTC 读时间,白改;chrony 会自动写回 RTC
  8. 时区只是显示规则不改变底层 UTC 时刻,差整小时多半是时区,差零碎分秒是真漂了
  9. 每台服务器都必须装并启用 chronyd,配可靠 NTP 源加 iburst 加速首次同步
  10. 分布式系统多机时间必须一致,让所有机器同步同一个源,内网最好自建 NTP server

总结

这次"两台机器时间差了 8 分钟、定时任务重复跑、日志顺序错乱"的事故,纠正了我一个埋得很深、自己却从没察觉的认知错误:我一直把"时间",当成了一种"配置"。在我的潜意识里,系统时间和一个 IP 地址、一个端口号、一个路径没什么两样——它是一个我设定下去的、静态的值。我设对了,它就该一直对;它要是不对了,那一定是某个环节出了错,是它"被改坏了"。正是这个"时间是配置"的心智模型,决定了我整场排查的姿势:我做的所有事情,本质上都是在"寻找那个把它改错的人或事",然后"把它改回正确的值"。我用 date -s 把 B 机的时间硬生生掰回去,就像我修正一个写错的配置项一样,做完还很有成就感。可几天之后,它又慢了。我于是再改一次。我陷在一个"改回正确值——它又偏离——再改回正确值"的循环里,却始终没有停下来问一句:一个静态的配置项,改对了之后,它怎么会自己"偏离"呢?复盘到根上,我才终于看清,时间从来就不是一个"配置",它是一个"过程"。机器里没有一个地方"存着"正确的时间等我去设定;机器只是靠主板上一颗石英晶振,一下一下地数着节拍,用这个节拍去"推算"现在是几点。而这颗晶振,和世界上所有的晶振一样,有它自己的固有误差,还会随温度变化——于是这台机器推算出来的时间,就【一定】会漂。它每天慢一点点,慢得悄无声息,可几个星期累积下来,就是实实在在的 8 分钟。所以 B 机的时间不对,根本不是"被谁改错了",而是它从被装好的那天起,就【从来没有人帮它校准过】——它只是一个人,孤独地、忠实地,按着自己那颗不那么准的晶振,越漂越远。我用 date -s 做的事,不过是每隔几天,把它漂出去的距离粗暴地清零一次,而那个让它持续漂移的源头,我一次都没碰过。我治的是症状,而且每次只治几天。真正的解法,根本不是某一条更高明的"改时间"的命令,而是【换一类东西去做这件事】——不是用一条一次性的命令,而是用一个常驻的服务。chrony 这样的时间同步服务,它做的不是"把时间设成某个值",而是"持续地、永不停歇地,把这台机器的时间,一点一点、平滑地,往全世界统一的标准时间上拽"。它承认时间会漂,所以它就一直校;它知道时间不能跳,所以它校得悄无声息,让时钟只是走快一丝或走慢一丝,而不是猛地跳一下——因为它清楚,一次时间的倒退或大跳,就足以让我的定时任务漏跑或重跑,就足以让两台机器的日志再也排不到一起。这次最大的收获,是我意识到,有些东西,它看起来像一个"值",但它的本质是一个"流"。一个值,你设定它、纠正它;而一个流,你只能去持续地【治理】它。我过去太习惯于用对待"值"的方式,去对待这个世界上所有的东西了——找到错的,改成对的,完事。可面对一个会自己流动、自己偏离的东西,"改成对的"这个动作本身就是无效的,因为你改对的那一刻,它就又开始流走了。对待这一类东西,唯一有效的姿势,是放弃"一劳永逸地设对它"的幻想,转而建立一个"持续地、自动地纠正它"的机制。下一次,当我再遇到一个"改了又错、错了再改"的循环时,我会立刻警觉:这大概不是我没找到那个正确的值,而是我错把一个"流"当成了"值"——我该做的,不是更用力地去改它,而是停下来,为它装上一个能持续校准它的东西。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
Linux教程

服务报 Cannot assign requested address:一次 Linux TIME_WAIT 端口耗尽排查复盘

2026-5-20 21:04:17

Linux教程

调用接口偶尔卡 5 秒整:一次 Linux DNS 解析慢排查复盘

2026-5-20 21:11:33

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索