☰
内网NTP时间服务器搭建:以本地时钟为源的chrony/ntpd配置指南
2026/10/16 6:58:14 网站建设 项目流程

做内网运维这行,时间同步是永远绕不开的坎。服务器日志对不上、监控曲线错位、数据库集群脑裂,十有八九都是时间不一致闹的。大家都习惯让服务器去公网NTP服务器拉时间,但总有那么些环境不给外网、或者安全要求高到不允许任何外部连接。这时候,最靠谱的办法就是自己搭一台“以本地时钟为源的时间服务器”,让它只靠自身的硬件时钟维持时间,然后向整个局域网发布授时服务。这东西听上去不复杂,不就是把本地时间共享出去嘛,但真动手做起来,坑比想象中多:本地时钟漂移怎么算、NTP同步层级怎么设、为什么客户端死活同步不上,我都会在这篇文章里展开讲清楚,顺便把两种主流的配置方案和排障记录一并交出来。

1. 方案定调:为什么本地时钟能撑起一台NTP服务器

1.1 本地时钟源的适用场景

大多数教程里教的是“NTP客户端怎么从上游同步”,也就是把服务器当成一个中转站,自己先去互联网上对时,再把对好的时间转手发给内网设备。这种方式没什么问题,但前提是你得能出网。

现实中我遇到过的场景包括:某单位的内网安全区,业务系统与互联网物理隔离;某个临时搭建的测试环境,外网线路还没接通;还有一些实验室设备,厂商要求必须在无外部时间源的环境下独立运行。这些地方同样需要标准时间,而且日志和审计都要求时间线一致,所以“以本地时钟为源”的授时服务器就成了刚需。它在协议层面没有任何特殊之处,依然是标准的NTP服务,只是把自己的硬件时钟当成了权威参考源,不依赖任何上游网络时间源。

1.2 本地时钟的误差从哪来、能压到多少

在说配置之前,得先把底层的东西聊明白:本地时钟的误差究竟是怎么产生的。

普通计算机主板上那颗RTC芯片,内部是一个石英晶振。石英晶振有一个特性,频率会随着温度变化而漂移,同时芯片本身还有一个老化率——用久了频率会慢慢偏移。普通RTC的日误差大概在1到10秒,听上去很不靠谱,但如果经过NTP软件的驯服和频率补偿,情况会好很多。NTP服务会记录下本机晶振与真实时间的偏差率,单位是ppm(百万分之一),一秒钟偏1ppm,累积一天大约是0.0864秒。这个偏差率被记录在driftfile里之后,服务会持续修正,本地时钟的长期稳定性就能维持得相当好。

1.3 三条技术路线的取舍

我做过几种不同精度档次的本地时间源,简单列一下,方便你按需求和预算选:

方案硬件投入短期精度长期稳定性适合场景
纯RTC芯片方案零成本,用服务器主板自带时钟秒级需定期人工校准内网一般设备、日志时间统一
外接高稳晶振(OCXO/TCXO)中低成本,加个小模块毫秒级几个月不用管实验室、无外网的工业系统
GPS授时模块辅助校准百元级,需要天线微秒到毫秒级长期稳定无人值守、要求高精度的内网

这里有一个概念需要明确:即使挂了GPS模块,这台机器对外提供的依然是“本地基准时间”,GPS只是用来驯服本地晶振的,不是让这台服务器去当公网客户端的普通节点。对外发布的NTP源仍然在这台机器上,也就是“本地时钟本源”。

2. 动手前的准备:机器选型与首次校时

2.1 当时间服务器需要什么样的硬件

很多朋友以为跑NTP服务得配一台高档服务器,真不用。NTP本身消耗极小,一台旧笔记本、一块开源单板计算机都能扛得住几百台客户端的授时请求。我更看重的是另外三样东西。

第一是电源稳定性。电源波动会直接影响晶振的工作状态,进而影响频率准确度。一台接了UPS、长期稳定运行的机器,比性能强但电源毛糙的机器靠谱得多。第二是网络稳定性。NTP走UDP 123端口,服务端需要持续响应客户端请求,网卡不稳定会导致客户端拿到的回复延迟大,进而产生偏移误判。第三是硬件时钟(RTC)的电池状态。主板上那颗纽扣电池没电了,服务器一断电重启,时间就直接退回出厂年份,这是最容易翻车的地方。

2.2 首次初始化:把“起点”校准确

任何NTP服务器第一次上线前,都必须先把系统时间校准到一个尽可能接近真实时间的状态,否则后面全是白搭。很多新手上来就配好服务然后启动,结果客户端问了一遍拿到了一个差半个多小时的时间,瞬间一堆服务报错。

