1. 服务器时间漂移凭什么值得你专门处理
很多运维同事对时间同步这件事的态度是“小事一桩,配一下就好”,直到某天凌晨三点被一通电话叫醒,才知道时间不准能惹出多大的麻烦。
我当时遇到过的情况是这样:某台数据库从库的复制链路突然报错,主库日志和从库日志对不上,进度差了好几分钟。查了很久网络、账号、权限都没问题,最后用 ntpq -p 看了一眼才发现,从库的系统时间比主库慢了将近 40 秒。主从复制的日志轮转和 GTID 判断依赖时间顺序,时间一偏,整个链路直接卡死。那次之后我才真正意识到,Linux 时钟同步不是“锦上添花”的运维工作,而是服务器基础环境里最容易被忽略、出事又最隐蔽的环节之一。
1.1 日志审计和排障会先遭殃
时间不准最直接的影响是日志。排查问题的时候,大家的第一反应都是先看各个服务打印的日志,然后按时间线把事件串起来。如果两台服务器之间差了几十秒甚至几分钟,日志的先后顺序就是乱的,告警触发时间和实际故障时间对不上,错误的判断和误操作就会趁机混进来。
我曾经在某个项目里见过更夸张的场景:业务侧的定时任务按 Cron 跑,凌晨两点执行一次全量数据清理。结果某台机器的时钟偏慢,任务实际执行时间被延后了一个多小时,和白天高峰期的业务请求撞在了一起,直接把一个核心接口拖到超时。日志里看起来“一切正常”,因为任务确实跑了,只是跑的时间完全不合预期。
1.2 证书校验、认证体系和分布式协议都是“时间敏感型”
如果只是日志乱一点,忍忍也就过去了。但下面的这些场景,时间偏差一旦超过阈值,就会直接变成“硬故障”:
- TLS/HTTPS 证书校验收到了服务器时间影响,时间超前或者落后太多,证书会被判定为“未生效”或者“已过期”,表现为各种奇怪的握手失败。
- Kerberos 认证有“时间偏移窗口”的设置,客户端和服务端时间差太大,票据就直接失效,表现是反复要求重新登录,或者服务间调用频繁报认证错误。
- 分布式数据库、消息队列、分布式锁这类依赖时钟顺序的系统,对时间偏差尤其敏感。哪怕不是严格按时间排序,只要节点间时间差过大,很多一致性协议的正常工作前提就被破坏了。
- 监控告警系统自己也需要时间基准,否则告警合并、事件关联、降噪规则全部失去意义。
这些问题的共性特点是:表面症状五花八门,根因却只有一个——系统时间没有做同步,或者同步机制本身没有跑起来。
1.3 不是“偶尔对一次”就行,而是要持续纠偏
有同学可能会说:“我装完系统的时候时间是对的呀,为什么还要专门弄?”这就是对时钟漂移的误解。服务器硬件里的晶振和振荡器并不是绝对精确的,温度变化、硬件老化、CPU 负载波动都会让时钟产生微小的偏移。一个典型的 x86 服务器,每天漂移几秒到十几秒都是正常水平。积累一个月,偏几分钟完全不奇怪。所以,时钟同步不是一个“配一次就永久生效”的动作,而是一个需要长期稳定运行的基础服务。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 拆开 Linux 时间体系:系统时间、RTC 与时钟源的三角关系
在动手配置之前,有必要把 Linux 的时间体系讲清楚。因为很多时钟同步的问题,其实不是同步没配好,而是把“系统时间”和“硬件时间”这两件事混为一谈,最后怎么排查都是晕的。
2.1 系统时间:内核维护的软件时间
我们平时用 date 命令看到的时间,就是系统时间。它由 Linux 内核维护,在内核启动后持续运行,应用层所有的文件时间戳、日志时间、连接超时计算,用的都是它。系统时间是基于系统启动时的初始值,再叠加系统启动后的 tick 计数得到的,并不直接等于硬件时间。
2.2 RTC 硬件时间:断电后依然存在的底层时钟
主板上的 RTC(Real Time Clock)芯片是独立供电的,即使服务器关机、断电,它也能依靠主板上的电池继续走时。所以每次开机的时候,内核会先从 RTC 读取一个初始时间,作为系统时间的起点。
这里有个很容易踩的坑:RTC 芯片默认是 UTC 时区存储还是本地时间存储,在不同的发行版和不同的安装习惯下是不一样的。如果这个设置错了,重启前后时间会差 8 个小时。后面我会专门讲这个。
2.3 时钟源决定“tick”的准度
系统时间运行过程中,内核依靠时钟源(Clock Source)来获取时间增量。常见的三种时钟源优缺点非常明显:
| 时钟源 | 原理 | 精度 | 适用场景 |
|---|---|---|---|
| TSC | 依赖 CPU 的 TSC 计数器,直接读取当前 CPU 周期计数 | 高 | 物理机首选,延迟低、开销小 |
| HPET | 独立的定时器芯片,精度高,但是每次读取有总线延迟 | 中 | TSC 不稳定时的备用方案 |
| kvm-clock | 半虚拟化时钟,由宿主机提供时间基准 | 中高 | KVM 虚拟机首选 |
| xen/vmi clock | 类似半虚拟化方案,由虚拟化平台提供时间基准 | 中高 | Xen 或特殊虚拟化环境 |
所以,虚拟机里如果发现时间漂移得特别厉害,先别急着怪 NTP,先看看时钟源是不是选对了。比如某些虚拟化环境下,如果 guest 用的是 TSC 或者 HPET,而宿主机又有 CPU 热迁移或者节能策略,时间就会频繁跳变。解决办法通常是确认虚拟化时钟驱动已经加载,并把它设为首选时钟源。
2.4 时区是显示层的事,别和时钟同步混为一谈
还有一类问题属于“看着像时间错,其实是时区错”。时钟同步解决的是“UTC 时刻对不对”的问题,时区解决的是“这个 UTC 时刻在本地应该显示成几点”的问题。
我建议的实践是:服务器统一用 UTC 存储,应用展示层做时区转换;或者所有服务器统一设置 Asia/Shanghai,保证部署、日志、定时任务所见即所得。关键是全公司要有一个一致的约定,不要一半机器用 UTC,一半机器用 CST,排起错来非常痛苦。
3. chrony、systemd-timesyncd与ntpd:三个方案的真实差距
Linux 下做时间同步,主流的方案现在基本就是三个:老牌的 ntpd、轻量的 systemd-timesyncd、以及后起之秀 chrony。很多初学者分不清它们的区别,我直接给出一张实际使用中的对比表。
| 对比项 | ntpd | chrony | systemd-timesyncd |
|---|---|---|---|
| 进程与配置 | 主进程 ntpd,配置文件 /etc/ntp.conf |
主进程 chronyd,配置文件 /etc/chrony.conf |
集成在 systemd 中,配置在 /etc/systemd/timesyncd.conf |
| 快速同步能力 | 偏移较大时会拒绝调整(默认超过 128ms 就不做 step) | 支持 makestep,首次启动能快速把时间校准到毫秒级 | 支持简单的快速同步,但策略不如 chrony 灵活 |
| 网络状况适应能力 | 网络抖动大时容易频繁跳步 | 通过频率调节和滤波算法,能很好地应对网络抖动 | 较基础,适合简单场景 |
| 服务端能力 | 可以作为 NTP Server 提供公共服务 | 同样可以作为 Server,且对客户端数量大时表现更好 | 只作为客户端,不支持对外提供时间服务 |
| 适用发行版 | 老系统为主,新默认越来越少见 | RHEL/CentOS/Rocky/Fedora 等默认方案 | Ubuntu/Debian 近版本的默认方案 |
| 监控客户端能力 | 较弱 | 自带 chronyc 客户端,可以看抖动、偏移、延迟 | 较弱 |
3.1 为什么新项目我基本都推荐 chrony
不用绕弯子,结论就是:现在新接触的机器,我基本无脑选择 chrony。原因其实很简单,它解决了老 ntpd 时代两个让人头大的痛点。
第一个痛点是首次启动时的校准速度。一台放了很久的虚拟机开机,时间可能偏了几分钟甚至几个小时。老 ntpd 对这种情况的态度非常保守,默认配置下它不会直接“步进”调时间,而是选择非常缓慢地“微调”,这是为了防止时间回调导致日志、文件时间戳出现乱序。但代价就是,如果你不管它,它可能需要几个小时甚至更久才能真正校准。而 chrony 内置了 makestep 的逻辑,默认配置是“前三次同步中,如果偏移超过 1 秒,就立即步进调整”,首轮同步几乎秒级完成,后续再靠频率补偿做精细校正。
第二个痛点是网络质量问题。ntpd 的滤波算法在网络抖动大、链路繁忙的时候表现比较挣扎,容易过度反应。chrony 在算法层面做了大量改进,它对网络延迟和抖动有更好的容忍度,在跨地域机房、负载较重的内网环境下也能保持相对稳定的同步质量。
3.2 systemd-timesyncd 什么时候够用
Ubuntu/Debian 系的很多发行版默认装的是 systemd-timesyncd。它的优点是足够轻,不额外引入新的服务,配置也简单,省心省事。缺点是功能有限,不支持做时间服务器,也不能精细调整同步策略。如果只是需要在云主机上做“基本对时”,没有高精度要求,不操心异常的抖动和漂移,那它确实够用。但一旦涉及到集群环境、多节点强一致、或者需要自定义时间源和同步策略,还是 chrony 更稳妥。
4. chrony 从安装到平滑落地:完整配置步骤
现在进入正题,给出完整的实施步骤。以下示例基于 Rock Linux 9 / CentOS Stream 环境,Debian/Ubuntu 系列的命令差异我会在对应位置标注。
4.1 确认当前状态和时区
动手之前先做三件基础检查。
第一个检查当前系统的时区和时间偏差:
bash复制timedatectl status
date
正常输出里会有一行 Time zone: Asia/Shanghai (CST, +0800),还有 System clock synchronized: yes/no 和 NTP service: active/inactive。这两个状态很重要,它们会直接告诉你系统当前是否启用了网络时间同步服务。
如果时区不对,先设置时区:
bash复制timedatectl set-timezone Asia/Shanghai
第二个检查 RTC 的时间标准:
bash复制cat /etc/adjtime
正常看到的内容类似下面这样:
code复制0.000000 1723456789 0.000000
0.000000
LOCAL
把最后一行 UTC 或者 LOCAL 记下来。物理机建议统一用 UTC,云服务器一般默认 UTC,不建议随意改动。
第三个检查当前是否有别的 NTP 服务在占用端口:
bash复制ss -ulpn | grep 123
如果 123 端口已经被占用,说明系统里已经有一个时间同步进程在运行,后续要注意停掉它并禁用自启,避免两个服务打架。
4.2 安装并启用 chrony
安装命令在两大包管理体系下分别是:
bash复制# RHEL 系
yum install -y chrony
# Debian/Ubuntu 系
apt install -y chrony
安装完成后先禁用冲突的 systemd-timesyncd:
bash复制systemctl disable --now systemd-timesyncd
然后启动 chronyd 并设置开机自启:
bash复制systemctl enable --now chronyd
systemctl status chronyd
确认服务状态是 active (running) 后,再看一眼端口是否正常监听:
bash复制ss -ulpn | grep 123
ss -ulpn | grep 323
这里解释一下两个端口:123 是 NTP 服务对外提供时间同步的端口,323 是 chronyd 自身使用的端口,供 chronyc 客户端连接查询状态。默认情况下 chronyd 只监听本机的 323 端口,外部访问不了,安全性没有问题。
4.3 配置 NTP 源:选对上游至关重要
chrony 的核心配置文件是 /etc/chrony.conf,里面最重要的就是服务器源的定义。默认配置里可能写的是 pool 2.rocky.pool.ntp.org iburst 一类的公共池,但在国内网络环境下,直接访问境外公共 NTP 池的延迟经常不太稳定,会对同步精度造成影响。
我建议将上游时间源改成国内公共 NTP 服务器,比如:
code复制# 阿里云 NTP
server ntp.aliyun.com iburst
# 腾讯云 NTP
server ntp.tencent.com iburst
# 国家授时中心
server ntp.ntsc.ac.cn iburst
注意 iburst 这个参数,它的作用是让 chronyd 在初始同步时发送一组快速连续请求,加速第一次校时。不加这个参数也行,但首次同步会慢一些。
对于同一机房的服务器,建议再增加一个本机房的“权威时间源”作为第二个 server。比如架构里已经有一台专门做内网 NTP Server 的机器,可以直接写:
code复制server 192.168.10.10 iburst
这样内网同步延迟极低,精度会比直接访问外网 NTP 更高。
4.4 理解关键参数:不是只加 server 就够了
/etc/chrony.conf 里还有几个参数,默认值可以用,但理解它们更重要。
-
driftfile /var/lib/chrony/drift:这个文件记录了本机时钟的频率偏移率。chronyd 每次校准之后,会利用该信息对本地时钟频率做补偿,这样即使网络临时不可用,系统也能保持一个相对稳定的走时精度。不要删除这个文件,否则每次重启后频率校准都要重新学习。 -
makestep 1 3:默认配置通常是makestep 1 3,含义是“如果时间偏移大于 1 秒,在前三次同步时直接步进调整”。这是 chrony 能实现“开机快速对时”的关键。如果去掉这行或者改成makestep 0 -1,chrony 就会退化成 ntpd 那样的保守微调模式。 -
rtcsync:这个参数让 chronyd 定期把系统时间写回 RTC 硬件时钟,避免重启后系统时间又回到旧的硬件时间。保持开启状态即可。 -
allow/deny:如果要让这台机器成为内网时间源,需要配置允许接入的网段,默认情况下 chronyd 不接受外部客户端请求。示例中不放开,因为多数场景下我们只需要做客户端同步。
配置完成后重启服务:
bash复制systemctl restart chronyd
4.5 立即生效:应对“时间差太大”的场景
如果当前时间和正确时间相差很大,即使 chrony 启动,首次校准也需要一点时间。可以手动触发一次强制同步:
bash复制chronyc makestep
这个命令的作用是立即向当前配置的上游时间源发起请求,并根据测量到的偏移执行步进调整。适合在已经确认追平时间不会引发业务问题的情况下使用。
执行后再验证:
bash复制chronyc tracking
timedatectl status
看到 Leap status: Normal、System clock synchronized: yes 就说明同步已经生效。
4.6 开机时间和业务低峰期的错峰处理
默认 chronyd 开机会自动启动并同步时间,但如果有那种“开机后立即判断证书有效性”的服务,建议在业务编排层面加入“等待时间收敛”的检查逻辑。虽然 chrony 的前几次同步很快,但极端情况下偏差几秒内还是可能有,涉及安全校验的业务最好在启动前做一个简单的时间健康检查。
5. 验证与巡检:让时间同步从“配好”变成“持续正常”
配置完成不等于万事大吉。时间同步是一个长期运行的过程,需要定期验证效果。这里给出几个实用检查手段。
5.1 用 chronyc 查看同步质量
最常用的命令是 chronyc tracking,输出里重点关注几个字段:
| 字段 | 含义 | 关注点 |
|---|---|---|
| Stratum | 当前时间源的层级,越接近 1 越准 | 一般客户端在 2~3 之间比较正常 |
| Ref ID | 当前使用的上游时间源的标识 | 确认不是你乱配的源 |
| Last offset | 上一次校正的系统时间偏移量 | 绝对值越小越好,毫秒级正常 |
| RMS offset | 最近一段时间偏移的均方根 | 衡量整体稳定性,波动大说明网络不稳定 |
| System time | 系统时间相对参考时间的偏差 | 长期稳定在毫秒级正常 |
想查看当前连接的所有时间源,可以用:
bash复制chronyc sources -v
输出里 ^* 开头表示当前正在使用的源,^- 表示可以被使用但不优先,^? 表示不可达或不可用。看到 ^* 就能确认同步链路是通的。
5.2 用 timedatectl 检查综合状态
timedatectl 是一条非常综合的命令,日常巡检时一眼就能看出所有关键信息:
bash复制timedatectl status
重点关注三行:
NTP service: active:时间同步服务是否在运行。System clock synchronized: yes:系统时间是否已经同步过。Time zone: Asia/Shanghai:时区设置是否符合规范。
5.3 如何发现“偷偷失效”的同步
时间同步服务最常见的故障不是进程挂掉,而是“进程在跑,但同步实际已经中断”。比如上游 NTP 服务器不可达、防火墙把 123 端口封了、DNS 解析失败,这些情况 systemctl status chronyd 看过去都是正常的,但时间会慢慢开始漂移。
所以定期巡检不要只看服务状态,还要看时间偏移量。建议在巡检脚本里加一条判断逻辑:连续多次检查 chronyc tracking 的 RMS offset,如果超过几百毫秒,就触发告警。对于普通的内部业务服务器,时间偏差控制在几十毫秒以内是合理的;对于交易、证券、数据库强一致场景,需要更严格的标准。
5.4 内网 NTP Server 的简单搭建思路
如果集群规模比较大,比如几十台机器以上,全部直接连公网 NTP 源不是不行,但建议内网先搭一个本地 NTP Server,其他机器统一指向它。好处是显而易见的:
- 内网延迟低,同步精度更高。
- 减少对公网 NTP 服务的依赖,公网网络抖动不会波及全部机器。
- 便于统一管理,时间源变更只需要改一台。
搭建方式很简单:在一台稳定运行的机器上安装 chrony,配置里上游指向公网 NTP 源,然后在配置里加上允许内网网段访问:
code复制allow 192.168.0.0/16
其他客户端把 server 指向这台内网服务器的 IP,配置方式完全一样。稳妥起见,内网 NTP Server 最好用两台做冗余,客户端配置两个 server,实现自动切换。
6. 开启时钟同步后,我遇到过的几个“翻车现场”
最后分享几个真实踩过的坑,希望能帮你省掉无意义的排查时间。
6.1 坑一:systemd-timesyncd 和 chronyd 互相打架
在 Ubuntu 20.04 上安装 chrony 时,如果没主动禁用 systemd-timesyncd,启动 chronyd 后可能看到服务状态正常,但时间就是不动。原因是 systemd-timesyncd 也在运行,两个进程都在监听 123 端口,chrony 请求到的上游地址可能被系统转发到另外那个服务上。
我碰到过一次 Ubuntu 服务器时间一直不对,systemd-timesyncd 显示 active,时间却还是偏。排查半天才发现 chrony 和 systemd-timesyncd 同时装了,跑在 123 端口的是后者。后来统一只保留一个。解决方法是装好 chrony 后立刻执行:
bash复制systemctl disable --now systemd-timesyncd
6.2 坑二:云平台默认没有放开 UDP 123
在云服务器上配置好 chrony 后发现 chronyc sources -v 对应的源显示 ^?,大概率是安全组或者防火墙没有放行 UDP 123 端口。比起纠结服务端配置,先自查本地防火墙和云平台安全组。
本地防火墙放行命令:
bash复制firewall-cmd --permanent --add-service=ntp
firewall-cmd --reload
如果用的是 ufw:
bash复制ufw allow 123/udp
6.3 坑三:改时间导致服务端证书校验瞬间失败
在一次时间校准中,因为数据库服务器时间偏差太大,我用 chronyc makestep 手动步进调整。步进一瞬间时间跳了好几分钟,结果数据库连接池里的连接全是无效的,所有正在跑的请求一股脑报错。后来对此类操作我总结出一个原则:
对于线上核心业务,时间偏差超过 1 分钟的调整,必须先和业务团队确认,尽量安排在业务低峰期。
如果偏差不大,可以让 chrony 用默认的微调策略慢慢追平,这种调整不会引起时间跳变。只有偏差较大且业务允许的情况下,才执行手动步进。
6.4 坑四:硬件时间没同步,重启后时间又回去了
配置好 chrony 并运行一段时间后,用 timedatectl 看一切正常。但是服务器重启后,时间又回到很久之前,看起来就像同步根本没生效。这种情况绝大多数是 rtcsync 没开,或者 RTC 的时间标准不对。
确认 /etc/chrony.conf 里有 rtcsync 参数,然后重启 chronyd。重启后可以主动把当前系统时间写回 RTC:
bash复制hwclock --systohc
再执行 hwclock --show 确认 RTC 时间正确。这样重启以后,系统从 RTC 读取的时间就是最新的,不会出现“同步完又回弹”的诡异现象。
6.5 坑五:虚拟机的“无效时钟源”
在虚拟化环境里,如果发现时间漂移异常,甚至出现偶尔回跳的现象,优先检查时钟源。确认当前时钟源:
bash复制cat /sys/devices/system/clocksource/clocksource0/current_clocksource
如果是 KVM 虚拟化,期望看到 kvm-clock。如果看到的是 tsc 或 hpet,可以尝试把 kvm-clock 设为当前时钟源:
bash复制echo kvm-clock > /sys/devices/system/clocksource/clocksource0/current_clocksource
但注意这个命令重启后不保留,建议在启动参数或者模块加载层面固定下来。这个问题的根因是 CPU 热迁移、节能策略等机制对 TSC 的时钟连续性造成干扰,使用半虚拟化时钟可以让 guest 直接跟宿主机的时间基准对齐。
6.6 坑六:NTP 源不可达时要有“备胎”
只配一个上游时间源也是很多人的习惯,其实这是个风险。上游 NTP 服务器一旦不可达或者响应超时,chrony 就没有可用的参照物,时间会继续漂移。生产环境我建议至少配置两个不同的上游源,并且它们最好来自不同的服务商,避免“鸡蛋装在一个篮子里”。同样的道理,如果内网有条件,内网主备 NTP Server 的稳定性会远高于直接依赖公网源。
7. 最后再分享一个小技巧
如果企业内部有多套环境,比如开发、测试、生产,时间源的配置一定要统一。不要开发用阿里云 NTP,测试用腾讯云 NTP,生产又用自建服务器,虽然时间理论上是同一时刻,但不同源之间本身存在几十毫秒的微小偏差。对于涉及多环境联调、日志统一采集分析的场景,这种偏差会在数据链路上放大。所有环境使用同一套时间源,是成本最低、收益最直接的规范。
我在实际业务中还有一个习惯:每个季度对所有服务器做一次时间漂移巡检,用脚本批量执行 chronyc tracking 并采集 RMS offset,超过阈值的机器单独处理。这项工作很小,但往往能让很多“隐性故障”在发生之前就被拦下来。时间同步这件事,配置十分钟,但真正让它持久稳定运行,靠的是持续的检查和应对策略。希望这篇文章能帮你少走一些弯路。
