1. 为什么还要折腾时间同步?先聊聊NTP这点事
很多线上系统的监控告警、日志审计、分布式事务协作,全都依赖一个底层前提:各节点的时间得基本一致。如果某台机器的时间慢了五分钟,日志时间戳对不上,排查问题时能把你绕进死胡同。更麻烦的是某些服务之间做签名校验或票据认证,时间偏差超过阈值会直接拒绝请求。这就引出了NTP(Network Time Protocol),它让设备从远程时间服务器同步标准时间,把误差从分钟级压到毫秒甚至微秒级。
前面这些属于背景铺垫,真正让我决定弃用旧方案、全面转向chrony的,是一次生产事故。当时线上某台数据库主机的时钟漂移严重,峰值偏差达到了几百毫秒,业务高峰期出现了竞态条件导致的脏数据。我检查后发现,那台机器还在用传统的ntpd做同步,而它面对大跨度时钟跳变时的修正策略偏保守,收敛慢,而且配置和维护方式也比较重。这时候我把目光放到了chrony身上,它是NTP协议的另一种实现,设计目标更现代,同步速度快、精度高,对网络抖动和丢包的容忍度也更好。不少发行版已经默认用它替代了ntpd。这篇文章就从配置出发,把我在实际环境中踩过的坑、验证过的参数,一并梳理出来。
先说清楚目标人群:如果你在维护几十台以内的小集群,或者家里有个装着Linux的服务器折腾各类自建服务,完全可以用chrony替换掉系统默认的时间同步方案。这篇内容不止讲配置命令,会把参数含义、选型逻辑、验证手法都拆开讲,确保你照着做就能得到稳定可靠的同步结果。
2. 内核里的时间体系与chrony的同步机制
2.1 时钟来源、时区与时间标准,先分清楚
动手配置chrony之前,必须把几个容易混淆的概念理顺:硬件时钟(RTC)、系统时钟、时区、UTC。硬件时钟是主板上那块靠电池维持的独立计时芯片,断电后依然走时;系统时钟是操作系统内核维护的软件计数,开机时从硬件时钟读取初值,之后靠CPU定时器中断持续更新。NTP同步的对象是系统时钟,而硬件时钟默认不会自动跟着改动,这导致很多人重启机器后又发现时间不对了。
另一个问题是时区。NTP协议传输的永远是UTC标准时间,不带时区偏移。服务器上如果用了Asia/Shanghai这类时区,chrony在显示本地时间时会自动做偏移换算,但实际同步过程始终基于UTC,这是设计上的重要原则。所以配置里出现的iburst、poll这些选项,和时区没有关系。你可以改动/etc/timezone与/etc/localtime来设置显示时区,但千万别去改/etc/chrony/chrony.conf里的服务器地址来“纠正”时差,那方向就错了。
底层还有一个“时间标准”的问题。新装系统时,安装器通常默认RTC使用UTC标准,也有系统默认让RTC走本地时间。双系统用户如果发现Windows和Linux时间老差八小时,十有八九是两边对RTC的基准理解不一致。Linux把RTC当作UTC处理,Windows把RTC当作本地时间处理,于是来回切换系统就会导致时间错乱。这个问题和chrony配置无关,但如果你要彻底管好时间同步,这句提醒值得记下来。
2.2 chrony为什么比ntpd好:测量模型与修正策略
chrony的同步机制和ntpd差异很大。ntpd对时钟频率漂移的修正很保守,初始同步可能要花几分钟到十几分钟,之后才进入稳定跟踪状态。chrony则用了一套更快的学习算法,它能在几十秒内完成粗略同步,并通过周期性测量持续修正本机时钟的频率偏差。
举个例子,如果系统时钟因为主板晶振老化或温度变化,每天稳定慢掉0.5秒,chrony会记录这个频率误差并做“预补偿”,而不只是反复校时。这个特性对无法频繁访问外部时间服务器的离线环境尤其有用。即使断网,chrony也能按之前学习到的漂移率继续维持相对准确的时间。这个能力来自设置文件中的driftfile,它会把时钟频率偏差保存到指定文件,重启后直接加载,不需要重新学习。
还有一点就是chrony对网络环境的适应能力。默认UDP 123端口与服务器通信时,如果某次响应超时或者迟到,chrony会根据RTT(往返时延)加权评估样本质量,对抖动大的服务器自动降低权重。这个机制在跨运营商网络的时候价值很明显,家里的宽带访问公共时间服务器经常会出现几十毫秒的延迟波动,ntpd会把这种波动算进时间样本里导致误差变大,chrony的鲁棒性明显好一些。
3. 配置前的准备:安装、确认与服务管理
3.1 安装与系统层面的前置检查
大多数现代系统的软件源里都有chrony这个包,安装过程很简单。Debian系、Ubuntu、CentOS都能直接安装:
# Debian / Ubuntu sudo apt update sudo apt install chrony # RHEL / CentOS / Rocky sudo yum install chrony装完之后先确认一下发行版默认的时间同步服务是否冲突。很多系统预装的是systemd-timesyncd,它和chrony都通过同一个系统时钟操作,两者同时运行会发生竞争。你需要禁用systemd-timesyncd,再启用chrony服务:
sudo systemctl stop systemd-timesyncd sudo systemctl disable systemd-timesyncd sudo systemctl enable --now chronyd如果系统里还留着旧版ntpd,也要一并停掉。否则两个NTP守护进程同时修改系统时钟,结果不是你修我、就是我修你,最后两个都失灵。检查命令也很直观:
sudo systemctl status chronyd sudo chronyc trackingchronyc tracking能输出当前同步状态。重点看Leap status是否变成Normal,Stratum是不是已经是合理层级,System time那一项代表本机相对参考时间的偏差值。看到这些为正常状态,说明chronyd已经在正常工作。
3.2 时间服务器选型:公共服务器与内网自建
时间服务器选型是个值得单独说的环节。公共NTP服务器很多,但和你的网络距离、运营商链路、使用策略都有关。我建议至少配置三台以上,分散到不同机构或者不同地域,避免单一依赖。国内常用的是各高校或研究机构提供的公共NTP地址,不要全部写同一家,否则上游抖动会直接影响你的同步质量。
小规模集群里更常见的做法是自建内网NTP服务器。找一台网络稳定、常年开机的节点,让它同步公共服务器,然后作为内部其他节点的时间源。其他机器只需指向这台内网服务器,省去大量对外请求,同时能降低延迟,因为局域网内的RTT可能只有几毫秒,比公网路径稳得多。这种分层结构在生产环境里属于标准操作。
# 内网客户端指向自建服务端的配置示例 server 192.168.10.10 iburst server 192.168.10.20 iburst上面这是客户端配置,如果你是那台内网主时间服务器,它需要同时完成两件事:对外同步公共服务器,对内提供服务。外部同步来源写在pool或server里,对内提供服务的监听地址默认就是0.0.0.0:123,完全不用额外改。关键是配合防火墙规则,UDP 123端口允许内网网段访问即可。
4. 配置文件逐行拆解:每个参数为什么要这样设
chrony的配置文件路径通常是/etc/chrony/chrony.conf。不同发行版预装配置差异不小,有些默认配置已经能直接用,但要得到理想效果,建议手工过一遍。我下面贴出一份我常用的配置,并逐部分解释参数含义,重点说明哪些是生产环境必须考虑的,哪些是新手容易忽略的。
# /etc/chrony/chrony.conf pool pool.ntp.org iburst server ntp.aliyun.com iburst server ntp1.aliyun.com iburst server time1.cloud.tencent.com iburst driftfile /var/lib/chrony/drift makestep 1 3 rtcsync allow 192.168.0.0/16 local stratum 10 logdir /var/log/chrony log measurements statistics tracking第一块是时间源配置。pool和server的区别在于,pool会从域名解析得到一个地址集合,chrony会自动管理这些多个服务器,而server只针对单台服务器。建议生产环境里至少保留一个pool作为兜底,再加上几个指定的server,这样无论公共解析怎么变化,总会有可用源。iburst参数会让chrony在启动后连续发送八个快速请求包,加快首次同步的收敛速度,这是默认要点开的选项。
driftfile参数保存时钟频率误差,路径需要确保进程有写权限。makestep 1 3这个参数特别重要,它表示前三次时间测量中,如果检测到系统时间与参考时间的偏差超过1秒,就立刻以跳跃方式修正系统时钟,而不是靠缓慢的 slew 逐步逼近期望值。很多刚接触chrony的人发现启动后时间没变过来,原因就是默认配置走了缓慢校准路线,偏差好几秒却要用很久才追平。内网环境或者刚开机的机器出现大偏差,这个参数能帮你快速恢复正常。
rtcsync让chrony定期把系统时间写回硬件时钟,确保重启后时间不会回退太多。这台机器如果没有特殊原因,保持开启就好。makestep解决的是启动瞬间的突变问题,rtcsync解决的是断电重启后的持久化问题,两者配合比较完善。
allow和local stratum通常只在服务器端配置。allow指定允许哪些网段来请求时间同步,比如192.168.0.0/16表示整个私网网段都可以访问。local stratum 10是给这台服务器在无法访问上游外部时间源时使用的兜底策略:即使它失去对外连接,也依然会以10层身份继续为内网提供服务,不会因为源丢失就拒绝响应。这个设计对离线环境非常关键。需要注意的是local stratum的数字大小代表时钟的“信任级别”,数字越大优先级越低。所以内网客户端默认会优先选择层级更小的时间源,而不会出现同步环路。
5. 实操:完整配置一台内网时间源服务器
5.1 服务器端配置与验证步骤
直接照搬你的场景来走一遍。假设你有一台常年运行的服务器,IP是192.168.10.10,操作系统是Debian系,现在要让这台机器成为内网时间服务器。我会从安装到验证,逐步说明实际操作。
第一步,安装完成后直接编辑配置文件。把供应商自带的公共服务器去掉,换成你信任的公共NTP源,也可以保留系统默认的pool源。重点要加上allow 192.168.0.0/16,否则客户端无法访问UDP 123端口。下面是我实际使用的一份服务端配置:
# 服务端 192.168.10.10 的 chrony.conf pool pool.ntp.org iburst server ntp.aliyun.com iburst server ntp1.aliyun.com iburst driftfile /var/lib/chrony/drift makestep 1 3 rtcsync allow 192.168.0.0/16 local stratum 10 bindaddress 0.0.0.0 port 123第二步重启服务并等待同步。启动后建议等两三分钟再做验证,给chrony留出完成初次同步的时间:
sudo systemctl restart chronyd sleep 120 chronyc tracking这时会输出类似这样的文本:
Reference ID : 203.107.6.88 (ntp.aliyun.com) Stratum : 2 Ref time (UTC) : Wed May 15 10:11:22 2024 System time : 0.000000031 seconds fast of NTP time Last offset : -0.000008232 seconds RMS offset : 0.000432882 seconds Frequency : 2.087 ppm fast Residual freq : -0.001 ppm Skew : 0.038 ppm Root delay : 0.053022 seconds Root dispersion : 0.003115 seconds重点字段看两个:Stratum等于2表示你从第1层公共服务器同步到了时间,可信度足够;System time显示的偏差越小越好,如果这项是分钟级数以上说明配置有问题。Frequency表示本机时钟相对标准时间的漂移率,单位是ppm(百万分之一)。稳定在个位数ppm是正常的,如果数值很大或者一直跳动,说明硬件时钟质量比较差或者温度影响严重。
第三步,显式测试本地端口与防火墙规则。用chronyc sources -v可以查看每个时间源的详细状态,^*开头的行表示该源已被选中并作为参考源,^+表示该源可作为备用源。如果全是^?或^x,说明同步有问题。查看端口监听状态用:
sudo ss -ulpn | grep 123看到0.0.0.0:123处于LISTEN,并且进程名是chronyd,说明服务端正常监听。防火墙方面,Debian系默认可能没有额外规则,但如果你开启过ufw,要记得放行:
sudo ufw allow 123/udp5.2 客户端配置与联动验证
内网其他节点配置就只需一行,再加上可选的本地漂移记录路径:
# 客户端 /etc/chrony/chrony.conf server 192.168.10.10 iburst driftfile /var/lib/chrony/drift makestep 1 3 rtcsync注意客户端的配置里不需要pool,也不需要allow,它只是一个请求方。iburst依旧建议打开,方便客户端启动后快速完成初始同步。
客户端完成了配置后,同样用chronyc sources -v检查源状态。正常会出现类似:
^* 192.168.10.10 3 6 377 22 -559us[-1899us] +/- 2389us关键看^*标识和偏移量。如果偏移是毫秒级,但能稳定收敛,可以接受;如果一直跳动到几十毫秒甚至几百毫秒,先检查服务端的allow配置,再检查网络丢包。内网环境下出现毫秒级跳动的常见原因,多半是交换机或宿主机CPU软中断处理不及时,你还可以尝试把客户端的轮询间隔加大,减少同步压力。
验证完单台客户端没问题后,建议找一台干净机器做端到端的时间偏差对比。在客户端机器上直接跑chronyc tracking,观察System time字段是否贴着0附近。另外可以手动对比:
chronyc -c 'tracking' | awk -F, '{print $4}'这样输出的就是当前绝对偏差秒数。我通常要求生产环境里偏差不超过20毫秒,如果超过这个值,多半是同步频率不够或者网络路径问题。
6. 同步源选择与架构分层:为什么不要全员直连公网
6.1 公共NTP源的取舍标准
公共NTP源选择其实是门学问。全球有大量开放NTP服务器,但不是所有都适合生产使用。我选源的标准有四个,你可以直接拿来参考:
第一,来源可靠。使用高校、研究机构或知名云厂商提供的服务,稳定性有保证。有一些私人维护的服务器可能某天突然停止服务,这会影响你排查问题时的思路。
第二,距离近。在同一运营商网络内,RTT越短越好。实测下来,国内云服务器访问几大云厂商的NTP地址,往返延迟都在几毫秒到十几毫秒之间,效果非常好。面向海外业务的服务器,适当选择海外公共NTP源更合适。
第三,数量适中有冗余。太少容易单点故障,过多会导致本地时间源选择算法要花时间挑选、容易受弱源干扰。一般维持三到五个源已经足够。
第四,尽量不要都指向同一个上游。比如全部指向同一个机构,如果对方NTP集群出现问题,你的所有源可能同时掉线。分散到两三家以上更安全。
6.2 分层设计:内网NTP架构的推荐模式
生产环境如果节点比较多,我不建议每个节点都直接请求公网NTP源。原因很直接:大量节点同时在公网请求,不仅延迟不可控,还容易受链路波动影响,也可能被上游限速。我更推荐分层的模式。
按规模来排的话,可以这样设计:
- 几十台规模:一台主时间服务器对外同步,其他节点全部指向这台。备选方案是加一台冷备,主备配置相同,客户端配置里把两台都写上。
- 上百到几百台规模:两台或三台时间服务器组成同步源池,各自对外同步,同时定期互相同步一次,消除相互之间的微小漂移。客户端把这几台全部写入
server列表。 - 跨机房、跨地域场景:每个机房至少保留一台本地时间服务器,各机房的时间服务器之间再做互相校准。客户端一律只访问本机房的服务器,避免跨专线的高延迟。
分层架构的好处在于把故障域隔离。如果你只有客户端直连公网,一条公网链路的波动会影响一组机器;如果有了分层,只有最上层的时间服务器受影响,底下所有节点不受牵连。而且内网同步延迟低,最终精度反而更好。
6.3 内网时间源失去外网时怎么兜底
这个场景可能不多,但必须提前准备好。内网时间服务器如果彻底断外网,按默认配置,它会因为无法访问外部时间源而拒绝为内网其他节点提供参考时间,这会连累整条同步链。解决办法就是前面提到的local stratum:
local stratum 10这个参数的意思是,即使这台服务器失去了所有同步源,它依然可以作为10层时间源继续为内网节点提供时间。但要注意,它提供的时间是基于本机时钟的,没有任何上游校准,时间会随时间累积漂移。这个设计只是为了兜底,不应长期依赖。一旦公网恢复,chronyd会自动重新找到上游源并恢复第2层身份,无需人工干预。
不过这种“无源兜底”状态,我建议配合告警。可以用一行简单的脚本检查:
chronyc tracking | grep -E "Reference ID" | grep -v "N\/A"如果输出为空,说明Reference ID是N/A,当前处于无源状态。配合定时任务或者监控系统就能第一时间发现异常。
7. 验证同步效果与故障排查:那些年我踩过的坑
7.1 用chronyc命令判断同步健康状况
配置完成后最核心的验证工具就是chronyc。除了tracking和sources -v,chronyc clients也很有用,它能查看当前有多少个客户端在向你这台服务器请求同步。如果发现大量外部IP,先确认allow范围是否设得太宽。
我还习惯用chronyc sourcestats来看每个源的统计样本情况。输出里的RMS offset反映最近测量的整体偏差,越小越好;Std Dev表示样本标准差,如果这个值持续增大,说明该源网络抖动大,值得排查。
还有一条相当实用的经验:在客户端做一次模拟并发,强制chronyd进行一次立即同步:
sudo chronyc makestep这个命令能让chronyd立刻执行一次阶梯跳跃,适合你在测试时快速看到效果。生产环境一般不需要手动执行,除非你确认时间偏差已经大到无法容忍。
7.2 常见时间不同步问题排查速查表
实际操作中总会出现各种奇奇怪怪的问题,我整理了一张高频问题速查表,基本覆盖我遇到过的80%场景:
| 症状 | 可能原因 | 排查命令 / 方案 |
|---|---|---|
sources -v全部显示^?或^x | 防火墙屏蔽UDP 123;上游源不可达 | 检查ss监听结果、防火墙规则、网络连通性 |
tracking显示Stratum为10 | 处于无源兜底状态,联系不上所有上游 | 确认外网连通性,检查pool配置域名能否解析 |
| 系统时间始终不跳变 | makestep未合理配置,偏差小于slew阈值 | 检查makestep参数,必要时手动执行chronyc makestep |
| 同步正常但偏差较大 | 网络抖动严重,或时间源离得太远 | 换距离更近的源,用sourcestats观察标准差 |
| 重启后时间回退 | 未开启rtcsync,或者RTC基准不对 | 开启rtcsync,检查RTC时区标准 |
| chronyd服务启动失败 | driftfile路径不可写;端口被占用 | 查看journalctl -u chronyd日志,修正目录权限 |
这张表里的每一条,我都实际碰到过。尤其是重启后时间回退这个问题,最容易被人忽视——配置本身没问题,服务也正常运行,但一断电就现原形。
7.3 日志解读与排查思路
日志是保底手段。chrony的日志默认配置在/etc/chrony/chrony.conf里通过log指令控制,常用的配置是:
log measurements statistics tracking其中statistics会记录每次时钟调整的统计信息,measurements记录与每个源之间的原始测量数据。日志输出到/var/log/chrony目录,文件名分别是measurements.log、statistics.log、tracking.log。
看一份statistics.log的实际记录时,重点关注adj这一列,它表示每次调整系统时钟的具体数值。如果多数adj值是正一个大值然后又负一个大值来回摆,说明你选的源本身存在严重的抖动,或者多个源之间不一致导致chrony反复修正。这种情况应该立即换源,硬件时钟本身可能没问题,但网络质量决定了最终同步质量。
日志里的measurements如果出现大段的Timeout,说明UDP 123端口丢包严重。可以先从ping和traceroute下手确认网络路径质量,同时检查上游NTP服务器是否有限流。公共NTP源被高频请求时有可能会临时断开响应,这是正常现象。
8. 生产环境维护建议:配置完成只是开始
8.1 定期同步校验与告警脚本
配置完成后,建议用监控系统或者定时任务持续校验同步状态,不要等出问题再介入。我自己通常会挂一个简单的指标采集脚本,每五分钟执行一次,把System time偏差值推进监控系统,设一个50毫秒的告警阈值,剥离开细节直接看结果。
一个非常轻量的做法是直接在cron里加:
*/5 * * * * /usr/bin/chronyc tracking | /usr/bin/grep -E "System time" >> /var/log/chrony-check.log但更推荐的做法是让监控系统直接抓取chronyc的输出。比如在自定义的文本采集器里执行:
chronyc -c tracking | awk -F, '{print "system_time_offset " $4}'这样得到的指标可以关联到每台机器的标签。任何一台机器出现时间偏差,监控面板马上会体现出来,不用等用户投诉日志时间不对再排查。
8.2 更新上游NTP源时的注意事项
上游NTP源不是永远一成不变的。有时候你使用的公共池会用新的域名替换旧的,有时候发现某个源质量下降需要替换。修改配置时注意两点:
第一,千万别一次性把所有旧源都删掉再换上新的,应该先加新源、观察一段时间,确认同步稳定后再逐步移除旧源。这样即使新源有问题,chrony还能继续使用旧源保持同步。
第二,更换源之后,记得检查driftfile里的频率估计值是否会受到新旧源跨度的冲击。如果是从国内源切换到海外源,RTT差异很大,tracking里的Frequency数值会出现暂时波动,这是正常现象。等几个小时后,频率估计会重新收敛,不用干预。
8.3 和硬件RTC的协同问题
前面提到rtcsync会让chronyd把系统时间写回硬件时钟,但默认的写入频率其实不高。如果你经常断电重启,可以考虑在系统层面额外同步一次硬件时钟:
sudo hwclock --systohc这条命令手动把系统时间写入RTC。也可以反过来校验RTC和系统时间的偏差:
sudo hwclock --show如果发现RTC偏差特别大,可能主板电池快耗尽了。锂电池一般能撑几年甚至十年,但到了一定年限,你会看到每次开机时间都回到某个固定年份,这是电池电量耗尽的典型信号。趁早换电池,不然你配置得再好,断电一次就打回原形。
9. 一些细节上的补充说明
9.1 chrony不监听端口时的排查顺序
如果你在客户端访问服务器超时,先不要怀疑chrony配置。我碰到过一次情况是云平台安全组把UDP 123给放行了,但系统内部firewalld默认区域没放行,导致怎么都连不上。排查顺序建议是:
- 确认服务端进程监听状态:
ss -ulpn | grep 123 - 确认系统防火墙规则:
iptables -L -n -v | grep 123或firewall-cmd --list-ports - 确认云平台安全组规则(如果托管在云上)
- 用客户端手动跑一次
chronyc sources -v观察错误信息
如果跳到第4步才测试,很可能浪费很多时间在无关地方。
9.2 不推荐把所有节点都设置成同一台源并开启本地服务
有些新手图省事,会把每台机器都配上local stratum然后互相指向。一旦有两台机器同时开了local stratum,而且各自的上游源临时失联,就会发生“互相参考”的环路状态,时间基准无法收敛。如果你只是想为内网提供时间服务,确定合理的层级,标注清楚谁是服务器谁是客户端,是最稳妥的做法。
9.3 关于公网NTP池的使用礼仪
很多教程都会推荐pool.ntp.org,但如果你有大量服务器,不建议所有节点全部指向同一个公共池。公共池的负载均衡机制会自动把请求分散到多个服务器,但它并不是无限容量。合理的做法是:少数几台服务器访问公共池,其余节点访问这几台内网服务器,既减轻了公共池的压力,也保证自身的同步质量。
10. 顺手把监控环境的时区统一问题解决一下
最后说一个和chrony配置配套的实际衍生问题。时间同步不仅指UTC,还牵扯到每个服务读取时间的展示方式。
很多应用默认读取系统本地时间,系统时区如果五花八门,有的是UTC、有的是Asia/Shanghai,那么记录到日志里的时间戳就对不上。生产环境建议所有服务器统一使用UTC,展示层单独做时区转换。这条建议听起来简单,实际团队协作时会遇到很多阻力,因为运维同学习惯看本地时间,开发同学习惯看UTC。与其争论,不如所有服务器统一UTC,日志里强制带时区偏移字段,这样任何人在任何地方看都能换算。
chrony把底层的“时间基准”修好了,上面的时区问题才能有干净的排查空间。如果时间基准不准,时区转换再正确也无济于事。这也是我把这篇文章的重点放在同步机制和验证流程上的原因——它虽然基础,却决定了整个系统的信息底座。
配置chrony这个事,看着简单,真正实现稳定可靠同步,还是有不少门道要摸索。我在这上面趟过的坑,单列出来足够再写一篇故障排查实录了。核心原则始终就一条:先确认同步源可达、再确认服务运行、最后用chronyc验证偏差值,沿着这条链路走,大多数问题都能定位到确切环节。