1. 时间不同步这件事,比你想的要命得多很多人觉得时间同步是个小问题,差个几秒又不会怎样。我刚开始做运维的时候也这么想,直到有一次排查一个分布式任务调度的问题,三个节点上的日志时间戳对不上,A节点显示任务10:00:03开始,B节点显示10:00:07才收到,C节点干脆显示09:59:58就完成了。三个日志摆在一起,根本没法拼出完整的调用链路,排查了整整一个下午才发现是各台机器的系统时间本身就差了十几秒。这件事之后我才真正重视起时间同步。不管你是管几台服务器的运维,还是家里有几台电脑需要协同工作,甚至是实验室里一堆设备要统一记录数据,时间不一致带来的问题都是隐性的、连锁的。日志分析错乱只是最表面的影响,更深层的问题包括:数据库主从复制延迟判断失准、证书校验失败、定时任务重复执行或漏执行、文件版本比对混乱、监控告警时间线错位等等。这篇内容就是把我这些年配置时间同步的经验完整梳理一遍。核心思路分两条线:有外网环境的简单方案和纯内网环境的进阶方案。前者适合大多数办公场景和家庭网络,几分钟就能搞定;后者适合隔离网络、实验室内网、生产环境不出网的情况,需要自己搭一台时间服务器。两条线我都会给出完整的操作步骤、参数解释和踩坑经验,不管你是刚接触这块的新手还是踩过几次坑的老手,都能直接抄作业。2. 先搞清楚时间同步的基本逻辑2.1 NTP到底在同步什么NTP的全称是Network Time Protocol,翻译过来叫网络时间协议。它的核心工作就是让一台机器的时间跟另一台机器(或者一组机器)的时间对齐。听起来简单,但里面有几个概念必须先理清楚,不然后面配置的时候容易犯迷糊。第一个概念是时间源(Time Source)。你的电脑要同步时间,总得有个参照物。这个参照物可以是互联网上的公共时间服务器,也可以是你内网里自己搭的一台服务器。时间源本身也有层级概念,叫Stratum(层级)。Stratum 0是原子钟、GPS时钟这类物理设备,Stratum 1是直接连接Stratum 0的服务器,Stratum 2是从Stratum 1同步的服务器,以此类推。层级数字越小越接近源头,精度越高。我们普通用户能接触到的公共NTP服务器基本都是Stratum 1或Stratum 2。第二个概念是时间偏差(Offset)和网络延迟(Delay)。NTP协议在工作的时候,客户端会跟服务器来回交换几个数据包,通过计算发送和接收的时间戳,算出两个东西:你的机器跟服务器的时间差了多少(Offset),以及数据包在网络上来回花了多少时间(Delay)。真正同步的时候,NTP会把网络延迟考虑进去做补偿,所以即使网络有几十毫秒的延迟,最终同步精度也能做到毫秒级别。第三个概念是时钟漂移(Clock Drift)。每台电脑主板上的晶振频率都有微小差异,有的走得快一点,有的走得慢一点。这就导致即使你这一刻把时间校准了,过一段时间它又会慢慢偏掉。普通电脑一天漂移几秒到几十秒都很正常。所以时间同步不是做一次就完事了,得让它持续运行,定期校准。2.2 为什么分“有外网”和“纯内网”两条路这个问题的本质是:你能不能直接访问互联网上的时间服务器。有外网的情况下,你的电脑可以直接跟公共NTP服务器通信,比如各大机构提供的标准时间服务。配置起来非常简单,改一个配置文件或者点几下鼠标就行。但这里有个细节:如果你的网络环境里有防火墙或者代理,UDP 123端口(NTP使用的端口)可能被拦截,这时候就需要换方案或者调整防火墙规则。纯内网的情况下,所有机器都出不了网,没法直接跟外面的时间服务器通信。这时候就必须在内网里找一台机器当“时间源”,让它先跟外部同步(如果它能出网的话),或者直接以它的硬件时钟为基准,然后其他机器都跟这台机器同步。如果整个内网完全隔离,连一台能出网的机器都没有,那就需要这台时间服务器用本机的硬件时钟作为基准,虽然精度不如跟外部同步,但在内网范围内保持一致是没问题的。注意:很多人会忽略一点——即使有外网,如果内网机器数量多,让每台机器都去访问外部NTP服务器也不是好做法。一来增加外部依赖,二来外部服务器可能对你的频繁请求做限制。更好的方式是内网搭一台时间服务器,统一对外同步,然后内网其他机器都跟它同步。3. 有外网环境:十分钟搞定的简单方案3.1 Windows系统的时间同步设置Windows系统自带时间同步功能,图形界面就能操作。右下角任务栏找到时间显示,右键选择“调整日期/时间”,在弹出的设置窗口里找到“同步你的时钟”或者“Internet时间”选项卡。点击“更改设置”,勾选“与Internet时间服务器同步”,然后在服务器地址栏里填入一个可用的NTP服务器地址。默认的服务器地址是time.windows.com,但这个服务器在国内的连通性时好时坏,有时候同步会失败。我一般会换成下面这几个里面挑一个:ntp.aliyun.com— 国内访问速度快,稳定性好cn.pool.ntp.org— 国内NTP池项目,自动分配最近的服务器time.apple.com— 苹果的时间服务器,国内访问也还行ntp.tencent.com— 腾讯的时间服务器选好之后点击“立即更新”,等几秒钟就会提示同步成功。如果提示失败,大概率是UDP 123端口被防火墙拦了,或者你选的服务器暂时不可用,换一个再试。命令行方式更适合批量操作或者写脚本。以管理员身份打开命令提示符或者PowerShell,用w32tm命令来配置:# 查看当前时间同步状态 w32tm /query /status # 指定时间服务器并同步 w32tm /config /manualpeerlist:"ntp.aliyun.com,ntp.tencent.com" /syncfromflags:manual /reliable:yes /update # 重启时间服务 net stop w32time && net start w32time # 立即触发同步 w32tm /resync # 查看同步结果 w32tm /query /status这里解释一下几个参数:/manualpeerlist后面跟的是时间服务器地址列表,多个地址用逗号分隔,Windows会从中选一个可用的;/syncfromflags:manual表示使用手动指定的服务器列表;/reliable:yes表示把这台机器标记为可靠时间源(如果是普通客户端,这个参数其实可加可不加);/update是让配置立即生效。实测下来,w32tm /resync有时候会报“服务尚未启动”之类的错误,这时候先执行net start w32time把服务拉起来再试。另外Windows的时间同步默认间隔是一周一次,对于时间精度要求高的场景,这个频率太低了。可以通过注册表调整同步间隔:# 修改同步频率为每小时一次(3600秒) reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient" /v SpecialPollInterval /t REG_DWORD /d 3600 /f # 重启时间服务使配置生效 net stop w32time && net start w32time3.2 Linux系统的时间同步设置Linux这边现在主流用的是chrony,老一点的系统可能还在用ntpd。我建议不管新老系统,能用chrony就用chrony,它对网络抖动的适应能力更强,同步速度也更快,特别适合虚拟机或者网络不稳定的环境。先确认系统里装的是哪个:# 检查chrony是否安装 which chronyd # 检查ntpd是否安装 which ntpd如果都没装,用包管理器装一个。以常见的发行版为例:# Debian/Ubuntu系 apt install chrony -y # RHEL/CentOS系 yum install chrony -y装好之后编辑配置文件/etc/chrony/chrony.conf(Debian/Ubuntu)或者/etc/chrony.conf(RHEL/CentOS),找到server或者pool开头的行,改成你想要的时间服务器:# 注释掉默认的服务器配置 # pool 2.debian.pool.ntp.org iburst # 添加国内的时间服务器 server ntp.aliyun.com iburst server ntp.tencent.com iburst server cn.pool.ntp.org iburstiburst这个参数的意思是:在启动的时候快速发送一批数据包来加速首次同步,正常同步之后就会恢复正常的轮询频率。对于客户端来说,加上这个参数能让同步在几秒内完成,而不是等几分钟。保存之后重启服务:# 重启chrony服务 systemctl restart chronyd # 设置开机自启 systemctl enable chronyd # 查看同步状态 chronyc sources -vchronyc sources -v的输出里,如果看到某个服务器前面有个^*符号,说明已经成功同步到那台服务器了。^+表示备选服务器,^-表示可用但被排除的,^?表示连接有问题。再看一下同步的详细情况:chronyc tracking这个命令会显示当前的时间偏差、频率偏差、参考服务器等信息。重点看System time这一行,显示的就是你本机跟参考源的时间差。正常同步之后应该在毫秒级别。3.3 有外网方案里最容易踩的三个坑第一个坑是防火墙拦截UDP 123端口。NTP用的是UDP协议,端口号123。很多企业网络的防火墙默认会拦截UDP流量,导致时间同步请求发不出去。排查方法很简单,用nc或者telnet测试一下UDP 123端口通不通:# 测试UDP 123端口连通性(需要nc支持-u参数) nc -u -z -v ntp.aliyun.com 123如果显示连接失败,基本就是防火墙的问题。需要找网络管理员开放UDP 123的出站规则。如果实在开不了,可以考虑用HTTP协议的时间同步服务作为替代方案,但精度会差一些。第二个坑是虚拟机的时间同步冲突。如果你在虚拟机里跑Linux,宿主机通常会有一个“时间同步”功能,会定期把虚拟机的时间跟宿主机对齐。这个功能跟虚拟机内部的NTP服务会打架,导致时间来回跳。解决办法是在虚拟机设置里关掉宿主机的时间同步,让虚拟机自己用NTP同步。以常见的虚拟化平台为例,在虚拟机配置里找到“时间同步”或者“与宿主机同步时间”的选项,取消勾选即可。第三个坑是系统时钟频率偏差太大。有些老旧的硬件或者低功耗设备,晶振偏差特别大,一天能漂几分钟。这种情况下即使NTP同步成功了,过一会儿又偏了。chrony有一个makestep指令可以处理这种情况:# 在chrony.conf中添加 makestep 1.0 3这行的意思是:如果时间偏差超过1秒,前3次同步直接跳变调整(而不是慢慢调整)。对于偏差特别大的机器,这个设置能避免同步过程拖太久。4. 纯内网环境:自己搭一台时间服务器4.1 内网时间服务器的选型与规划纯内网环境的核心问题是:所有机器都出不了网,没法直接访问外部NTP服务器。解决办法是在内网里选一台机器当时间服务器,其他机器都跟它同步。这台时间服务器怎么选?我的经验是看三个条件:硬件时钟相对稳定、网络位置居中、能长期开机。硬件方面,服务器级别的机器通常比普通PC的晶振要好,但也不是绝对的。网络位置方面,尽量选一台到其他机器网络延迟都差不多的机器,避免某几台机器因为网络延迟大导致同步精度下降。长期开机这个不用多说,时间服务器要是老关机,其他机器就失去了参照。如果内网里有一台能出网的机器(比如通过某种方式能访问外部网络),那最好让它先跟外部NTP同步,然后内网其他机器再跟它同步。这样整个内网的时间都能跟标准时间对齐。如果整个内网完全隔离,一台能出网的机器都没有,那就只能以这台时间服务器的本机硬件时钟为基准。这种情况下,内网范围内的时间一致性是可以保证的,但跟标准时间可能会有偏差。提示:完全隔离的内网里,如果对绝对时间精度要求不高(比如只是为了让日志时间线一致),用本机时钟做基准完全够用。但如果涉及证书校验、金融交易等对绝对时间敏感的场景,就需要考虑用GPS时钟或者铷原子钟这类外部时间源了。4.2 在Linux上搭建内网NTP服务器假设我们选了一台Linux机器作为内网时间服务器,IP地址是192.168.1.100。这台机器如果自己能出网,就配置它跟外部NTP同步;如果不能出网,就让它用本机时钟。先装chrony:# Debian/Ubuntu系 apt install chrony -y # RHEL/CentOS系 yum install chrony -y编辑配置文件/etc/chrony/chrony.conf,分两种情况。情况一:这台服务器能出网# 跟外部NTP服务器同步 server ntp.aliyun.com iburst server ntp.tencent.com iburst # 允许内网网段同步 allow 192.168.1.0/24 # 如果本机时间偏差太大,前3次直接跳变 makestep 1.0 3 # 启用本地时间源(当外部服务器都不可用时,用本机时钟) local stratum 10情况二:这台服务器完全不出网# 不配置任何外部server # 允许内网网段同步 allow 192.168.1.0/24 # 启用本地时间源,层级设为10 local stratum 10 # 如果本机时间偏差太大,前3次直接跳变 makestep 1.0 3这里重点解释几个配置项。allow 192.168.1.0/24是允许这个网段的机器来同步时间,你可以根据实际网段调整。local stratum 10这个配置很关键——它的作用是告诉chrony:即使没有外部时间源,也把本机时钟当作一个Stratum 10的时间源对外提供服务。如果不加这行,当外部服务器不可达时,chrony会认为自己没有可用的时间源,就不会响应客户端的同步请求。stratum 10这个数字可以改,范围是1到15。数字越小表示层级越高。在纯内网环境里,设成10就行,没必要设太小,避免跟外部时间源的层级混淆。配置好之后重启服务:systemctl restart chronyd systemctl enable chronyd然后在服务器上确认一下状态:# 查看时间源状态 chronyc sources -v # 查看本机作为服务器的状态 chronyc tracking如果配置了local stratum 10,在chronyc sources -v的输出里应该能看到一个Local的源,前面有个^*或者^?的标记。4.3 内网客户端的配置内网其他机器作为客户端,配置就简单了,只需要把时间服务器指向内网那台机器就行。Linux客户端编辑/etc/chrony/chrony.conf:# 注释掉所有外部server # server ntp.aliyun.com iburst # 指向内网时间服务器 server 192.168.1.100 iburst # 如果本机时间偏差太大,前3次直接跳变 makestep 1.0 3重启服务:systemctl restart chronyd systemctl enable chronydWindows客户端用命令行配置:# 指定内网时间服务器 w32tm /config /manualpeerlist:"192.168.1.100" /syncfromflags:manual /reliable:no /update # 重启时间服务 net stop w32time && net start w32time # 立即同步 w32tm /resync配置完成后,在客户端上用chronyc sources -v(Linux)或者w32tm /query /status(Windows)确认同步状态。Linux客户端应该能看到^*标记指向192.168.1.100,Windows客户端应该显示“上次同步时间”是最近的。4.4 内网方案里那些让人头疼的细节第一个细节:时间服务器的防火墙要放行UDP 123。这个看起来是废话,但我见过好几次有人配好了chrony,客户端死活同步不上,最后发现是服务器本机的防火墙没放行。Linux上用firewall-cmd或者iptables放行:# firewalld firewall-cmd --permanent --add-port=123/udp firewall-cmd --reload # iptables iptables -A INPUT -p udp --dport 123 -j ACCEPTWindows服务器的话,在“高级安全Windows Defender防火墙”里新建一条入站规则,允许UDP 123端口。第二个细节:多台时间服务器做冗余。如果内网规模比较大,建议搭两台时间服务器,客户端同时配置这两个地址。这样一台挂了另一台还能顶上。chrony支持配置多个server,它会自动选择最优的:server 192.168.1.100 iburst server 192.168.1.101 iburst第三个细节:时间服务器的硬件时钟要定期校准。如果是完全隔离的内网,时间服务器用的是本机硬件时钟,时间长了也会漂移。建议每隔一段时间手动校准一次,或者用GPS授时模块这类外部时间源来定期校准。我见过一个实验室的内网,时间服务器跑了半年没管,最后跟标准时间差了将近两分钟。第四个细节:虚拟化环境下的时间同步。如果内网时间服务器本身是虚拟机,那它的时间精度会受到宿主机的影响。虚拟机的时钟漂移通常比物理机大,因为虚拟CPU的调度不是实时的。这种情况下,要么把时间服务器放在物理机上,要么在宿主机层面做好时间同步,让虚拟机跟着宿主机走。5. 怎么确认时间同步真的生效了配置完了不代表就万事大吉了,得验证一下同步是否真的生效,精度是否满足要求。5.1 Linux下的验证方法最直接的是用chronyc系列命令:# 查看时间源状态 chronyc sources -v # 查看同步详情 chronyc tracking # 查看统计信息 chronyc sourcestats -vchronyc tracking的输出里,重点看这几个字段:字段含义正常范围System time本机跟参考源的时间偏差毫秒级Last offset上次同步时的偏差毫秒级RMS offset偏差的均方根值毫秒级Frequency本机时钟的频率偏差几十ppm以内Residual freq剩余频率偏差接近0Skew频率偏差的估计误差越小越好Root delay到参考源的总网络延迟跟网络环境有关Root dispersion到参考源的总离散度越小越好如果System time显示的是几百毫秒甚至秒级,说明同步还没完成或者有问题。正常情况下应该在几毫秒到几十毫秒之间。还可以用timedatectl命令快速查看:timedatectl status输出里会显示System clock synchronized: yes,表示系统时钟已经同步。5.2 Windows下的验证方法Windows用w32tm命令:# 查看同步状态 w32tm /query /status # 查看时间源 w32tm /query /source # 查看详细统计 w32tm /query /peersw32tm /query /status的输出里,重点看Last Successful Sync Time(上次成功同步时间)和Source(时间源)。如果上次同步时间是很久以前,说明同步有问题。还可以用w32tm /stripchart命令实时观察时间偏差:# 每2秒采样一次,共采样5次 w32tm /stripchart /computer:192.168.1.100 /samples:5 /dataonly这个命令会显示每次采样的时间偏差,单位是秒。正常应该在毫秒级。5.3 跨平台验证的小技巧如果你有多台机器需要验证时间是否一致,最直观的方法是在所有机器上同时执行date命令,对比输出。但手动对比太麻烦,可以写个简单脚本:# 在每台机器上执行,输出时间戳 date +"%Y-%m-%d %H:%M:%S.%N"然后在同一时刻收集所有机器的输出,对比差异。如果差异在几十毫秒以内,说明同步效果不错。另一个方法是看日志时间戳。找一条在所有机器上都会产生的日志(比如cron任务的执行日志),对比各台机器上这条日志的时间戳。如果时间戳基本一致,说明同步没问题。6. 那些年我踩过的时间同步坑6.1 时间跳变导致的服务异常有一次在一台生产服务器上配置NTP,没加makestep参数,结果chrony用“渐进式调整”的方式慢慢校准时间。本来这也没什么问题,但那台机器上跑了一个对时间敏感的服务,它发现系统时间在“变慢”,以为出了什么异常,直接触发了保护机制把服务停了。后来加上makestep 1.0 3,让前几次同步直接跳变,问题就解决了。这个坑的教训是:对于已经运行了关键服务的机器,时间调整方式要慎重选择。渐进式调整虽然平滑,但可能会被某些服务误判为异常。跳变式调整虽然粗暴,但干脆利落。具体选哪种,要看机器上跑的服务对时间变化的敏感程度。6.2 防火墙规则顺序导致的同步失败这个坑更隐蔽。有一次在内网时间服务器上配置防火墙,明明加了允许UDP 123的规则,客户端就是同步不上。排查了半天才发现,防火墙规则里有一条更靠前的“拒绝所有UDP”规则,把NTP的流量先拦掉了。防火墙规则是按顺序匹配的,前面的规则先生效。所以加规则的时候要注意插入位置,或者把允许规则放在拒绝规则前面。6.3 虚拟机快照恢复后的时间错乱做虚拟机快照恢复测试的时候发现,恢复后的虚拟机时间停留在快照创建的时刻,跟当前时间差了好几天。这时候NTP同步会触发makestep,直接跳变调整。但如果快照恢复后的时间偏差太大(比如差了几个月),有些系统会拒绝跳变,认为时间异常。这种情况下需要手动先把时间调到一个大致正确的范围,再让NTP去精细同步。6.4 时间服务器单点故障早期搭内网时间服务器的时候只搭了一台,觉得内网环境简单不会出问题。结果有一次那台机器硬件故障宕机了,内网所有机器的时间同步都断了。虽然短时间内各机器还能靠本机时钟撑着,但过了几天时间偏差就肉眼可见了。后来改成两台时间服务器互为备份,客户端同时配置两个地址,就再没出过问题。7. 进阶话题:让时间同步更稳的几个配置7.1 调整轮询间隔chrony默认的轮询间隔是动态调整的,根据网络条件和时钟稳定性自动变化。但在某些场景下,手动指定轮询间隔可能更合适。比如网络延迟很稳定的内网环境,可以把最小轮询间隔调小一点,让同步更频繁:# 在server行后面加上minpoll和maxpoll参数 server 192.168.1.100 iburst minpoll 4 maxpoll 6minpoll 4表示最小轮询间隔是2的4次方秒,也就是16秒;maxpoll 6表示最大轮询间隔是2的6次方秒,也就是64秒。默认情况下minpoll是6(64秒),maxpoll是10(1024秒)。调小这两个值可以让同步更频繁,但也会增加网络流量和服务器负载。内网环境带宽充足的话,适当调小没问题。7.2 启用硬件时间戳如果网卡支持硬件时间戳(Hardware Timestamping),可以启用它来提高同步精度。硬件时间戳是在网卡层面记录数据包的收发时间,比操作系统层面记录的要精确得多,能消除操作系统调度带来的抖动。在chrony配置里加上:# 启用硬件时间戳 hwtimestamp *然后重启chrony。不过这个功能需要网卡驱动支持,不是所有网卡都能用。可以用ethtool -T 网卡名查看网卡是否支持硬件时间戳。7.3 监控时间同步状态对于重要的时间服务器,建议加上监控。最简单的办法是用chrony的chronyc tracking输出做定期检查,把关键指标采集到监控系统里。比如用Prometheus的node_exporter,它自带chrony的采集插件,可以导出时间偏差、频率偏差等指标。然后设置告警规则,当时间偏差超过阈值时触发告警。如果不想搞这么复杂,也可以写个简单的脚本定期检查:#!/bin/bash # 检查时间偏差是否超过100毫秒 offset=$(chronyc tracking | grep "System time" | awk '{print $4}') if (( $(echo "$offset > 0.1" | bc -l) )); then echo "警告:时间偏差过大:${offset}秒" # 这里可以加上发送告警的逻辑 fi把这个脚本加到crontab里,每小时跑一次,有问题就能及时发现。8. 关于时间同步的一点个人体会搞了这么多年时间同步,最大的感受是:这件事平时不出问题的时候你根本感觉不到它的存在,一旦出问题就是连锁反应。日志对不上、任务乱序、证书失效,每一个都能让你排查半天。所以我的建议是,不管环境大小,时间同步这件事都要提前做好,别等出了问题再补。另外就是,配置的时候别图省事。该加的iburst加上,该设的makestep设上,该放行的防火墙规则放行。这些配置花不了几分钟,但能省掉后面很多麻烦。内网环境的话,尽量搭两台时间服务器做冗余,别嫌麻烦,一台挂了的时候你就知道两台的好处了。最后说一个我经常用的检查习惯:每次配置完时间同步,我都会在所有机器上同时跑一遍date命令,肉眼对比一下。虽然粗糙,但很直观。如果各机器的时间戳基本一致,说明同步没问题;如果肉眼都能看出差异,那就得回去查配置了。这个习惯帮我省了不少事。
1. 时间不同步这件事,比你想的要命得多很多人觉得时间同步是个小问题,差个几秒又不会怎样。我刚开始做运维的时候也这么想,直到有一次排查一个分布式任务调度的问题,三个节点上的日志时间戳对不上,A节点显示任务10:00:03开始,B节点显示10:00:07才收到,C节点干脆显示09:59:58就完成了。三个日志摆在一起,根本没法拼出完整的调用链路,排查了整整一个下午才发现是各台机器的系统时间本身就差了十几秒。这件事之后我才真正重视起时间同步。不管你是管几台服务器的运维,还是家里有几台电脑需要协同工作,甚至是实验室里一堆设备要统一记录数据,时间不一致带来的问题都是隐性的、连锁的。日志分析错乱只是最表面的影响,更深层的问题包括:数据库主从复制延迟判断失准、证书校验失败、定时任务重复执行或漏执行、文件版本比对混乱、监控告警时间线错位等等。这篇内容就是把我这些年配置时间同步的经验完整梳理一遍。核心思路分两条线:有外网环境的简单方案和纯内网环境的进阶方案。前者适合大多数办公场景和家庭网络,几分钟就能搞定;后者适合隔离网络、实验室内网、生产环境不出网的情况,需要自己搭一台时间服务器。两条线我都会给出完整的操作步骤、参数解释和踩坑经验,不管你是刚接触这块的新手还是踩过几次坑的老手,都能直接抄作业。2. 先搞清楚时间同步的基本逻辑2.1 NTP到底在同步什么NTP的全称是Network Time Protocol,翻译过来叫网络时间协议。它的核心工作就是让一台机器的时间跟另一台机器(或者一组机器)的时间对齐。听起来简单,但里面有几个概念必须先理清楚,不然后面配置的时候容易犯迷糊。第一个概念是时间源(Time Source)。你的电脑要同步时间,总得有个参照物。这个参照物可以是互联网上的公共时间服务器,也可以是你内网里自己搭的一台服务器。时间源本身也有层级概念,叫Stratum(层级)。Stratum 0是原子钟、GPS时钟这类物理设备,Stratum 1是直接连接Stratum 0的服务器,Stratum 2是从Stratum 1同步的服务器,以此类推。层级数字越小越接近源头,精度越高。我们普通用户能接触到的公共NTP服务器基本都是Stratum 1或Stratum 2。第二个概念是时间偏差(Offset)和网络延迟(Delay)。NTP协议在工作的时候,客户端会跟服务器来回交换几个数据包,通过计算发送和接收的时间戳,算出两个东西:你的机器跟服务器的时间差了多少(Offset),以及数据包在网络上来回花了多少时间(Delay)。真正同步的时候,NTP会把网络延迟考虑进去做补偿,所以即使网络有几十毫秒的延迟,最终同步精度也能做到毫秒级别。第三个概念是时钟漂移(Clock Drift)。每台电脑主板上的晶振频率都有微小差异,有的走得快一点,有的走得慢一点。这就导致即使你这一刻把时间校准了,过一段时间它又会慢慢偏掉。普通电脑一天漂移几秒到几十秒都很正常。所以时间同步不是做一次就完事了,得让它持续运行,定期校准。2.2 为什么分“有外网”和“纯内网”两条路这个问题的本质是:你能不能直接访问互联网上的时间服务器。有外网的情况下,你的电脑可以直接跟公共NTP服务器通信,比如各大机构提供的标准时间服务。配置起来非常简单,改一个配置文件或者点几下鼠标就行。但这里有个细节:如果你的网络环境里有防火墙或者代理,UDP 123端口(NTP使用的端口)可能被拦截,这时候就需要换方案或者调整防火墙规则。纯内网的情况下,所有机器都出不了网,没法直接跟外面的时间服务器通信。这时候就必须在内网里找一台机器当“时间源”,让它先跟外部同步(如果它能出网的话),或者直接以它的硬件时钟为基准,然后其他机器都跟这台机器同步。如果整个内网完全隔离,连一台能出网的机器都没有,那就需要这台时间服务器用本机的硬件时钟作为基准,虽然精度不如跟外部同步,但在内网范围内保持一致是没问题的。注意:很多人会忽略一点——即使有外网,如果内网机器数量多,让每台机器都去访问外部NTP服务器也不是好做法。一来增加外部依赖,二来外部服务器可能对你的频繁请求做限制。更好的方式是内网搭一台时间服务器,统一对外同步,然后内网其他机器都跟它同步。3. 有外网环境:十分钟搞定的简单方案3.1 Windows系统的时间同步设置Windows系统自带时间同步功能,图形界面就能操作。右下角任务栏找到时间显示,右键选择“调整日期/时间”,在弹出的设置窗口里找到“同步你的时钟”或者“Internet时间”选项卡。点击“更改设置”,勾选“与Internet时间服务器同步”,然后在服务器地址栏里填入一个可用的NTP服务器地址。默认的服务器地址是time.windows.com,但这个服务器在国内的连通性时好时坏,有时候同步会失败。我一般会换成下面这几个里面挑一个:ntp.aliyun.com— 国内访问速度快,稳定性好cn.pool.ntp.org— 国内NTP池项目,自动分配最近的服务器time.apple.com— 苹果的时间服务器,国内访问也还行ntp.tencent.com— 腾讯的时间服务器选好之后点击“立即更新”,等几秒钟就会提示同步成功。如果提示失败,大概率是UDP 123端口被防火墙拦了,或者你选的服务器暂时不可用,换一个再试。命令行方式更适合批量操作或者写脚本。以管理员身份打开命令提示符或者PowerShell,用w32tm命令来配置:# 查看当前时间同步状态 w32tm /query /status # 指定时间服务器并同步 w32tm /config /manualpeerlist:"ntp.aliyun.com,ntp.tencent.com" /syncfromflags:manual /reliable:yes /update # 重启时间服务 net stop w32time && net start w32time # 立即触发同步 w32tm /resync # 查看同步结果 w32tm /query /status这里解释一下几个参数:/manualpeerlist后面跟的是时间服务器地址列表,多个地址用逗号分隔,Windows会从中选一个可用的;/syncfromflags:manual表示使用手动指定的服务器列表;/reliable:yes表示把这台机器标记为可靠时间源(如果是普通客户端,这个参数其实可加可不加);/update是让配置立即生效。实测下来,w32tm /resync有时候会报“服务尚未启动”之类的错误,这时候先执行net start w32time把服务拉起来再试。另外Windows的时间同步默认间隔是一周一次,对于时间精度要求高的场景,这个频率太低了。可以通过注册表调整同步间隔:# 修改同步频率为每小时一次(3600秒) reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient" /v SpecialPollInterval /t REG_DWORD /d 3600 /f # 重启时间服务使配置生效 net stop w32time && net start w32time3.2 Linux系统的时间同步设置Linux这边现在主流用的是chrony,老一点的系统可能还在用ntpd。我建议不管新老系统,能用chrony就用chrony,它对网络抖动的适应能力更强,同步速度也更快,特别适合虚拟机或者网络不稳定的环境。先确认系统里装的是哪个:# 检查chrony是否安装 which chronyd # 检查ntpd是否安装 which ntpd如果都没装,用包管理器装一个。以常见的发行版为例:# Debian/Ubuntu系 apt install chrony -y # RHEL/CentOS系 yum install chrony -y装好之后编辑配置文件/etc/chrony/chrony.conf(Debian/Ubuntu)或者/etc/chrony.conf(RHEL/CentOS),找到server或者pool开头的行,改成你想要的时间服务器:# 注释掉默认的服务器配置 # pool 2.debian.pool.ntp.org iburst # 添加国内的时间服务器 server ntp.aliyun.com iburst server ntp.tencent.com iburst server cn.pool.ntp.org iburstiburst这个参数的意思是:在启动的时候快速发送一批数据包来加速首次同步,正常同步之后就会恢复正常的轮询频率。对于客户端来说,加上这个参数能让同步在几秒内完成,而不是等几分钟。保存之后重启服务:# 重启chrony服务 systemctl restart chronyd # 设置开机自启 systemctl enable chronyd # 查看同步状态 chronyc sources -vchronyc sources -v的输出里,如果看到某个服务器前面有个^*符号,说明已经成功同步到那台服务器了。^+表示备选服务器,^-表示可用但被排除的,^?表示连接有问题。再看一下同步的详细情况:chronyc tracking这个命令会显示当前的时间偏差、频率偏差、参考服务器等信息。重点看System time这一行,显示的就是你本机跟参考源的时间差。正常同步之后应该在毫秒级别。3.3 有外网方案里最容易踩的三个坑第一个坑是防火墙拦截UDP 123端口。NTP用的是UDP协议,端口号123。很多企业网络的防火墙默认会拦截UDP流量,导致时间同步请求发不出去。排查方法很简单,用nc或者telnet测试一下UDP 123端口通不通:# 测试UDP 123端口连通性(需要nc支持-u参数) nc -u -z -v ntp.aliyun.com 123如果显示连接失败,基本就是防火墙的问题。需要找网络管理员开放UDP 123的出站规则。如果实在开不了,可以考虑用HTTP协议的时间同步服务作为替代方案,但精度会差一些。第二个坑是虚拟机的时间同步冲突。如果你在虚拟机里跑Linux,宿主机通常会有一个“时间同步”功能,会定期把虚拟机的时间跟宿主机对齐。这个功能跟虚拟机内部的NTP服务会打架,导致时间来回跳。解决办法是在虚拟机设置里关掉宿主机的时间同步,让虚拟机自己用NTP同步。以常见的虚拟化平台为例,在虚拟机配置里找到“时间同步”或者“与宿主机同步时间”的选项,取消勾选即可。第三个坑是系统时钟频率偏差太大。有些老旧的硬件或者低功耗设备,晶振偏差特别大,一天能漂几分钟。这种情况下即使NTP同步成功了,过一会儿又偏了。chrony有一个makestep指令可以处理这种情况:# 在chrony.conf中添加 makestep 1.0 3这行的意思是:如果时间偏差超过1秒,前3次同步直接跳变调整(而不是慢慢调整)。对于偏差特别大的机器,这个设置能避免同步过程拖太久。4. 纯内网环境:自己搭一台时间服务器4.1 内网时间服务器的选型与规划纯内网环境的核心问题是:所有机器都出不了网,没法直接访问外部NTP服务器。解决办法是在内网里选一台机器当时间服务器,其他机器都跟它同步。这台时间服务器怎么选?我的经验是看三个条件:硬件时钟相对稳定、网络位置居中、能长期开机。硬件方面,服务器级别的机器通常比普通PC的晶振要好,但也不是绝对的。网络位置方面,尽量选一台到其他机器网络延迟都差不多的机器,避免某几台机器因为网络延迟大导致同步精度下降。长期开机这个不用多说,时间服务器要是老关机,其他机器就失去了参照。如果内网里有一台能出网的机器(比如通过某种方式能访问外部网络),那最好让它先跟外部NTP同步,然后内网其他机器再跟它同步。这样整个内网的时间都能跟标准时间对齐。如果整个内网完全隔离,一台能出网的机器都没有,那就只能以这台时间服务器的本机硬件时钟为基准。这种情况下,内网范围内的时间一致性是可以保证的,但跟标准时间可能会有偏差。提示:完全隔离的内网里,如果对绝对时间精度要求不高(比如只是为了让日志时间线一致),用本机时钟做基准完全够用。但如果涉及证书校验、金融交易等对绝对时间敏感的场景,就需要考虑用GPS时钟或者铷原子钟这类外部时间源了。4.2 在Linux上搭建内网NTP服务器假设我们选了一台Linux机器作为内网时间服务器,IP地址是192.168.1.100。这台机器如果自己能出网,就配置它跟外部NTP同步;如果不能出网,就让它用本机时钟。先装chrony:# Debian/Ubuntu系 apt install chrony -y # RHEL/CentOS系 yum install chrony -y编辑配置文件/etc/chrony/chrony.conf,分两种情况。情况一:这台服务器能出网# 跟外部NTP服务器同步 server ntp.aliyun.com iburst server ntp.tencent.com iburst # 允许内网网段同步 allow 192.168.1.0/24 # 如果本机时间偏差太大,前3次直接跳变 makestep 1.0 3 # 启用本地时间源(当外部服务器都不可用时,用本机时钟) local stratum 10情况二:这台服务器完全不出网# 不配置任何外部server # 允许内网网段同步 allow 192.168.1.0/24 # 启用本地时间源,层级设为10 local stratum 10 # 如果本机时间偏差太大,前3次直接跳变 makestep 1.0 3这里重点解释几个配置项。allow 192.168.1.0/24是允许这个网段的机器来同步时间,你可以根据实际网段调整。local stratum 10这个配置很关键——它的作用是告诉chrony:即使没有外部时间源,也把本机时钟当作一个Stratum 10的时间源对外提供服务。如果不加这行,当外部服务器不可达时,chrony会认为自己没有可用的时间源,就不会响应客户端的同步请求。stratum 10这个数字可以改,范围是1到15。数字越小表示层级越高。在纯内网环境里,设成10就行,没必要设太小,避免跟外部时间源的层级混淆。配置好之后重启服务:systemctl restart chronyd systemctl enable chronyd然后在服务器上确认一下状态:# 查看时间源状态 chronyc sources -v # 查看本机作为服务器的状态 chronyc tracking如果配置了local stratum 10,在chronyc sources -v的输出里应该能看到一个Local的源,前面有个^*或者^?的标记。4.3 内网客户端的配置内网其他机器作为客户端,配置就简单了,只需要把时间服务器指向内网那台机器就行。Linux客户端编辑/etc/chrony/chrony.conf:# 注释掉所有外部server # server ntp.aliyun.com iburst # 指向内网时间服务器 server 192.168.1.100 iburst # 如果本机时间偏差太大,前3次直接跳变 makestep 1.0 3重启服务:systemctl restart chronyd systemctl enable chronydWindows客户端用命令行配置:# 指定内网时间服务器 w32tm /config /manualpeerlist:"192.168.1.100" /syncfromflags:manual /reliable:no /update # 重启时间服务 net stop w32time && net start w32time # 立即同步 w32tm /resync配置完成后,在客户端上用chronyc sources -v(Linux)或者w32tm /query /status(Windows)确认同步状态。Linux客户端应该能看到^*标记指向192.168.1.100,Windows客户端应该显示“上次同步时间”是最近的。4.4 内网方案里那些让人头疼的细节第一个细节:时间服务器的防火墙要放行UDP 123。这个看起来是废话,但我见过好几次有人配好了chrony,客户端死活同步不上,最后发现是服务器本机的防火墙没放行。Linux上用firewall-cmd或者iptables放行:# firewalld firewall-cmd --permanent --add-port=123/udp firewall-cmd --reload # iptables iptables -A INPUT -p udp --dport 123 -j ACCEPTWindows服务器的话,在“高级安全Windows Defender防火墙”里新建一条入站规则,允许UDP 123端口。第二个细节:多台时间服务器做冗余。如果内网规模比较大,建议搭两台时间服务器,客户端同时配置这两个地址。这样一台挂了另一台还能顶上。chrony支持配置多个server,它会自动选择最优的:server 192.168.1.100 iburst server 192.168.1.101 iburst第三个细节:时间服务器的硬件时钟要定期校准。如果是完全隔离的内网,时间服务器用的是本机硬件时钟,时间长了也会漂移。建议每隔一段时间手动校准一次,或者用GPS授时模块这类外部时间源来定期校准。我见过一个实验室的内网,时间服务器跑了半年没管,最后跟标准时间差了将近两分钟。第四个细节:虚拟化环境下的时间同步。如果内网时间服务器本身是虚拟机,那它的时间精度会受到宿主机的影响。虚拟机的时钟漂移通常比物理机大,因为虚拟CPU的调度不是实时的。这种情况下,要么把时间服务器放在物理机上,要么在宿主机层面做好时间同步,让虚拟机跟着宿主机走。5. 怎么确认时间同步真的生效了配置完了不代表就万事大吉了,得验证一下同步是否真的生效,精度是否满足要求。5.1 Linux下的验证方法最直接的是用chronyc系列命令:# 查看时间源状态 chronyc sources -v # 查看同步详情 chronyc tracking # 查看统计信息 chronyc sourcestats -vchronyc tracking的输出里,重点看这几个字段:字段含义正常范围System time本机跟参考源的时间偏差毫秒级Last offset上次同步时的偏差毫秒级RMS offset偏差的均方根值毫秒级Frequency本机时钟的频率偏差几十ppm以内Residual freq剩余频率偏差接近0Skew频率偏差的估计误差越小越好Root delay到参考源的总网络延迟跟网络环境有关Root dispersion到参考源的总离散度越小越好如果System time显示的是几百毫秒甚至秒级,说明同步还没完成或者有问题。正常情况下应该在几毫秒到几十毫秒之间。还可以用timedatectl命令快速查看:timedatectl status输出里会显示System clock synchronized: yes,表示系统时钟已经同步。5.2 Windows下的验证方法Windows用w32tm命令:# 查看同步状态 w32tm /query /status # 查看时间源 w32tm /query /source # 查看详细统计 w32tm /query /peersw32tm /query /status的输出里,重点看Last Successful Sync Time(上次成功同步时间)和Source(时间源)。如果上次同步时间是很久以前,说明同步有问题。还可以用w32tm /stripchart命令实时观察时间偏差:# 每2秒采样一次,共采样5次 w32tm /stripchart /computer:192.168.1.100 /samples:5 /dataonly这个命令会显示每次采样的时间偏差,单位是秒。正常应该在毫秒级。5.3 跨平台验证的小技巧如果你有多台机器需要验证时间是否一致,最直观的方法是在所有机器上同时执行date命令,对比输出。但手动对比太麻烦,可以写个简单脚本:# 在每台机器上执行,输出时间戳 date +"%Y-%m-%d %H:%M:%S.%N"然后在同一时刻收集所有机器的输出,对比差异。如果差异在几十毫秒以内,说明同步效果不错。另一个方法是看日志时间戳。找一条在所有机器上都会产生的日志(比如cron任务的执行日志),对比各台机器上这条日志的时间戳。如果时间戳基本一致,说明同步没问题。6. 那些年我踩过的时间同步坑6.1 时间跳变导致的服务异常有一次在一台生产服务器上配置NTP,没加makestep参数,结果chrony用“渐进式调整”的方式慢慢校准时间。本来这也没什么问题,但那台机器上跑了一个对时间敏感的服务,它发现系统时间在“变慢”,以为出了什么异常,直接触发了保护机制把服务停了。后来加上makestep 1.0 3,让前几次同步直接跳变,问题就解决了。这个坑的教训是:对于已经运行了关键服务的机器,时间调整方式要慎重选择。渐进式调整虽然平滑,但可能会被某些服务误判为异常。跳变式调整虽然粗暴,但干脆利落。具体选哪种,要看机器上跑的服务对时间变化的敏感程度。6.2 防火墙规则顺序导致的同步失败这个坑更隐蔽。有一次在内网时间服务器上配置防火墙,明明加了允许UDP 123的规则,客户端就是同步不上。排查了半天才发现,防火墙规则里有一条更靠前的“拒绝所有UDP”规则,把NTP的流量先拦掉了。防火墙规则是按顺序匹配的,前面的规则先生效。所以加规则的时候要注意插入位置,或者把允许规则放在拒绝规则前面。6.3 虚拟机快照恢复后的时间错乱做虚拟机快照恢复测试的时候发现,恢复后的虚拟机时间停留在快照创建的时刻,跟当前时间差了好几天。这时候NTP同步会触发makestep,直接跳变调整。但如果快照恢复后的时间偏差太大(比如差了几个月),有些系统会拒绝跳变,认为时间异常。这种情况下需要手动先把时间调到一个大致正确的范围,再让NTP去精细同步。6.4 时间服务器单点故障早期搭内网时间服务器的时候只搭了一台,觉得内网环境简单不会出问题。结果有一次那台机器硬件故障宕机了,内网所有机器的时间同步都断了。虽然短时间内各机器还能靠本机时钟撑着,但过了几天时间偏差就肉眼可见了。后来改成两台时间服务器互为备份,客户端同时配置两个地址,就再没出过问题。7. 进阶话题:让时间同步更稳的几个配置7.1 调整轮询间隔chrony默认的轮询间隔是动态调整的,根据网络条件和时钟稳定性自动变化。但在某些场景下,手动指定轮询间隔可能更合适。比如网络延迟很稳定的内网环境,可以把最小轮询间隔调小一点,让同步更频繁:# 在server行后面加上minpoll和maxpoll参数 server 192.168.1.100 iburst minpoll 4 maxpoll 6minpoll 4表示最小轮询间隔是2的4次方秒,也就是16秒;maxpoll 6表示最大轮询间隔是2的6次方秒,也就是64秒。默认情况下minpoll是6(64秒),maxpoll是10(1024秒)。调小这两个值可以让同步更频繁,但也会增加网络流量和服务器负载。内网环境带宽充足的话,适当调小没问题。7.2 启用硬件时间戳如果网卡支持硬件时间戳(Hardware Timestamping),可以启用它来提高同步精度。硬件时间戳是在网卡层面记录数据包的收发时间,比操作系统层面记录的要精确得多,能消除操作系统调度带来的抖动。在chrony配置里加上:# 启用硬件时间戳 hwtimestamp *然后重启chrony。不过这个功能需要网卡驱动支持,不是所有网卡都能用。可以用ethtool -T 网卡名查看网卡是否支持硬件时间戳。7.3 监控时间同步状态对于重要的时间服务器,建议加上监控。最简单的办法是用chrony的chronyc tracking输出做定期检查,把关键指标采集到监控系统里。比如用Prometheus的node_exporter,它自带chrony的采集插件,可以导出时间偏差、频率偏差等指标。然后设置告警规则,当时间偏差超过阈值时触发告警。如果不想搞这么复杂,也可以写个简单的脚本定期检查:#!/bin/bash # 检查时间偏差是否超过100毫秒 offset=$(chronyc tracking | grep "System time" | awk '{print $4}') if (( $(echo "$offset > 0.1" | bc -l) )); then echo "警告:时间偏差过大:${offset}秒" # 这里可以加上发送告警的逻辑 fi把这个脚本加到crontab里,每小时跑一次,有问题就能及时发现。8. 关于时间同步的一点个人体会搞了这么多年时间同步,最大的感受是:这件事平时不出问题的时候你根本感觉不到它的存在,一旦出问题就是连锁反应。日志对不上、任务乱序、证书失效,每一个都能让你排查半天。所以我的建议是,不管环境大小,时间同步这件事都要提前做好,别等出了问题再补。另外就是,配置的时候别图省事。该加的iburst加上,该设的makestep设上,该放行的防火墙规则放行。这些配置花不了几分钟,但能省掉后面很多麻烦。内网环境的话,尽量搭两台时间服务器做冗余,别嫌麻烦,一台挂了的时候你就知道两台的好处了。最后说一个我经常用的检查习惯:每次配置完时间同步,我都会在所有机器上同时跑一遍date命令,肉眼对比一下。虽然粗糙,但很直观。如果各机器的时间戳基本一致,说明同步没问题;如果肉眼都能看出差异,那就得回去查配置了。这个习惯帮我省了不少事。