ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Linux NTP时间服务器搭建与chrony内网同步配置实战

Linux NTP时间服务器搭建与chrony内网同步配置实战 时间这东西平时没人留意一旦不对就是连环事故。日志时间戳乱序、分布式节点互相认为对方的消息“来自未来”、数据库主从因为时间差导致数据对不上、定时任务在凌晨多跑一次或者干脆不跑——这些锅最后追下来十有八九都指向一个原因机器的时间不准。Linux ntp时间服务器的搭建和配置这件事说简单点就是让局域网里几十上百台机器统一听一个“报时人”的话说复杂点里面涉及分层授时、漂移补偿、防火墙穿透、虚拟化时钟冲突一堆细节。我前后在几套内网环境里折腾过这套东西从传统的 ntpd 到现在的 chrony踩过的坑足够写一篇避雷指南。这篇文章面向的是手里有 Linux 服务器、需要做内网统一时间的运维和开发同学也照顾刚接触这块的新手我会把每一步为什么这么做讲清楚配置直接给到能抄的程度。1. 内网时间不准到底会捅出哪些娄子1.1 时间漂移不是玄学是物理规律先破除一个误解服务器上的时间不是“准的”它只是“暂时没那么离谱”。主板上的晶振受温度、电压、老化影响频率会有微小偏差行业里常说的量级是每百万偏差几个到几十个 ppm。换算成实际感受一台普通服务器每天慢个几百毫秒到一两秒非常正常一个月累积下来差几十秒毫不稀奇。虚拟机更夸张vCPU 被调度暂停、宿主机负载高的时候时钟直接“卡住”这种漂移是无规律的。你可能会想那机器自己在后台校准不就行了问题在于默认配置里很多发行版用的是systemd-timesyncd它只做最基础的 SNTP 同步精度和抗抖动能力都有限而且它和 chrony、ntpd 是互斥的同时跑必然打架。更关键的是几十台机器各自去找公网时间源同步时刻不同、网络延迟不同、上游源也不同最后结果是每台机器都“各自准确”但彼此之间差着几百毫秒——这在单机场景无所谓在集群场景就是灾难。提示判断一台机器时间是否靠谱不要只看date显示的数字那个数字看着都挺正常。要看的是它和上游源的偏差量以及这个偏差是在收敛还是在发散。1.2 哪些场景必须自建时间服务器不是所有环境都需要自建。如果只有两三台机器直接指向国内公共时间源就行。但下面这几种情况下自建内网时间服务器几乎是必选项。第一种是集群类应用。Hadoop、Spark 这类框架对节点间时间差有硬性容忍度超出范围任务会直接失败数据库主从复制、分布式锁、Raft 选举这些机制底层都假设各节点时钟大致一致。第二种是日志与审计。安全事件追溯时如果 A 机器和 B 机器的日志差了半分钟事件顺序就完全对不上了排查等于白做。第三种是内网隔离环境。生产网往往不允许所有机器直接访问外网只能开一台机器作为出口由它统一对外取时、对内授时。第四种是合规与业务要求。某些行业的交易、计费、监控系统对时间戳有明确精度要求。这里有个很实际的考量公网时间源虽然免费但可用性和延迟不稳定几十台机器同时去请求遇到网络抖动就集体偏。自建一台内网服务器让它长期与多个上游源保持同步其余机器只跟它同步链路短、延迟低、抖动小整体稳定性反而更高。1.3 NTP 的分层结构五分钟看懂NTP 用 stratum 层级来描述时间源的“远近”。Stratum 0 是原子钟、GPS 这类物理基准不直接联网。Stratum 1 是直接连着 Stratum 0 的服务器。Stratum 2 从 Stratum 1 取时依此类推最大到 1516 就被认为不可用。我们自建的内网服务器通常处于 Stratum 2 或 3它自己从公网 Stratum 1/2 源同步同时对内网提供授时服务。内网客户端再往下就是 Stratum 3 或 4。层级越深理论精度越差但在局域网里每一跳的延迟往往只有零点几毫秒实际影响可以忽略。NTP 同步的本质是“测量补偿”不是简单地把时间抄过来。客户端会发若干次请求根据往返延迟算出网络传输耗时再推算上游的真实时刻最后决定是“缓慢调整”还是“跳变”。缓慢调整叫 slew通过略微加快或放慢本地时钟频率把偏差一点点抹平这个过程不影响时间单调递增跳变叫 step直接把时间设过去通常只在偏差很大或者服务刚启动时使用。理解这一点很重要因为它直接解释了后面你会遇到的一个现象明明配置对了chronyc sources也显示正常但date看着没变化——那是在缓慢校准不是没生效。1.4 选 chrony 还是 ntpd别纠结太久老资料里基本都是 ntpd现在的发行版默认给的是 chrony。两者都能干这活但 chrony 在几个方面更适合当下的环境它对网络抖动的适应性更好同步收敛更快支持在虚拟机、网络时断时续的场景下工作配置文件更直观客户端管理工具chronyc的输出比ntpq友好不少。CentOS 7 之后、Ubuntu 18.04 之后基本都预装了 chronyRHEL 系从 8 开始直接把 chrony 作为默认。我的建议很直接新环境一律用 chrony除非你有历史包袱或者某些老设备只认 ntpd 的协议行为。本文会以 chrony 为主线同时给出 ntpd 的对照配置方便你在混合环境里改。2. 动手前的环境确认与源站选择2.1 先把系统现状摸清楚配置之前先做体检避免边配边发现冲突。第一件事是确认当前有没有别的时间同步服务在跑。执行下面几条命令看看# 查看时间同步服务状态 timedatectl status # 检查 chrony / ntp / systemd-timesyncd 是否在运行 systemctl status chronyd 2/dev/null systemctl status ntp 2/dev/null systemctl status systemd-timesyncd 2/dev/nulltimedatectl的输出里有几行关键信息System clock synchronized表示系统时钟是否已同步NTP service表示当前由哪个服务在管时间RTC in local TZ表示硬件时钟是用本地时区还是 UTC 存储。这几个值后面都要用到。接下来确认时区。内网服务器统一用 UTC 存储、按需显示本地时区是最稳的做法能避免夏令时和跨时区带来的混乱。设置命令timedatectl set-timezone Asia/Shanghai再确认硬件时钟的存储方式。Linux 内核默认期望 RTC 存的是 UTC如果你的机器之前被设成 local time重启后可能出现一次性的大跳变这会干扰 NTP 的正常运行。用timedatectl set-local-rtc 0切回 UTC 存储除非有 Windows 双系统之类的特殊需求。2.2 上游时间源怎么选才靠谱选源的核心原则是多个、地理近、延迟低、别都指向同一个。NTP 协议本身会做源筛选算法叫 intersection/clustering给 4 个以上的源它才能有效剔除“说谎”的源。只配 1 个源一旦这个源抽风你的服务器就跟着跑偏。公共时间源方面cn.pool.ntp.org是国内常用的池服务它会自动解析到多个国内节点ntp.aliyun.com、ntp1.aliyun.com这类大厂提供的源延迟一般较低time.windows.com也可以作为备选。说句实在话pool域名解析出来的 IP 有时质量参差如果你的环境对精度要求高最好先手动 ping 或者用chronyd -Q做一轮探测看看哪个源延迟稳定再固定下来。内网服务器的配置思路是配 4 到 6 个公网源作为上游同时把内网网段加入白名单对外授时。为了在外网全断时也能继续对内提供服务可以启用本地时钟兜底chrony 的local指令这样即使上游全挂它也会以自身时钟降级对外授时保证内网时间至少是单调一致的。这点很实用很多方案漏了这一步一断外网内网就乱套。2.3 防火墙、SELinux 和虚拟化时钟的坑NTP 走的是 UDP 123 端口这个端口必须在服务端放行否则客户端请求根本到不了。firewalld 环境firewall-cmd --permanent --add-servicentp firewall-cmd --reloadiptables 环境iptables -A INPUT -p udp --dport 123 -j ACCEPT service iptables saveSELinux 在主流发行版上默认已经允许 NTP 服务一般不用额外处理。但如果之前手动改过策略可以用getenforce确认状态出现异常时用ausearch -m avc -ts recent查拒绝日志。真正的隐藏坑在虚拟化层。VMware、KVM 这类平台提供的“宿主机时间同步”功能VMware Tools 里的时间同步选项会和 NTP 抢着调时钟两者同时工作时系统时间会被来回拉扯表现为时间反复跳、chronyc tracking里偏差忽大忽小。正确的做法是二选一要么关掉虚拟化平台的时钟同步交给 chrony 管要么依赖平台同步别装 NTP 服务。生产环境一般选择后者作为兜底、前者作为主控但要明确关闭平台侧的强制同步开关。3. 服务端配置全流程照着做就行3.1 安装与启动Debian/Ubuntu 系apt update apt install -y chronyRHEL/CentOS 系yum install -y chrony # 或 dnf install -y chrony安装完先别急着启动因为要改配置。如果系统里已经有systemd-timesyncd在跑先把它停掉并禁用避免和 chrony 冲突systemctl stop systemd-timesyncd systemctl disable systemd-timesyncd然后编辑配置文件。chrony 的主配置在/etc/chrony.confDebian/Ubuntu 有时是/etc/chrony/chrony.conf改完再systemctl enable --now chronyd启动。3.2 配置文件逐段拆解下面是内网服务端一份可直接用的配置我按功能块解释每个参数为什么要这么写。# 上游时间源配 4 个以上便于算法筛选 pool ntp.aliyun.com iburst server ntp1.aliyun.com iburst server cn.pool.ntp.org iburst server time.windows.com iburst # 关键参数首次同步时允许的最大跳变步长秒 makestep 1.0 3 # 记录时钟漂移重启后仍能沿用历史频偏加快收敛 driftfile /var/lib/chrony/drift # 允许内网网段发起同步请求 allow 192.168.1.0/24 allow 10.0.0.0/8 # 上级源全挂时以本地时钟作为降级授时源 local stratum 10 # 强制让硬件时钟跟随系统时钟 rtcsync # 日志排查时很有用 logdir /var/log/chrony log measurements statistics tracking逐条说。iburst是让客户端启动时快速发一串请求通常 4 个包间隔 2 秒而不是按正常间隔慢慢来能把首次锁定时间从几分钟压缩到几秒。makestep 1.0 3的意思是如果偏差超过 1 秒允许前 3 次同步时直接跳变超过 3 次之后不再跳变改为缓慢校准。这个设计是为了防止时间被频繁“硬调”导致应用出错——启动阶段可以快稳定运行后要稳。driftfile用于记录本机时钟相对标准的频率偏差。有了它chrony 重启后能直接沿用历史频偏做补偿收敛速度明显更快这也是为什么不要把它放在内存文件系统上。allow是服务端的核心授权指令只有写进来的网段才被允许同步。这里的授权范围一定要收紧公网环境千万别写allow all否则你的服务器就成了公开的 NTP 反射攻击源流量会被打满还会被投诉。local stratum 10是兜底方案。当所有上游源都不可达时chrony 会降级为 stratum 10 的本地时钟源继续对内授时。效果是内网各机器仍然能跟着这台机器保持一致虽然此时绝对时间可能不准但相对一致等外网恢复再自动纠正。这个指令后面必须跟一个 stratum 数字写 10 是比较常见的做法。rtcsync让内核定期把系统时间写回硬件时钟默认每 11 分钟一次保证断电重启后时间不会差太多。3.3 选 port 和 fork 的取舍有一个老生常谈的配置项port 123默认就是 123通常不用写。如果你出于某些原因想换端口比如内网安全策略限制那服务端和客户端要同步改客户端的写法是server x.x.x.x port 1234。实话说能不改就不改NTP 默认端口统一是有道理的改完以后交接和维护的人容易懵。还有一个bindcmdaddress控制chronyc管理接口监听在哪。默认是 127.0.0.1只允许本机连。如果你希望通过网络远程用chronyc管理这台服务器需要额外配置并做好访问控制一般没必要SSH 上去执行更安全。3.4 ntpd 环境的对照配置如果你维护的是老系统/etc/ntp.conf的对应写法是这样的server ntp1.aliyun.com iburst server cn.pool.ntp.org iburst driftfile /var/lib/ntp/drift restrict default nomodify notrap nopeer noquery restrict 127.0.0.1 restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap注意restrict的语义和 chrony 的allow不同它是“限制列表”默认那条表示禁止一切修改类操作只允许查询后面再逐条放开内网网段。ntpd 还有个tinker panic 0的调优项作用是当偏差过大时不直接退出而是继续慢慢调整在虚拟机环境里挺有用。响应查询这块restrict ... noquery表示不允许对该地址做ntpq查询但同步请求时钟服务本身不受noquery影响所以内网客户端仍然能正常同步。很多人第一次看到noquery会以为禁止了授时其实不是这个坑我见过不止一个人踩。启动和自启命令systemctl enable --now ntpd # 或者老系统 chkconfig ntpd on service ntpd start4. 客户端接入与批量下发4.1 单台客户端的标准动作客户端配置比服务端简单核心就是指向内网服务器。用 chrony 的话编辑/etc/chrony.conf把上游源改成你的服务器地址server 192.168.1.10 iburst makestep 1.0 3 driftfile /var/lib/chrony/drift rtcsync这里有个新手常犯的错误客户端配置里保留了默认的 pool 公网源同时又加了内网服务器。结果客户端一边连内网一边连公网到底跟谁走完全看算法心情日志里会出现反复切换的情况。正确做法是清掉或注释掉所有其他源只留内网服务器最多再留一个公网源作为应急备份前提是这台客户端能上外网。如果这台客户端能用 ntpd那就改/etc/ntp.conf里的 server 行restrict部分至少保留restrict default nomodify notrap nopeer noquery和restrict 127.0.0.1。改完配置重启服务systemctl restart chronyd # 或 ntpd systemctl enable chronyd4.2 验证同步是否真的生效重启后别急着下结论第一次同步需要几十秒。用chronyc观察# 查看偏差、层级、频率等汇总信息 chronyc tracking # 查看源列表及状态 chronyc sources -v # 查看源统计偏差、抖动、丢包 chronyc sourcestats -v # 立即发起一次同步 chronyc makestepchronyc sources -v输出里的符号含义要记住^*表示当前选中的同步源^表示备选可用源^-表示被算法淘汰的源^?表示不可达或状态未知。看到^*才算真正锁定。chronyc tracking里的System time那一行是当前偏差正常稳定后应该在几十微秒到几毫秒之间Frequency是你的机器时钟频偏单位 ppm这个值没有绝对值好坏关键看它是否稳定。ntpd 的对应命令是ntpq -p看源、ntpq -c rv看运行状态。ntpq -p里最左边一列前的*表示当前同步源表示备选-表示淘汰x表示异常源含义和 chrony 基本一致。4.3 检查硬件时钟与时间单调性同步完成后确认硬件时钟和系统时钟一致hwclock --show date -u两者应该大致一致允许秒级以内的差异因为读写时刻不同。另外提醒一点不要在这时候用date -s手动改时间那会和 NTP 服务打架导致服务重新进入大步长校准。要改时间就通过 NTP 本身实在需要手动设先停服务、改完、再启动。4.4 批量下发的几种思路机器一多不可能一台台改。常见的做法有这么几种。第一种是配置管理工具Ansible、SaltStack 都行写一个模板把 chrony 配置渲染出去再重启服务这是最规范的做法。第二种是发一个安装脚本脚本里判断系统类型、装包、替换配置、启动、自检适合临时环境。第三种是用 DHCP 选项下发 NTP 服务器地址Windows 和部分 Linux 客户端能从 DHCP 里读取好处是零配置缺点是不同客户端对这种选项的遵循程度不一。我个人的偏好是第一种因为配置文件的差异Debian 路径和 RHEL 路径不同用变量控制最省事。贴一个 Ansible 任务的核心骨架注意路径判断- name: 部署 chrony 配置 hosts: all tasks: - name: 安装 chrony package: name: chrony state: present - name: 下发配置 template: src: chrony.conf.j2 dest: {{ /etc/chrony.conf if ansible_os_family RedHat else /etc/chrony/chrony.conf }} owner: root group: root mode: 0644 notify: restart chronyd - name: 关闭 timesyncd 避免冲突 systemd: name: systemd-timesyncd state: stopped enabled: false when: ansible_os_family Debian handlers: - name: restart chronyd systemd: name: chronyd state: restarted模板里把server那一行用变量替换成你的内网服务器 IP一套模板通吃整个集群。5. 排查实录同步不上的那些人那些坑5.1 排查顺序别乱从下往上遇到同步不上很多人上来就重启服务其实有更清晰的排查路径。我习惯按这个顺序走先看服务在不在跑再看端口通不通再看配置对不对最后看源选没选中。第一步确认进程和端口systemctl status chronyd ss -lunp | grep 123服务端应该能看到chronyd监听在0.0.0.0:123UDP。如果只有127.0.0.1:123说明配置里的allow或绑定地址有问题外部连不上。第二步在客户端验证网络可达性。NTP 是 UDPping只能证明主机活着证明不了端口通。更好的办法是用chronyc自己去看或者用nc -u -z 192.168.1.10 123探测端口。第三步核对配置。重点看这几处内网服务器 IP 写对没写错、有没有残留的公网 pool、allow网段是否包含客户端、防火墙是否放行。这几处占了我遇到问题的八成以上。第四步看源状态。chronyc sources -v如果全是^?说明根本没连上源如果有^*但tracking里偏差一直不掉说明连上了但没同步成功这时候要看是不是时钟偏差太大触发了保护机制。5.2 时间偏差太大被“保护”了NTP 有个安全机制当本地时间和上游偏差超过约 1000 秒默认 panic 阈值的时候很多实现会选择退出或者拒绝同步理由是“偏差这么大可能是配置错误或者源本身就错了”。这在机房搬迁、硬件换电池、长期关机后重启的场景里很常见。遇到这种情况手动做一次大跳变再让 NTP 接管。停掉服务用date -s或ntpdate如果还在把时间大致调到接近真实值再启动 NTP 服务它就能正常收敛了。chrony 用户也可以直接chronyc makestep强制跳变前提是makestep指令允许。注意chronyc makestep会让系统时间瞬间跳变如果这台机器上跑着对时间敏感的服务比如某些数据库最好挑维护窗口做跳变可能导致事务时间戳异常或者连接超时。5.3 常见问题速查表现象可能原因处理办法客户端chronyc sources全是^?网络不通 / 防火墙挡了 UDP 123检查防火墙规则和ss -lunp监听地址服务端只能本机同步外部连不上allow未包含客户端网段在服务端配置里补上对应网段时间同步成功但反复跳变虚拟化平台时钟同步与 NTP 冲突关闭 VMware Tools/KVM 的时间同步偏差一直很大不收敛偏差超阈值被保护手动先粗调再让 NTP 接管服务重启后时间又偏了硬件时钟未保持同步配置rtcsync确认 RTC 存 UTC客户端同步到了公网源配置里残留默认 pool清掉多余源只保留内网服务器timedatectl显示未同步systemd-timesyncd和 chrony 抢占停用并禁用 timesyncd同步延迟忽高忽低源质量差或网络抖动换低延迟源配多个源让算法筛选这张表基本覆盖了我这几年遇到的所有典型情况出问题先对照着看能省不少时间。6. 长期运维该盯哪些指标6.1 监控项与观测方法搭好不是终点时间服务需要长期盯着。我一般监控三件事一是同步状态源有没有一直锁定在^*二是偏差量chronyc tracking里的System time如果持续偏大或者持续上升说明补偿跟不上了三是服务存活端口和进程是否在。写成脚本很简单把chronyc tracking和chronyc sources的输出采集一下塞进监控系统做趋势图。偏差量做告警时阈值别设太紧机器冷启动阶段波动是正常的我看过设置 1 毫秒告警结果天天响的案例纯属自找麻烦。还有一点硬件时钟电池是有寿命的。服务器用了五六年之后主板纽扣电池没电会导致每次断电重启时间回到出厂值表现为开机后偏差巨大、NTP 要花很久才拉回来。这种情况换块电池比调 NTP 有用得多。6.2 我踩过的几个坑第一个坑是权限和端口冲突。同一台机器上如果装了两套时间服务谁先启动谁占用 123 端口另一个就起不来日志里报Address already in use。别只看systemctl status显示 active 就以为没问题要确认到底哪个进程在真正监听。第二个坑是 SELinux 下的日志目录。chrony 默认往/var/log/chrony写日志如果这个目录是你手动建的、权限和上下文不对服务会静默地不写日志排查时什么都看不到。用restorecon -Rv /var/log/chrony修一下上下文就好。第三个坑是容器环境。在容器里跑 NTP 服务需要特权模式而且容器和宿主机共享内核时钟很多时候容器内同步会直接改宿主机时间行为很反直觉。我的建议是容器里别跑 NTP 服务交给宿主机统一管理容器通过挂载或者直接读取系统时钟来用。第四个坑最隐蔽CTF 或者靶场环境里练习 NTP 配置时发现怎么配都不生效最后发现是实验环境本身把 UDP 123 的出站流量给限了。排查到网络层之前完全想不到白白折腾一下午。6.3 几个提升稳定性的实用参数如果你想让这套服务更抗造可以额外考虑这几个点。配置多个上游源是基础4 个起步网络质量一般的话适当调大maxpoll减少请求频率减轻源压力对精度要求高的场景可以在服务端加 GPS 或者 PTP 硬件时钟作为 stratum 1 源普通业务没必要走到这一步。日志时长也要考虑。chrony 的 measurements 日志会持续增长长期不清理会占满磁盘。配合 logrotate 做个轮转或者监控分区使用率别等写满了才发现。7. 一点实操上的个人体会整套东西配下来配置本身不到二十行真正花时间的是确认环境和排查那些看起来毫不相关的干扰项。我的经验是第一次部署时把每一条配置的意图在注释里写清楚一年后回来看还能立刻明白当初为什么这么写另外把服务端配置和客户端配置做成两个独立的模板分别管理比混在一起改要清爽得多。新环境一开始就禁用systemd-timesyncd、关闭虚拟化平台的时钟同步、明确 RTC 存 UTC这三步做完后面百分之九十的诡异问题都不会出现。要是你正准备在一批新机器上铺时间服务按这个顺序走一遍基本能一次成型。
返回列表