服务器时间为什么会跑偏,以及它到底能惹多大的祸
很多站长对服务器时间的印象,停留在"看起来是对的就行"。直到某天你发现:日志里的时间戳和现实差了半小时,按时间排查故障时把所有事件都对错了顺序;HTTPS 证书突然报 "certificate is not yet valid";双机同步的数据库因为时间偏差导致主从复制异常;甚至定时任务在错误的时间点批量触发,把服务器 CPU 打满。
这些都不是危言耸听。时间是服务器上最基础、也最容易被忽视的一项基础设施。这篇文章讲清楚 Linux 上时间同步的正确做法:为什么该从 ntpd 换成 chrony、如何挑选 NTP 源、时间漂移怎么排查,以及虚拟化环境里那个绕不开的"时间跑得比现实快"的问题。
先弄明白三个概念:UTC、硬件时钟、系统时钟
要调时间,先得分清 Linux 上有三个"时间":
UTC(协调世界时)是基准,所有时间同步的目标都是让本机时间和 UTC 保持一致。
硬件时钟(RTC)是主板上的小电池维持的时钟,即使断电也在走。它可能被存成 UTC,也可能被存成本地时间,由 timedatectl 的 RTC in local TZ 字段决定。
系统时钟是内核维护的、我们平时看到的 date 命令输出的那个时间,它由内核根据硬件时钟初始化,之后独立运行,需要靠 NTP 不断校正。
麻烦往往出在这三者不一致。比如服务器重启后时间突然跳变,通常是硬件时钟本身走偏了,或者 RTC in local TZ 的设置和你的预期相反。
为什么不推荐用老的 ntpd,而选 chrony
过去十年里,Linux 上的时间同步标准答案是 ntpd。但现在无论 CentOS 8+、Ubuntu 20.04+ 还是 Debian,默认都换成了 chrony。原因主要有三个:
同步更快。ntpd 可能需要几分钟甚至几十分钟才把时间偏差收敛到位,chrony 通常几秒内就能完成首次同步,这对频繁开关机的 VPS 和容器环境特别友好。
对不稳定的网络更宽容。chrony 能更好地处理网络时断时续、时延波动大的情况,适合家用宽带上的自建服务器。
支持更大范围的时间调整。ntpd 默认对超过 1000 秒的时间偏差直接拒绝调整,chrony 可以处理任意大小的偏差(虽然大偏差需要 makestep 指令配合)。
结论很明确:新装服务器直接用 chrony,还在用 ntpd 的也建议迁移。
chrony 的安装与基础配置
Debian / Ubuntu 上:
apt update
apt install -y chrony
systemctl enable --now chrony
systemctl status chronyCentOS / Rocky / AlmaLinux 上:
yum install -y chrony
systemctl enable --now chronyd配置文件在 /etc/chrony/chrony.conf(Debian 系)或 /etc/chrony.conf(RHEL 系)。核心就是 server 或 pool 指令,用来指定上游时间源。一份精简的配置示例:
# 使用国内可访问的 NTP 池
pool ntp.aliyun.com iburst
pool cn.pool.ntp.org iburst
pool time.cloudflare.com iburst
# 首次启动时允许大幅调整时间,但之后只做微调
makestep 1.0 3
# 允许本机成为内网 NTP 服务端(可选,多机场景)
# allow 10.0.0.0/24
# 指定漂移文件位置
driftfile /var/lib/chrony/chrony.drift
这里有几个关键点值得解释。iburst 让 chrony 在启动时快速发送四次探测包,加快首次同步速度,几乎总要加上。makestep 1.0 3 的意思是:在启动后的前 3 次同步中,如果偏差超过 1 秒,就直接"跳变"调整时间而不是慢慢追赶——这是解决服务器重启后时间大幅偏差的关键。超过前 3 次之后,chrony 只做平滑微调,不会让时间突然跳回去。
选 NTP 源的一个实用原则
很多人配置 NTP 时随手填了 pool.ntp.org,结果国内服务器同步起来又慢又不稳定。正确的做法是:优先用国内可直连的时间源,再补充几个公共源做冗余。
推荐组合是 ntp.aliyun.com、ntp.tencent.com、cn.pool.ntp.org 各配一个,再加一个 time.cloudflare.com 作为备选。注意用 pool 而不是 server 指向公共 NTP 池,因为 pool 会自动在多个机器间做负载均衡,避免你死磕一台服务器。
顺便提醒:公共 NTP 服务有反滥用策略,不要在一台机器上配置几十个 server 条目疯狂探测,正常配置四到六个就足够了。
验证时间同步是否真的生效
配置完重启 chrony,然后执行:
chronyc sources -v输出里每一行代表一个时间源,重点关注两个标记:行首的 ^* 表示这是当前正在使用的最佳源,^+ 表示备选源。如果某个源前面是 ^?,说明它不可达或还没被同步过。看到 ^* 才算同步成功。
再看偏差大小:
chronyc tracking重点看 System time 这一行,它显示本机相对于标准时间的偏差,正常情况下应该是几十微秒到几毫秒级别。如果这个数字是几百毫秒甚至更大,说明你的源质量不好或者网络时延太大。
还可以直接和 date 对照,用 timedatectl 看整体状态:
timedatectl
timedatectl set-ntp true # 确保 NTP 同步已启用虚拟化环境里的"时间跑得比现实快"难题
这是 VPS 用户几乎都会遇到的问题:明明配好了 chrony,但时间还是慢慢跑偏,尤其是 CPU 负载高的时候,服务器时间会明显走得快。原因是虚拟机的时钟中断不总是准时,宿主机 CPU 被抢占时,guest 的时钟可能累积误差。
chrony 恰好就是为这种场景设计的,它比 ntpd 更擅长处理频率漂移。如果漂移仍然明显,可以尝试以下措施:
第一,确认 KVM 时钟源是 kvm-clock 或 tsc。
cat /sys/devices/system/clocksource/clocksource0/current_clocksource如果显示的是 hpet 或 acpi_pm,时钟精度会比较差,可以在内核启动参数里加上 clocksource=kvm-clock 或 clocksource=tsc(需要宿主机和内核支持)。
第二,缩短同步间隔。在 chrony.conf 里把 minpoll、maxpoll 调小,让 chrony 更频繁地校正:
server ntp.aliyun.com iburst minpoll 4 maxpoll 6其中 minpoll 4 表示最短 2^4=16 秒探测一次,maxpoll 6 表示最长 64 秒一次,比默认的 64~1024 秒频繁得多。
第三,容器里不要自己跑 chrony。Docker 容器默认和宿主共享内核时钟,容器里装 chrony 既没意义也容易出问题。容器内只要正确设置时区即可:
docker run -e TZ=Asia/Shanghai your-image或者挂载宿主机的时区文件:-v /etc/localtime:/etc/localtime:ro。
一次真实的时间排查过程
回到开头那次故障:日志时间对不上。我的排查顺序是这样的——
第一步,timedatectl 看系统时间和时区,确认时区设置成了 Asia/Shanghai 而不是默认的 UTC。很多"时间对不上"其实是时区问题,不是同步问题,这一步先排除掉一半可能。
第二步,chronyc tracking 看偏差,发现 System time 是 1800 多秒——整整半小时。这么大的偏差说明同步根本没成功,而不是精度问题。
第三步,chronyc sources -v 看到所有源都是 ^?。进一步检查发现,服务器的防火墙放行了 UDP 123 入口,但没放行出口,导致 chrony 根本发不出探测包。
第四步,放开出站 UDP 123,重启 chrony,makestep 立即把时间跳回正确值,问题解决。
这个案例的教训是:时间同步失败最常见的两个原因,一是时区配错(假故障),二是 NTP 端口被防火墙挡住(真故障)。排查时先看 timedatectl 排时区,再看 chronyc sources 排连通性,基本能覆盖八成场景。
把时间同步纳入日常运维
对个人站长来说,时间同步不需要天天盯着,但值得做两件事。一是把 chronyc tracking 的偏差值写进你的服务器巡检脚本,偏差超过阈值就告警;二是把 NTP 出站端口的放行写进服务器初始化的标准清单里,避免新机器重蹈覆辙。
时间这件事,平时无人关心,出事就是一连串连锁反应。花十分钟配好 chrony,比事后花两小时对着一堆时间错乱的日志抓头要划算得多。
进阶:自建内网时间服务器,多机统一时间
当你手里不止一台服务器时,让每台机器都去连公网 NTP,既慢又不整齐,还可能触发公共池的反滥用限制。更专业的做法是挑一台网络稳定的机器做内网时间服务器,其他机器都向它同步。
在内网时间服务器的 chrony.conf 里,除了配置上游公网源,还要允许内网客户端来查询:
# 上游公网源
pool ntp.aliyun.com iburst
makestep 1.0 3
driftfile /var/lib/chrony/chrony.drift
# 允许内网网段访问本机 NTP 服务
allow 10.0.0.0/24
allow 192.168.1.0/24
# 即使暂时连不上上游,也继续对内提供服务
local stratum 10local stratum 10 这行的作用很关键:它让这台服务器在失去上游连接时,仍然以第 10 层的时间源身份对内提供服务,避免整个内网因为上游抖动而集体失去时间基准。
客户端机器的配置就简单了,只需要指向这台内网服务器:
server 10.0.0.10 iburst
makestep 1.0 3
driftfile /var/lib/chrony/chrony.drift别忘了在内网时间服务器上放行入站 UDP 123:
# firewalld
firewall-cmd --permanent --add-service=ntp
firewall-cmd --reload
# ufw
ufw allow 123/udp这样一来,整个内网的时间都被一台机器统一收敛,客户端彼此之间的时间偏差能控制在毫秒级以内,对分布式日志、主从复制、证书校验这类对时间敏感的场景非常友好。
常见问题速查表
问题:chronyc sources 全部显示 ^?,一个源都同步不上。检查三件事:出站 UDP 123 是否放行、DNS 能否解析到 NTP 域名、上游源地址是否写错。先用 ping ntp.aliyun.com 和 nc -u -v ntp.aliyun.com 123 确认联通性。
问题:时间同步上了,但 date 显示的还是错的时区。这不是同步问题,是时区问题。执行 timedatectl set-timezone Asia/Shanghai 修正,同步本身没错。
问题:每次重启后时间都会跳变一次。大概率是硬件时钟和系统时钟不一致。可以在同步成功后执行 hwclock --systohc 把系统时间写回硬件时钟,让两者对齐。同时确认 makestep 1.0 3 已配置。
问题:容器里的时间和宿主机差了 8 小时。这是时区问题,不是同步问题。给容器设置 TZ=Asia/Shanghai 环境变量或挂载宿主 /etc/localtime 即可,不要在容器里跑 chrony。
问题:VPS 时间持续走快,chrony 一直在追。这是虚拟化时钟漂移的典型表现。优先确认时钟源是 kvm-clock 或 tsc,再把 minpoll/maxpoll 调小增加校正频率,一般就能把漂移压住。
总结
服务器时间同步是个典型的"低关注度、高影响面"工程。chrony 取代 ntpd 已经是行业共识,配置起来也不复杂:选好国内 NTP 源、加上 iburst 和 makestep、验证 ^* 源出现、定期看偏差。多机环境再进一步自建内网时间服务器,把时间基准收敛到一处。把这套流程固化进你的服务器初始化脚本,就能一劳永逸地告别时间跑偏带来的一堆怪问题。