如果有临时外网权限,最简单的方式是用系统自带的校时工具同步一次时间,然后断开外网。命令参考:

# 使用chrony临时同步一次,然后停止外部源 chronyc makestep # 或者传统一点的ntpdate方式(未安装chrony的环境) ntpdate pool.ntp.org

如果完全没外网,只能用人工的方式。建议用手机上的标准时间作为参照,通过date命令手动设置:

date -s "2024-05-20 14:30:00"

这里有个小教训:手动设置之后,系统时间虽然对了,但硬件RTC里的值还没更新,重启后又会跳回去。所以必须同时把系统时间写回硬件时钟,用下面这条命令:

hwclock --systohc

如果这一布经常忘记,建议在crontab里加一条定期同步的规则,把系统时间周期性地写回RTC,避免断电丢时间。

2.3 用NTP守护进程自动记录频率误差

刚才提到了driftfile,这个文件很关键。它的作用就是让NTP服务“记住”本机晶振的偏差率。第一次启动时,服务还没有任何参考数据,只能凭系统时钟硬顶。运行一段时间后,服务会对比本地时钟和时间源的差值,估算出偏差率,写入driftfile。之后每次启动,服务都会读取这个文件,直接用补偿后的频率来维持时间。

这个机制就相当于你请了一位熟悉这台机器脾气的老师傅,他知道这台机器每分钟会快多少毫秒,然后在每次报时前自动扣掉这部分误差。所以driftfile一定要配置正确,而且给服务写入权限,不然每次重启都得重新学习一遍,精度自然上不去。

3. 两种主流配置落地:chrony与ntpd

3.1 现代环境首选:chrony的local方案

chrony是现在绝大多数发行版的默认NTP实现,它的优势在于同步速度快、对网络抖动不敏感,而且配置语法比老ntpd清爽。以本机为源,配置相当简练。

编辑/etc/chrony.conf,把默认从互联网同步的server或pool指令全部注释掉,然后加几行关键配置:

# 不连接任何外部时间源,注释掉所有server/pool行 # 声明本机为本地权威时间源,stratum设为10 local stratum 10 # 允许局域网内所有客户端访问,按实际网段调整 allow 192.168.0.0/16 # 记录晶振频率漂移修正值 driftfile /var/lib/chrony/drift # 允许本机系统时间根据NTP平滑调整 makestep 1 3

local stratum 10这条指令的意思是:当chrony没有可用的外部参考源时,它可以把本机当前系统时间作为时间基准,以stratum 10的层级对外提供服务。Stratum是NTP的层级概念,stratum 0是原子钟和GPS这种硬件时钟,stratum 1是直接连接stratum 0设备的时间服务器,层数越大代表离权威源越远。这里为什么设为10而不设为1?因为本地RTC时钟的精度不足以支撑更高级别的声明,万一以后内网还接了更精准的时间源,这个“备胎”不会混淆客户端的优先级。

配置完成后重启服务并查看状态:

systemctl restart chronyd systemctl status chronyd chronyc tracking

如果能看到Leap status : Normal,并且Stratum一栏显示10,就说明服务已经把自己当成时间源了。

3.2 老系统兼容方案:ntpd的本地参考时钟

虽然chrony已经是事实标准,但很多老系统、低配嵌入式环境还在用经典的ntpd。ntpd里实现“以本地时钟为源”的语法更直白一些:它通过127.127.1.0这个特殊地址引用本机的本地时钟驱动。编辑/etc/ntp.conf:

# 使用本地时钟作为时间源 server 127.127.1.0 # 指定本地时钟的stratum层级 fudge 127.127.1.0 stratum 8 # 记录漂移修正 driftfile /var/lib/ntp/drift # 禁止所有远程管理,只允许局域网客户端查询 restrict default nomodify notrap nopeer noquery restrict 192.168.0.0 mask 255.255.255.0 nomodify notrap

这里的关键在于127.127.1.0并不是一个真实的IP地址,它是ntpd内置的“本地时钟伪设备”的标识。fudge指令用来给它补充参数,最常用的就是指定stratum级别。我习惯设在8左右,既比真实公网服务器层级低,又能让局域网客户端把它纳入同步源。

启动ntpd之后,用ntpq -p查看:

ntpq -p

如果看到类似LOCAL(1)的条目,并且前面有*号,说明本地时钟已经被选为同步源,服务正常。

