OpenEuler 这款系统我用得比较多,最初接手一批 22.03 SP3 的服务器时,第一件事不是装业务,而是先把时间同步搞定。原因很简单:证书校验、日志审计、数据库复制、分布式调度,哪一样都依赖服务器时间。如果机器之间时间差了十几秒,排查问题时会非常痛苦。这次要写的,是 OpenEuler 基础运维里“设置日期和时间”的第一阶段——用 chrony 做时间同步。
作为一个常年跟 Linux 服务器打交道的人,我对时间同步这件事的态度经历了从“无所谓”到“必须先搞定”的转变。早期用 ntpdate 定时跑一次同步,看起来很省事,但服务器时钟漂移、网络抖动、启动时的时间跳变,都容易让服务出现莫名其妙的问题。后来换到 chrony,发现它对网络环境的要求更宽容,收敛速度也更快,尤其适合现代虚拟化和容器环境。这篇文章我就以 OpenEuler 22.03 SP3 为例,把 chrony 的安装、配置、验证、排障完整走一遍,顺便分享一些生产环境里踩过的坑。
1. 为什么OpenEuler的时间管理默认选中chrony
1.1 chrony解决了ntpd的哪些问题
早些年 Linux 服务器做时间同步,基本绕不开 ntpd。ntpd 的历史非常长,功能也确实稳,但它的问题在于启动后的收敛速度太慢,尤其是在系统时间和真实时间偏差较大的情况下,可能需要几十分钟甚至几个小时才能慢慢把时间拉回来。对于开机后马上要跑业务、要校验证书的系统来说,这个时间窗口非常尴尬。
chrony 是另一个开源的 NTP 实现,核心思路和 ntpd 完全不同。它不会死板地“每隔一段时间校正一次”,而是通过数学模型持续跟踪系统时钟的漂移率,然后动态调整频率补偿。这样做的好处有几个:一是同步收敛快,二是对网络延迟和抖动的容忍度高,三是在没有网络的情况下也能基于历史漂移数据维持一个相对准确的时间。你可能听过一个对比说法:ntpd 是“每隔一段时间看一眼表”,chrony 是“一直在心里默算误差趋势”。这个比喻虽然不那么严谨,但方向上是对的。
在实际使用中,chrony 还支持硬件时间戳,可以在网卡支持 PTP 时获得亚微秒级别的精度。当然普通服务器用不到这么高,但在金融交易、科学计算这类场景里,这个能力是实打实的加分项。
1.2 在OpenEuler 22.03 SP3里的定位
OpenEuler 是面向企业级场景的 Linux 发行版,它的默认软件选型通常会照顾稳定性和运维习惯。在 22.03 SP3 里,chrony 基本是作为默认的 NTP 客户端和服务端来提供的,系统安装时就可能被装上。不过国内很多伙伴做精简安装,或者基于最小化模板部署,这时候系统里可能只剩一个 systemd-timesyncd,根本没有 chrony。
systemd-timesyncd 并不是不能用,它适合“只要能同步上时间就行”的轻量场景。但它没有 chrony 那么多调试命令,不支持做时间服务器,也不太好观察同步质量。所以只要你有多台机器,或者打算搭一套内部时间源,我还是建议直接装 chrony。
另外,OpenEuler 的软件源里一直维护着 chrony 包,版本跟随上游更新,不会有功能缺失的问题。我的做法是:不管服务器以后要不要对外提供时间服务,先把 chrony 装上并启用,至少它比 systemd-timesyncd 可控得多。
1.3 时间同步在运维中的影响范围
如果你觉得时间同步只是“date 命令显示得准不准”的事,那就太小看它了。我拆过几起线上故障,最后都收敛到“服务器时间差了几秒”。比如:
- HTTPS 证书校验会失败,因为在 TLS 握手时客户端会检查服务器证书的有效期;
- 数据库主从复制可能中断,尤其像 MySQL 的 GTID 模式,时间差大会影响事务判断;
- 日志系统的时间戳错乱会直接导致排查告警时对不上号;
- 定时任务可能在错误的时间点触发,备份窗口被莫名跳过;
- 分布式服务里的请求超时和重试逻辑,会因为时钟不一致产生连锁问题。
所以,时间同步看起来是一个很基础的操作,但它直接影响整个系统的可观测性和数据一致性。这也是我为什么坚持在每台 OpenEuler 服务器上把时间同步单独列为一个配置项,而不是等出问题了再补救。
2. 安装chrony前的环境准备
2.1 确认系统版本和chrony是否已安装
开始配置之前,先确认两件事:系统版本和 chrony 的安装状态。环境不对,后面所有配置都可能是白做。
cat /etc/openEuler-release rpm -qa | grep chrony如果执行 rpm 查询没有任何输出,说明系统里还没有安装 chrony。有网的情况下直接用 dnf 安装:
dnf install -y chrony安装完成后,先不要急着启动,我们要先把配置和环境整理好。
这里多说一句,OpenEuler 的版本差异会影响配置文件路径和默认参数。22.03 LTS SP3 这一代,chrony 的配置文件是/etc/chrony.conf,服务名是chronyd,日志默认在/var/log/chrony/。如果你用的是更早的版本,最好先man chrony.conf确认一下,别拿旧记忆套新系统。
2.2 静态IP和DNS的检查
时间同步看似只是发一个 UDP 包到 NTP 服务器,但其实对网络环境的依赖比很多人想的要大。第一,你要能访问外网或内网的时间服务器;第二,如果配置里用的是域名而不是 IP,DNS 解析失败会导致同步直接不可用。
用域名做时间源时,DNS 就是命门。我遇到过一台机器,ping 外网 IP 完全通,但chronyc sources里始终显示?,查了半天才发现是/etc/resolv.conf里的 DNS 配置不对,chronyd 把域名解析失败,自然连不上时间服务器。
所以,在配置 chrony 之前,先把静态 IP 和 DNS 检查一遍:
ip addr cat /etc/resolv.conf如果网卡还没配静态 IP,用 nmcli 或者直接改配置文件都行。OpenEuler 上我习惯用 nmcli,因为命令式操作更适合批量执行:
nmcli con mod ens160 ipv4.addresses 192.168.1.100/24 nmcli con mod ens160 ipv4.gateway 192.168.1.1 nmcli con mod ens160 ipv4.dns 192.168.1.1 nmcli con mod ens160 ipv4.method manual nmcli con up ens160这里把网卡名ens160换成你实际的设备名,配置完成后ip addr确认一下。有人问过静态 IP 跟时间同步有什么关系,其实关系很大:如果机器使用 DHCP 获取地址,租约续租出问题会导致网络短暂中断,ala章会扰到持续的时间同步;另外固定 IP 也方便你后续把这台机器配置成内网 NTP 服务器,让其他机器指向它。
2.3 防火墙放行UDP 123端口
NTP 协议走的是 UDP 123 端口,默认情况下 OpenEuler 的防火墙可能会拦截入站的 NTP 流量。如果你只是做客户端,只主动向外发起连接,大部分情况下出站流量默认是放通的,问题不大。但如果你想把这台机器作为内网时间源,让其他服务器也指向它,就必须在防火墙里放行 UDP 123。
firewall-cmd --permanent --add-service=ntp firewall-cmd --reload firewall-cmd --list-all如果你不想用 service 方式,也可以直接开端口:
firewall-cmd --permanent --add-port=123/udp firewall-cmd --reload在日常排障时,很多人会先把 SELinux 和防火墙全部关掉来定位问题,这不适合生产环境。我更推荐的做法是在最开始就把规则写对,然后用firewall-cmd --list-all确认,避免后续被各种安全策略干扰。
3. chrony的核心配置与选型思路
3.1 /etc/chrony.conf 逐项分析
安装完成后,最重要的事情就是编辑/etc/chrony.conf。我先把一份常用配置贴出来,再逐条解释每个参数的意义,这样你在后续调优时能知道自己在改什么。
# 时间服务器来源 server ntp.aliyun.com iburst server ntp1.aliyun.com iburst server ntp2.aliyun.com iburst pool 2.pool.ntp.org iburst # 允许局域网内的客户端访问本机时间服务 allow 192.168.1.0/24 # 本地时钟作为后备 local stratum 10 # 当系统时间偏差超过1秒时,前3次轮询直接步进 makestep 1 3 # 启用RTC同步 rtcsync # 日志目录 logdir /var/log/chrony第一条server指定 NTP 服务器地址,后面的iburst参数表示启动时快速发送几个包,以便在短时间内完成第一次同步。这个参数我建议保留,尤其是服务器刚开机、时间偏差很大的时候,iburst能明显缩短校准时间。
pool和server的区别在于,pool会从域名解析结果里拿多个 IP,并自动选择一个最优的作为同步源,适合没有固定 NTP 服务器的场景。如果你有明确的内网时间源,直接用server更可控。
allow是关键的一个配置,它决定了谁可以请求本机的 NTP 服务。如果你不需要对外提供时间服务,这一行可以不写。但如果你打算让这台机器成为内网时间源,就把它设置为你信任的网段,例如allow 10.0.0.0/8,而不是直接写成allow all。
local stratum 10的意思是,当所有上游时间服务器都不可达时,chronyd 会宣布自己作为第 10 层时间源继续对外服务。这样做的目的是避免内部机器反复切换时间源。需要注意的是,local会让本机在失联时仍然认为自己的本地时钟有效,如果你的机器不是权威时间源,这个参数最好别乱加。
makestep 1 3是我重点想说的一项。默认情况下,chronyd 不会让系统时间发生大的跳变,而是通过微调频率慢慢校准,这样对应用更友好。但当时间偏差超过 1 秒,并且系统刚启动还处于前 3 次轮询阶段时,直接步进到正确时间反而更有价值。它的格式是makestep 阈值 次数,你可以根据业务容忍度灵活调整。
rtcsync的作用是让 chronyd 每 11 分钟把系统时间同步到硬件时钟 RTC。这个参数在生产环境里非常重要,因为如果系统时间和硬件时钟差太多,一旦操作系统因异常重启,时间就会回退到很久之前的值,后面所有依赖时间的服务都会遭殃。
3.2 选择时间源:server、pool与本地时钟
配置时间源时,我见过两种极端情况:一种是什么都不改,用默认的 pool.ntp.org;另一种是因为和上游时间源连接不稳定,干脆只配一个本地时钟。两种都不是最优解。
对于能够访问外网的机器,我建议至少配置两个外部时间源,并且尽量选地理位置近的。距离越远,网络抖动越大,虽然 chrony 能容忍,但收敛速度和精度都会受影响。例如在国内,可以选择阿里云的 NTP 服务,或者教育网的 NTP 服务;有条件的企业也会在 IDC 里放一台 GPS 或北斗授时服务器,对外提供内部时间源。
对于不能访问外网的机器,NTP 分层结构就比较重要了。你可以把一台能出外网的机器作为第一层,然后将它设置为内网时间源,其他机器再指向它。这种情况下,allow的网段范围要精确控制,千万不要放开到整个外网,否则一旦 IP 暴露,会成为别人免费利用的 NTP 放大器。
本地时钟local的定位是后备方案,而不是首选方案。因为服务器本身的晶振精度有限,时间会持续漂移,长期依赖本地时钟会导致所有下游机器时间越来越偏。所以我会把它理解为“断网兜底”,而不是常配项。
3.3 离线环境、内网环境和多级服务器的配置建议
如果整个环境完全离线,没有外部时间源,那只能退而求其次:要么靠服务器 RTC,要么自己接授时设备。这种情况下配置会简单很多:
server 127.127.1.0 local stratum 10这里的127.127.1.0是 chrony 内置的本地时钟伪设备。不过这样配置的精度非常有限,不建议多台机器都这样做。如果离线环境里有一台能接 GPS/北斗授时模块的机器,更合理的方式是让这台机器作为唯一的时间源,其他机器全部通过server指向它。
在内网环境中,我习惯把时间源分几层:第一层直接同步外部权威时间源;第二层若干台业务服务器指向第一层,同时开放给局域网内的其他机器;第三层设备只需要指向第二层即可。这样的好处是,即使第一层机器短暂失联,整个内网的时间也能保持一致,不会东倒西歪。
另外,多级服务器一定要特别注意makestep和local的组合。上游时间源故障时,下游机器会选择本地时钟,但如果每级都设置local stratum 10,就可能出现“用出错的本地时间互相同步”的假象。所以对于严格要求的场景,高层的服务器不要轻易加local,宁可让下游报错,也要保证不会把错误时间扩散开。
4. 启动chrony并验证同步效果
4.1 服务管理和开机自启
配置写好以后,先检查语法是否正确,再启动服务。chrony.conf 的语法检查没有专门的命令,但你可以通过启动服务后的日志状态判断。
systemctl enable --now chronyd systemctl status chronydenable --now会把开机自启和立即启动一起做掉,这是我在部署时最偏好的一条命令。启动完成后,执行systemctl status chronyd看服务状态,如果显示active (running),说明配置文件基本没问题。
如果你修改了配置文件,需要重启服务才能生效:
systemctl restart chronyd如果不想中断当前同步,也可以执行chronyc reload sources,这个命令会重新加载时间源配置,但不会重启整个服务。在频繁调整时间源的场景下,这个命令非常实用。
4.2 chronyc sources命令怎么看
服务启动后,验证同步效果最直接的命令是chronyc sources。我建议先加上-v参数,显示更详细的信息:
chronyc sources -v输出里会有一个表格,每行表示一个时间源。列头里的^*表示当前正在使用的时间源,^+表示可用的候补源,^-表示不可用源,^?表示不可达。如果看到^*出现,说明 chrony 已经成功锁定了一个时间源,同步已经开始工作。
另一个命令chronyc tracking也非常关键:
chronyc tracking它会告诉你系统时间当前与标准时间的偏差、频率漂移率、每轮同步的延迟等指标。其中System time字段如果显示0.000000000 seconds fast,说明时间已经对齐;如果数值在正负几百毫秒以内,说明还在收敛过程中,可以稍微等一会儿再看。
在生产环境里,我习惯把chronyc tracking和chronyc sources的结果放在同一个窗口里对比:先看 sources 里是否^*,再确认 tracking 里的偏差是否在可接受范围。这能避免你看到表面正常、实际同步无效的假象。
4.3 手动校时与makestep的使用场景
大多数时候我们只需要等待 chrony 自动同步。但有一种情况例外:新装的服务器时间偏差特别大,甚至超过了好几天。这时 chrony 默认的平滑调整方式会花很长时间,应用启动和证书验证都会出问题。最好的办法是手动校时一次。
chronyc makestep这个命令会强制让系统时间立即跳到当前 NTP 时间,无论偏差是多少。它的触发条件还受配置里makestep 1 3的控制——在服务启动后的前 3 次轮询中,如果偏差超过 1 秒,chrony 就会自动步进。手动执行则没有这个次数限制,立刻生效。
另一个常用的命令是timedatectl:
timedatectl它能显示当前系统时间、RTC 时间、时区以及 NTP 是否启用。如果显示NTP service: active,说明 chronyd 在被 systemd 管理时已经正常接入了。timedatectl set-ntp true也常用于开启 NTP 服务,不过 OpenEuler 上只要 chronyd 正常运行,这个状态通常会自动是活动的。
5. 常见问题排查与避坑
5.1 chronyc sources显示问号的排查流程
如果你执行chronyc sources -v后看到时间源状态是?,说明这台机器无法从上游时间源获取时间。遇到这种问题,我的排查步骤基本固定:
systemctl status chronyd chronyc sources -v chronyc tracking journalctl -u chronyd -n 50先看服务本身是不是还活着,再看时间源状态,最后看日志。日志里经常会有Name server cannot be used或Source ... unreachable这样的提示。如果日志显示的是域名解析失败,按 2.2 节的方法检查 DNS;如果是网络连接超时,检查防火墙是否放行 UDP 123 端口,以及是否配置了能到达时间源的路由。
还有个小细节容易被忽略:如果时间源写在配置里是域名,但本机/etc/chrony.conf里启用了某些安全策略,或者系统使用了比较严格的上游 DNS,解析时可能拿到 IPv6 地址,而你的网络并没有 IPv6 路由。这时候可以尝试在配置里写 IP,或者给域名后面追加maxdelay等参数来过滤异常的响应。
5.2 虚拟机、容器和物理机的时间漂移差异
我在生产环境里发现,物理机和虚拟机的时间漂移行为差别非常大。物理机有独立 RTC,就算系统挂掉了,时间也会走;虚拟机则完全依赖宿主机,一旦宿主机负载过高,虚拟机的时钟源就可能出现明显漂移,甚至出现时间回退。
对于 KVM 虚拟机,建议先看看使用的时钟模型。OpenEuler 在 KVM 下一般默认使用 kvm-clock,这个驱动能把客户机时间与宿主机时间对齐,减少漂移。但如果漂移问题仍然严重,就需要确保 chronyd 在虚拟机里正常运行,并且不要人为禁用时间同步。
容器环境则是另一个极端。容器共享宿主机的内核,默认情况下容器内的进程不能直接修改系统时间,否则会因为没有SYS_TIME权限而报错。如果你在容器里跑的是应用,时间直接用宿主机的;如果你确实要在容器内运行包含时间同步的进程,需要额外配置容器的 capabilities,这已经属于比较深层的容器安全范畴了。对于大多数容器使用场景,我建议把时间同步放在宿主机层面统一解决,容器内不要重复折腾。
另外,不管什么环境,都要留意rtcsync是否打开。虚拟机在重启后会从宿主机重新获取时间,影响不大;但物理机如果没启用 RTC 同步,断电重启后时间可能停在上次关机时。这是非常经典的“重启后时间不对”的原因。
5.3 时间跳变引发的业务问题怎么预防
时间跳变是很多业务最害怕的事情。突然往前跳 1 秒,对日志时间戳影响不大,但对数据库锁、分布式事务、缓存过期时间、消息队列延迟统计都可能造成误判。chrony 默认倾向于平滑调整,这是为了保护应用。但你手动执行chronyc makestep,或者配置了过于激进的makestep,就可能造成跳变。
预防思路是分场景:对时间精确度要求高的系统,比如交易系统、数据库主从,我一般把makestep的阈值调低,比如makestep 1 3保持不变,但在部署初期就手动校时一次,之后不再频繁执行 makestep;对普通业务服务器,平滑调整就够了,没必要大幅步进。
还有一个容易忽略的点:如果你用 chrony 充当时间服务器,客户端的同步策略会影响整体稳定性。客户端如果设置了过于频繁的poll,会给时间服务器带来不必要的压力,甚至还可能被下游设备的风控策略误判。一般建议 poll 间隔保持在 64 秒到 1024 秒之间,不需要刻意追求高频。
6. 把chrony用好的一些细节
6.1 不要忘记rtcsync:硬件时钟维护
很多人配置完 chrony 后,只记得看系统时间,却忽略了硬件时钟。rtcsync这个参数我再次强调,它让 chronyd 定期把系统时间同步到主板上的 RTC。如果没有它,或者它被某条安全策略禁用了,下次开机时系统时间会直接读取上次遗留的 RTC 值,跟真实的当前时间差得离谱。
用hwclock --show可以查看当前硬件时钟:
hwclock --show timedatectl如果硬件时钟和系统时间差很多,可以手动校正一次:
hwclock --systohc这个命令会用系统时间去覆写硬件时钟。但更稳妥的方式,还是让 chronyd 的rtcsync机制自动完成,减少人工干预。
6.2 延伸场景:嵌入式平台与交叉编译chrony
聊到 OpenEuler,还有个绕不过去的场景是嵌入式平台。很多 AI 边缘设备、工业控制板用的都是 OpenEuler 或基于 OpenEuler 的发行版,比如 RK3588 这类 ARM 平台。在 ARM 板上跑 chrony 通常有两种方式:一种是用发行版自带的软件源直接安装,另一种是拿到源码交叉编译。
如果你需要交叉编译 chrony,流程大致是这样的:先下载 chrony 源码,然后在 x86 主机上配置交叉编译工具链,比如aarch64-linux-gnu-gcc,再执行:
./configure --host=aarch64-linux-gnu make编译出来的二进制文件可以拷贝到目标板运行,但要注意依赖库是否完整。chrony 依赖于libcap、libseccomp、readline这些库,如果目标系统缺失,需要一起移植过去或者静态编译。
不过如果你是做常规的 OpenEuler 服务器运维,我并不建议一上来就交叉编译,直接用dnf install chrony是最省事、最稳定的做法。交叉编译更多是给嵌入式和特殊定制场景准备的,普通服务器用不到这么复杂。
6.3 日常运维里我常用的几个chronyc命令
最后把我日常用得比较多的chronyc命令整理一下,有些你可能已经见过,有些是管理层会用到的高级命令:
| 命令 | 作用 |
|---|---|
| chronyc tracking | 查看系统时间偏差和漂移率 |
| chronyc sources -v | 查看时间源状态和同步选择结果 |
| chronyc sourcestats | 查看每个时间源的统计信息 |
| chronyc makestep | 立即步进系统时间 |
| chronyc clients | 查看哪些客户端在请求本机时间服务 |
| chronyc activity | 查看当前所有时间源的活动状态 |
其中chronyc clients适合时间服务器管理员使用,它可以显示局域网里有多少台设备在依赖这台服务器校时。这个命令在排查“谁在吃我的 NTP 流量”时特别有用。
实际部署中,我习惯在完成时间同步后立刻把timedatectl和chronyc tracking的输出记进服务器档案里,因为后续排查单据超时或证书失效时,这几行内容能帮我省下很多沟通成本。chrony 的配置并不复杂,真正决定它好不好用的,是环境整理和日常观察的功夫。