有一点要特别提醒:ntpd在本地时钟模式下,启动初期会有一个所谓的“哄骗”阶段。它会先进入超时等待状态,直到本地时钟被认为足够稳定之后才开始服务。如果刚启动就去查状态可能会看到没有同步源,这时候等几分钟再复查就行了。

3.3 进阶玩法:接一条GPS授时模块当“本地基准”

很多人觉得本地时钟不靠谱,担心精度不够。其实想要高精度,完全可以在“本地”这个框架内做升级,最常用的方案是接一条串口GPS授时模块。

过程并不复杂。将GPS模块通过USB转串口接到服务器上,安装gpsd套件,让它读取NMEA协议输出的时间报文,然后通过共享内存(SHM)接口把时间喂给chrony。在/etc/chrony.conf里加一行:

refclock SHM 0 offset 0.5 refid GPS precision 1e-1 poll 3

这条指令的意思是:从共享内存0号通道读取gpsd写入的参考时间,offset 0.5用于补偿串口和线缆带来的固定延迟,refid GPS是给这个参考源起个便于识别的名字。这里的思路其实还是“本地时钟为源”:GPS信号只是作为校准器,chrony持续用GPS信号驯服系统时钟,最终对客户端公布的仍然是本机的NTP服务。

装上GPS授时模块后,这台本地时间服务器的精度能达到毫秒甚至微秒级别,而且长期不用管。适合那种“不仅要求独立,还要求准”的场景。如果你用的系统是老ntpd,同样的参考源配置方式稍有不同,需要使用NMEA驱动的地址和fudge参数,相对麻烦一些。

3.4 防火墙、监听与权限收紧

不管用哪种服务,NTP默认监听UDP 123端口,防火墙要提前放行。以常见的firewalld和ufw为例:

# firewalld firewall-cmd --add-service=ntp --permanent firewall-cmd --reload # ufw ufw allow 123/udp

权限这块也容易踩坑。NTP服务应当以普通用户身份运行,通过restrict指令收紧客户端权限,只允许查询,禁止修改和远程管理。尤其注意不要开放noquery,否则客户端连查询都会被拒。这类服务器最好只跑NTP一个服务,不要顺便挂别的业务,减少系统负载波动对时间精度的影响。

4. 客户端接入:Linux、Windows与验证方法

4.1 Linux客户端一分钟接入

客户端配置要简单得多。以chrony为例,编辑/etc/chrony.conf,把默认的互联网源注释掉,加上内网时间服务器的地址:

# 指向本地时间服务器 server 192.168.1.10 iburst # 其他配置保持默认

这里iburst参数很实用,它让chrony在刚启动时快速发送一串请求,能够在几秒内完成首次同步,而不是慢慢等它按默认节奏轮询。

改完重启服务,手动触发同步:

systemctl restart chronyd chronyc makestep

用timedatectl验证:

timedatectl

关键看两行:NTP synchronized: yes和Time zone。如果显示yes,说明设备已经在使用NTP服务同步时间。

4.2 Windows内网客户端配置

Windows客户端可以走图形界面,也可以直接改注册表。图形界面路径是:控制面板 → 日期和时间 → Internet时间 → 更改设置 → 在服务器输入框里填内网时间服务器IP,然后点击“立即更新”。这条路适用于单台机器手动配置。

如果有一批Windows机器要批量接入,建议用组策略或命令行直接改注册表。命令行方式参考:

w32tm /config /manualpeerlist:192.168.1.10 /syncfromflags:manual /update net stop w32time && net start w32time w32tm /resync

这里有个Windows特有的坑:默认的Windows时间服务(W32Time)在普通工作站上被设计为“非关键服务”,如果它检测不到能同步的源,就会跳过,不会主动报错。所以批量化配置后,一定要抽查几台机器,执行w32tm /query /status确认“Source”一栏是不是你搭的时间服务器IP。

4.3 如何证明时间真的同步上了

配置完成不等于工作正常,我在排查问题的时候从来不会只看客户端有没有显示“已同步”,而是会从服务器端和客户端交叉验证。

服务器端执行:

chronyc sources -v chronyc tracking

chronyc sources -v能看到当前的参考源情况,chronyc tracking会显示本机系统时间的修正进程。客户端执行同样的命令,重点看Stratum这一栏——它应该比服务器端的数字大。如果服务器是stratum 10,客户端显示stratum 11或类似层级就是正常的。

精确一点的测量可以用clockdiff命令:

clockdiff -o 192.168.1.10

如果局域网网络质量好,同步完成后的offset应该在几十毫秒以内,好的情况下能打到个位数毫秒。我自己测试过同一交换机下的设备,稳定状态偏移在0.1ms级别的场景也见过,不过那需要服务器端源本身就非常干净。

5. 实战排查:那些翻过车的问题

5.1 服务器明明在跑,客户端却连接拒绝

这个问题我在很多环境里都遇到过,症状是客户端执行chronyc makestep后报错,或者日志里出现连接失败。

排查顺序要固定。先看服务进程在不在:systemctl status chronyd,确认状态是active。再看端口监听:ss -ulpn | grep 123,确认UDP 123被正常监听。然后看防火墙规则:iptables -L -n | grep 123,或者直接用tcpdump抓包看UDP 123的流量是否从一端发出、另一端无回应。

过程中最不起眼的一个坑是服务器开启了多网卡,NTP服务只默认监听了一个IP。如果客户端访问的地址不是服务监听的地址,就会出现客户端请求发过来了,服务却毫无反应的情况。解决办法是在chrony配置里显式指定监听地址,比如:

bindaddress 192.168.1.10

5.2 客户端同步后时间反而跳变

这个情况更像是配置逻辑错误,而不是服务故障。常见于两种操作不当:一是客户端把服务器当成了唯一源,但同时服务器端local指令配置的层级太低,客户端在校验时产生了误判;二是服务端在系统时间本身偏差很大的情况下,配置了过于激进的makestep策略,结果把客户端直接“带偏”了。

我的处理习惯是:服务器端第一次部署前必须手动把时间校到秒级以内,同时把chrony的makestep策略调得保守一些,比如默认只在启动后前三次同步时允许大步进。客户端那边则可以用默认策略,让它快速跟上服务器。另外,如果内网有不止一台时间服务器,客户端配置时不要把多台来源放在同一份配置里,除非你能保证这些源之间是相互一致且没有形成环路的。

5.3 GPS模块有数据但服务不认

进阶玩法里最常见的坑是GPS模块已经通过串口输出了数据,gpsmon命令也能看到NMEA报文,但chrony却始终认为GPS参考源是离线状态。

这类问题多半出在共享内存的权限上。gpsd写共享内存的进程和chrony读取共享内存的进程,必须都具备相同权限,并且操作的是同一段共享内存编号。排查时可以用:

ls -l /var/run/shm/

检查共享内存文件的属主和权限。另一个常见坑是串口芯片的驱动加载问题,USB转串口的芯片在某些内核版本下会识别成不同的设备节点,比如/dev/ttyUSB0和/dev/ttyUSB1之间跳变。建议用一个固定的udev规则把模块绑定到稳定的设备节点。

最后是天线摆放。GPS授时模块的天线必须放在能接收到卫星信号的位置,不能放在金属机箱内部或者玻璃窗贴上信号衰减严重的地方。模块通电后,gpsmon里能看到卫星数量和定位状态,如果一直是0颗卫星,就别指望时间能出来——这种时候先解决天线和馈线的问题。

5.4 电源与硬件时钟的连环坑

把这个问题放在最后讲,是因为它最具隐蔽性。有一台服务器时间服务器运行了几个月都很稳定,突然某天客户端全部报错,一查服务器系统时间竟然跳到了几个月前。第一反应以为是漂移,结果发现是主板电池没电了。系统在运行期间NTP一直维持着时间,但一旦断电再开机,BIOS里的硬件时钟复位到某个出厂默认时间,加载时系统时间就也跟着变了。

解决思路分两步。第一步是换上新的CR2032纽扣电池,花几块钱的事。第二步是在系统里多重保险,开启rtcsync功能,让服务定期把系统时间写回硬件时钟。以chrony为例,rtcsync默认开启,在/etc/chrony.conf里确保没有被注释掉就行。

另外还有一个值得养成的习惯:整个时间服务器最好配上UPS。频繁的突然断电对扳机供电的稳定性是长期伤害,而且断电重启的过程很容易触发RTC复位。我目前维护的这台时间服务器已经连续跑了快一年半,有UPS兜底之后,再也没出现过于硬件时钟相关的幺蛾子。

做这个项目到后期,我最深的体会是:以本地时钟为源的时间服务器,技术难点根本不在配置那一行行命令,而在于怎么把“持续校准”和“提前兜底”这套心思落到实处。只要把首次校时、漂移记录、权限收紧、硬件可靠性这几件事想明白,一台普通Linux机器就能变成整个内网最靠谱的“钟表铺”。这套方案我已经在不同环境里验证过多次,实操下来成本低、效果好,至少到目前为止,那些曾经因为时间错位而查得焦头烂额的日志问题,在我负责的网络里再没有复现过。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询