一、Linux 时间管理基础

1.1 为什么时间必须准确

时间是运维体系里最容易被忽视、却最容易引发"灵异故障"的基础设施。一台机器时间不准,往往表现为完全不相关的业务异常。

  • 日志排序与关联分析:一次请求跨越 Nginx、Tomcat、MySQL、Redis 多台机器,排障时需要按时间轴把各机器日志拼接起来。若某台机器时间偏差 3 分钟,日志顺序就会颠倒,因果关系被彻底破坏。集中式日志系统(ELK、Loki)按 @timestamp 排序与聚合,时间错乱会导致查询窗口内根本查不到数据。

    详见 08-04-ELK日志分析.md

  • 定时任务:crond / at / anacron 都以系统时间为触发依据。时间跳变会导致任务重复执行(时间被回拨)或整段任务被跳过(时间被快进)。凌晨跑批、对账、备份任务尤其敏感。
  • Kerberos 认证:Kerberos 票据带有时间戳,默认允许的时钟偏差(clock skew)只有 5 分钟,超出即认证失败,报 Clock skew too great。Windows 域、Hadoop 安全模式、NFSv4 都依赖它。
  • TLS/SSL 证书校验:证书有 Not Before / Not After 有效期。系统时间落在有效期之外会直接报证书过期或"证书尚未生效",yum、git clone https://、浏览器访问全部失败。
  • 数据库主从与分布式一致性:MySQL 主从复制的 Seconds_Behind_Master、基于时间戳的增量同步、分布式事务与 MVCC 快照、Cassandra 的 last-write-wins 冲突解决,都直接依赖时间。时间回拨可能造成新写入被判定为"旧数据"而被丢弃。
  • 集群心跳与选主:Keepalived、ZooKeeper、etcd、Elasticsearch 的会话超时与租约(lease)判定依赖时间差。时间跳变会引发误判节点下线、脑裂、频繁重新选主。
  • 财务与审计合规:交易流水、审计日志的时间戳具有法律效力。等级保护、PCI-DSS、SOX 等合规要求都明确规定服务器必须与可信时间源同步,并保留同步记录。
  • HTTP 与缓存:Date、Expires、Last-Modified、If-Modified-Since 头,以及 AWS S3 / 阿里云 OSS 的签名请求(签名带时间戳,默认容忍 15 分钟)都会因时间偏差而失败。

1.2 时间相关概念

缩写 全称 说明
UTC Coordinated Universal Time 世界统一时间 / 世界标准时间 / 国际协调时间
GMT Greenwich Mean Time 格林尼治标准时间;UTC 基于 GMT 提供更准确的时间,简单意义上二者等同
RTC Real-Time Clock 实时时钟,由 PC 主板上的晶振及相关电路生成脉冲
Localtime 本地时钟 本地所在时区的当前时间
CST China Standard Time 中国标准时间,UTC+8(注意 CST 是个有歧义的缩写,也可指美国中部时间)
NTP Network Time Protocol 网络时间协议

1.3 硬件时钟 RTC 与系统时钟

Linux 上存在两个相互独立的时钟,理解它们的区别是所有时间问题的基础。

两类时钟:

  • 硬件时钟(Hardware Clock / RTC / BIOS 时钟 / CMOS 时钟):在 BIOS 画面中设定的时钟。由主板上的独立晶振电路驱动,靠主板纽扣电池供电,关机断电后仍然继续走。精度较低(每天可能漂移几秒到几十秒)。
  • 系统时钟(System Clock / 内核时钟 / 软时钟):Linux 内核(kernel)维护的时钟。以 1970-01-01 00:00:00 UTC 为起点的秒数(Unix 时间戳)表示,由内核定时器中断累加维护。只在系统运行期间存在,关机即消失。所有应用程序(date、日志、文件时间戳)读到的都是系统时钟。

两者的关系与工作流程:

  1. 开机:内核启动时读取 RTC,用它来初始化系统时钟(这一步由 hwclock --hctosys 或 systemd 内部完成)。
  2. 运行期间:系统时钟由内核自行维护,与 RTC 完全脱钩,各走各的。此时用 date -s 改系统时间不会影响 RTC,用 hwclock --set 改 RTC 也不会影响系统时间。
  3. 关机:正常关机流程会执行 hwclock --systohc,把系统时钟写回 RTC,保证下次开机时间正确。异常断电 / 强制关机则不会写回,这就是为什么强制断电重启后时间常常"倒退"。

关键结论:

  • 校准时间必须两个时钟都校准。只改系统时钟,重启后会被 RTC 拉回去;只改 RTC,当前系统时间不变。
  • ntpd / chronyd 默认只维护系统时钟,需要通过 rtcsync(chrony)或 SYNC_HWCLOCK=yes(ntpd)等机制才会周期性写回 RTC。

查看 RTC 相关的内核接口文件:

cat /proc/driver/rtc                    # RTC 驱动的完整状态信息
cat /sys/class/rtc/rtc0/date            # RTC 日期
cat /sys/class/rtc/rtc0/time            # RTC 时间
cat /sys/class/rtc/rtc0/since_epoch     # RTC 自 Epoch 起的秒数
cat /sys/class/rtc/rtc0/hctosys         # 1 表示该 RTC 用于开机初始化系统时钟

/proc/driver/rtc 输出示例:

rtc_time        : 02:48:46
rtc_date        : 2016-01-19
alrm_time       : 00:00:00
alrm_date       : ****-**-**
alarm_IRQ       : no
alrm_pending    : no
update IRQ enabled     : no
periodic IRQ enabled   : no
periodic IRQ frequency : 1024
max user IRQ frequency : 64
24hr            : yes
periodic_IRQ    : no

1.4 UTC、本地时间与时区

系统时钟内部只保存 UTC 时间戳,不保存时区信息。"本地时间"是显示时由时区规则换算出来的:

本地时间 = UTC 时间戳 + 时区偏移量(含夏令时规则)

时区相关的关键路径:

路径 说明
/etc/localtime 系统当前时区文件。CentOS 7 中是指向 /usr/share/zoneinfo/<区域>/<城市> 的软链接;CentOS 6 中通常是从 zoneinfo 复制过来的普通文件
/usr/share/zoneinfo/ 系统自带的全部时区数据库(tzdata 包提供),按洲/城市组织,如 Asia/Shanghai、America/New_York、UTC
/usr/share/zoneinfo/Asia/Shanghai 中国大陆时区文件(CST,UTC+8)
/etc/sysconfig/clock CentOS 6 的时区/RTC 配置文件,记录 ZONE 与 UTC 两个变量。CentOS 7 中已废弃,仅少数程序仍读取
/etc/timezone Debian/Ubuntu 系用于记录时区名的文本文件(CentOS 无此文件)
/etc/adjtime 记录 RTC 漂移率、上次校准时间以及 RTC 采用 UTC 还是 LOCAL 时间

TZ 环境变量可以在不改动系统时区的前提下,临时改变当前 shell 或单个命令的时区,优先级高于 /etc/localtime:

TZ='America/New_York' date            # 只对这一条命令生效,查看纽约时间
TZ='UTC' date                         # 查看 UTC 时间
TZ='Asia/Tokyo' date +'%F %T %Z'      # 查看东京时间并显示时区名

export TZ='Asia/Shanghai'             # 对当前 shell 及其子进程生效
unset TZ                              # 取消,恢复使用 /etc/localtime

# 写入 ~/.bash_profile 可对某个用户永久生效(不影响其他用户和系统服务)
echo "export TZ='Asia/Shanghai'" >> ~/.bash_profile

/etc/adjtime 内容示例(第三行是关键):

0.000000 1461393600 0.000000
1461393600
UTC
  • 第 1 行:漂移率(秒/天)、上次校准的 Unix 时间戳、校准余量。
  • 第 2 行:上次执行漂移调整的时间戳。
  • 第 3 行:UTC 表示 RTC 中保存的是 UTC 时间;LOCAL 表示 RTC 中保存的是本地时间。

UTC 还是 LOCAL? Linux 惯例是 RTC 存 UTC,Windows 惯例是 RTC 存 LOCAL。因此 Linux 与 Windows 双系统共存时,两个系统会互相把对方的 RTC 值"改错",表现为切换系统后时间差 8 小时。解决办法:把 Linux 也改成 LOCAL(timedatectl set-local-rtc 1),或改注册表让 Windows 使用 UTC。

1.5 时区设置

CentOS 6 的时区设置方式(旧写法,仍然有效)

CentOS 6 没有 timedatectl,通过直接操作 /etc/localtime 文件完成。

方式一:复制时区文件(/etc/localtime 成为普通文件):

cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 或者保留备份
cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

方式二:建立软链接(推荐,与 CentOS 7 行为一致):

rm -f /etc/localtime
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ls -l /etc/localtime

同时修改 /etc/sysconfig/clock(该文件主要影响 hwclock 的行为以及部分启动脚本):

cat /etc/sysconfig/clock                # 查看系统当前时区(仅对 hwclock 有效)
vim /etc/sysconfig/clock
ZONE="Asia/Shanghai"
UTC=true
ARC=false
  • ZONE:时区名,对应 /usr/share/zoneinfo/ 下的相对路径。
  • UTC=true:表示 RTC 中保存的是 UTC 时间(Linux 推荐);false 表示保存本地时间。
  • ARC=false:仅 Alpha 架构相关,保持 false。

交互式选择时区(CentOS 6/7 通用,由 glibc 提供):

tzselect                                # 交互式查看全部时区并修改时区

tzselect 只会在最后打印出建议的 TZ 值,并不会真正修改系统时区,仍需自己写入 /etc/localtime 或 ~/.bash_profile。

图形化时间配置工具(不常用):

system-config-date

改完时区后,让已运行的服务生效需要重启相关服务(服务在启动时已缓存时区):

/etc/init.d/crond restart               # CentOS 6
/etc/init.d/rsyslog restart

CentOS 7 的时区设置方式(timedatectl,推荐)

CentOS 7 及以上由 systemd 提供 timedatectl,统一管理时间、时区与 NTP 状态。

timedatectl                             # 等同于 timedatectl status,查看全部状态
timedatectl status

timedatectl list-timezones              # 列出所有可用时区
timedatectl list-timezones | grep -i shanghai
timedatectl list-timezones | grep ^Asia # 只看亚洲时区

timedatectl set-timezone Asia/Shanghai  # 设置时区为上海(会自动重建 /etc/localtime 软链接)
timedatectl set-timezone UTC            # 设置为 UTC

timedatectl set-time "2026-08-22 10:30:00"   # 设置日期和时间
timedatectl set-time "10:30:00"              # 只设置时间
timedatectl set-time "2026-08-22"            # 只设置日期

timedatectl set-ntp true                # 启用 NTP 自动同步(启动 chronyd/ntpd)
timedatectl set-ntp false               # 关闭 NTP 自动同步

timedatectl set-local-rtc 0             # RTC 使用 UTC(Linux 推荐,默认)
timedatectl set-local-rtc 1             # RTC 使用本地时间(与 Windows 双系统时使用)

重要:启用了 NTP 同步(set-ntp true)时,timedatectl set-time 会被拒绝,报 Failed to set time: Automatic time synchronization is enabled。必须先 timedatectl set-ntp false 再手工设置。

timedatectl status 输出示例与各字段含义:

      Local time: Sat 2026-08-22 10:48:46 CST
  Universal time: Sat 2026-08-22 02:48:46 UTC
        RTC time: Sat 2026-08-22 02:48:45
       Time zone: Asia/Shanghai (CST, +0800)
     NTP enabled: yes
NTP synchronized: yes
 RTC in local TZ: no
      DST active: n/a
字段 含义
Local time 按当前时区换算出的本地时间,末尾是时区缩写(CST)
Universal time 对应的 UTC 时间,与 Local time 相差时区偏移(此处 8 小时)
RTC time 硬件时钟当前值。若 RTC in local TZ: no,此值应与 Universal time 接近
Time zone 当前时区名、缩写和 UTC 偏移量
NTP enabled 是否启用了自动时间同步服务(chronyd / ntpd 是否被 systemd 管理为时间同步单元)
NTP synchronized 内核是否报告时钟已同步(adjtimex 的 STA_UNSYNC 标志位)。yes 才代表真正同步成功
RTC in local TZ RTC 中保存的是否为本地时间。no = UTC(推荐),yes = 本地时间
DST active 夏令时是否生效。中国大陆无夏令时,显示 n/a

CentOS 7.x 新版本中字段名略有变化,可能显示为 NTP service: active 与 System clock synchronized: yes,含义相同。

1.6 时间查看与手工设置

date:查看与修改系统时间

显示系统时间:

date                            # 默认格式,如 Sat Aug 22 10:48:46 CST 2026
date +'%F %T'                   # 年-月-日 时:分:秒
date +'%Y-%m-%d %H:%M:%S'       # 等效写法

输出示例:

2016-01-19 10:48:46

date 常用格式化占位符(以 2026-08-22 星期六 09:05:03 为例):

占位符 含义 示例输出
%Y 4 位年份 2026
%y 2 位年份 26
%m 月份(01-12) 08
%d 日(01-31) 22
%e 日(空格补齐,1-31) 22
%j 一年中的第几天(001-366) 234
%H 小时(00-23) 09
%I 小时(01-12,12 小时制) 09
%M 分钟(00-59) 05
%S 秒(00-60) 03
%N 纳秒(000000000-999999999) 123456789
%p AM / PM AM
%P am / pm(小写) am
%F 完整日期,等价于 %Y-%m-%d 2026-08-22
%T 完整时间,等价于 %H:%M:%S 09:05:03
%D 美式日期 %m/%d/%y 08/22/26
%R 时:分,等价于 %H:%M 09:05
%c 本地完整日期时间表示 Sat 22 Aug 2026 09:05:03 AM CST
%x 本地日期表示 08/22/2026
%X 本地时间表示 09:05:03 AM
%s Unix 时间戳(自 1970-01-01 UTC 起的秒数) 1787443503
%a 星期缩写 Sat
%A 星期全称 Saturday
%b / %h 月份缩写 Aug
%B 月份全称 August
%u 星期几(1=周一 … 7=周日) 6
%w 星期几(0=周日 … 6=周六) 6
%U 一年中的第几周(周日为一周起始,00-53) 33
%W 一年中的第几周(周一为一周起始,00-53) 33
%V ISO 8601 周number(01-53) 34
%Z 时区缩写 CST
%z 时区数字偏移 +0800
%:z 带冒号的时区偏移 +08:00
%n 换行符
%t 制表符
%% 一个百分号 %

常用组合示例(脚本里最常见的写法):

date +%F                                # 2026-08-22,用于日志/备份文件按天命名
date +%Y%m%d                            # 20260822
date +%Y%m%d_%H%M%S                     # 20260822_090503,备份文件常用
date +%s                                # 1787443503,时间戳,用于计算耗时
date +'%Y-%m-%d %H:%M:%S'               # 标准日志时间格式
date +"%Y年%m月%d日 %H时%M分%S秒"       # 中文格式
date -u +'%F %T'                        # 强制显示 UTC 时间
date +'%F %T.%3N'                       # 带毫秒
date -R                                 # RFC 5322 格式,邮件头常用
date -Is                                # ISO 8601 格式,2026-08-22T09:05:03+08:00

date -d / --date:解析与相对时间计算

-d(等价于 --date)接受一个日期时间描述字符串,date 解析后按指定格式输出,不改变系统时间。这是 shell 脚本里做日期计算最常用的手段。

date -d "2026-08-22" +%F                # 解析指定日期
date -d "2026/08/22 10:30" +'%F %T'     # 斜杠格式也可解析
date -d @1787443503 +'%F %T'            # 把 Unix 时间戳转成可读时间(@ 前缀)
date -d "now" +%s                       # 当前时间戳

相对时间表达式(关键字可组合、可用 +/-):

date -d "+1 day" +%F                    # 明天
date -d "-1 day" +%F                    # 昨天
date -d "1 day ago" +%F                 # 昨天(另一种写法)
date -d "yesterday" +%F                 # 昨天
date -d "tomorrow" +%F                  # 明天
date -d "today" +%F                     # 今天
date -d "now" +'%F %T'                  # 现在

date -d "2 hours ago" +'%F %T'          # 2 小时前
date -d "+30 minutes" +'%F %T'          # 30 分钟后
date -d "-10 seconds" +'%F %T'          # 10 秒前
date -d "+1 week" +%F                   # 一周后
date -d "-2 weeks" +%F                  # 两周前
date -d "+1 month" +%F                  # 一个月后
date -d "-1 month" +%F                  # 一个月前(上月同一天)
date -d "+1 year" +%F                   # 一年后
date -d "next monday" +%F               # 下个周一
date -d "last friday" +%F               # 上个周五
date -d "next month" +%F                # 下个月

# 组合表达式
date -d "-1 day -2 hours" +'%F %T'      # 1 天 2 小时前
date -d "2026-08-22 +10 days" +%F       # 指定日期往后 10 天
date -d "1 month ago" +%Y%m             # 上个月的年月,常用于按月归档

# 月初、月末的经典技巧
date -d "$(date +%Y-%m-01) -1 day" +%F  # 上月最后一天
date -d "$(date +%Y-%m-01) +1 month -1 day" +%F   # 本月最后一天
date -d "$(date +%Y-%m-01)" +%F         # 本月第一天

实用场景:

# 删除 7 天前的日志文件(按文件名中的日期)
rm -f /var/log/app-$(date -d "7 days ago" +%F).log

# 计算两个时间点之间相差的秒数
start=$(date -d "2026-08-01 00:00:00" +%s)
end=$(date -d "2026-08-22 00:00:00" +%s)
echo $(( (end - start) / 86400 ))       # 相差天数:21

# 脚本计时
t1=$(date +%s); sleep 3; t2=$(date +%s); echo "耗时 $((t2-t1)) 秒"

date -s:修改系统时间

date -s <时间字符串>
date --set=<时间字符串>                 # 长选项等价写法

示例:

date -s 2015-09-02                      # 只修改日期,不改时间
date -s 11:08:00                        # 只修改时间,不改日期
date -s "2015-09-18 04:53:00"           # 同时修改日期和时间
date -s "2026-08-22 10:30:00"
date -s "$(date -d '+1 hour' +'%F %T')" # 把系统时间往后拨 1 小时

date 其他常用选项:

选项 说明
-d, --date=STRING 解析 STRING 描述的时间并输出(不改系统时间)
-s, --set=STRING 将系统时间设置为 STRING
-u, --utc, --universal 以 UTC 显示或设置时间
-R, --rfc-2822 以 RFC 2822/5322 格式输出
-I[FMT] 以 ISO 8601 格式输出,FMT 可为 date/hours/minutes/seconds/ns
-r, --reference=FILE 显示 FILE 的最后修改时间
-f, --file=DATEFILE 从文件中按行批量解析时间
date -r /etc/passwd +'%F %T'            # 查看文件最后修改时间
date -u                                 # 查看 UTC 时间

生产环境警告:date -s 是跳变式(step)修改时间,会瞬间改变时钟。若时间被往后拨(回拨),正在运行的数据库、Java 应用、集群心跳可能出现严重异常。生产环境应使用 ntpd / chronyd 做渐进调整(slew),详见第二章 2.1 节。

hwclock:硬件时间管理

hwclock 用于读写硬件(BIOS/RTC)时间(hw = hardware)。必须 root 权限。

hwclock                                 # 不带参数等同于 -r,读取硬件时间
hwclock -r                              # 读取硬件时间(BIOS 时间),--show
hwclock --show                           # 同上
hwclock -w                              # 将系统时间写入硬件时间
hwclock --systohc                       # 同 -w,以系统时钟为准同步硬件时钟(sys to hc)
hwclock -s                              # 以硬件时钟为准同步系统时钟
hwclock --hctosys                       # 同 -s,以硬件时钟为准同步系统时钟(hc=硬件,sys=系统)
hwclock --set --date="07/07/16 10:19"   # 直接设定硬件时间(月/日/年 时:分:秒)
hwclock --set --date="2026-08-22 10:30:00"

记忆口诀:

  • --systohc(= -w):sys to hc,系统 → 硬件。校准完系统时间后必须执行这一步,否则重启失效。
  • --hctosys(= -s):hc to sys,硬件 → 系统。开机时自动执行;当系统时间乱了而 RTC 正确时手工用。

hwclock 完整常用选项:

选项 说明
-r, --show 显示 RTC 时间(按本地时区换算后显示)
-w, --systohc 系统时钟 → 硬件时钟
-s, --hctosys 硬件时钟 → 系统时钟
--set --date="..." 直接设置 RTC 为指定时间
--localtime 声明 RTC 中保存的是本地时间,据此换算
--utc, -u 声明 RTC 中保存的是 UTC 时间,据此换算(Linux 默认/推荐)
--adjust 根据 /etc/adjtime 中记录的漂移率对 RTC 做补偿调整
--systz 仅根据当前时区调整内核时区设置,不动 RTC
--noadjfile 不读写 /etc/adjtime,此时必须显式给出 --utc 或 --localtime
--directisa 直接访问 ISA 总线上的 RTC 硬件(旧机器兼容用)
-f, --rtc=FILE 指定 RTC 设备文件,如 /dev/rtc1
-D, --debug 输出详细调试信息,排查 RTC 读写问题时使用

--localtime 与 --utc 的重要影响:

hwclock -r --utc                        # 把 RTC 值当 UTC 解释再换算为本地时间显示
hwclock -r --localtime                  # 把 RTC 值当本地时间直接显示

若解释方式与实际存储方式不一致,显示结果会相差整个时区偏移(中国为 8 小时)。这是"时间差 8 小时"类故障最常见的原因之一。

hwclock --systohc --utc                 # 把系统时间以 UTC 形式写入 RTC(推荐)
hwclock --systohc --localtime           # 把系统时间以本地时间形式写入 RTC(Windows 双系统)

CentOS 6 中,ntpd 是否周期性把系统时间写回 RTC 由此文件控制:

vim /etc/sysconfig/ntpdate
# Options for ntpdate
OPTIONS="-U ntp -s -b"
# Number of retries
RETRIES=2
# Set to 'yes' to sync hw clock after successful ntpdate
SYNC_HWCLOCK=yes

cal:查看日历

cal                                     # 显示本月日历
cal 2026                                # 显示 2026 全年日历
cal 8 2026                              # 显示 2026 年 8 月日历
cal -3                                  # 显示上月、本月、下月三个月
cal -1                                  # 只显示当月(默认)
cal -y                                  # 显示当年全部日历
cal -m                                  # 以周一作为一周的第一天(默认周日)
cal -s                                  # 以周日作为一周的第一天
cal -j                                  # 显示儒略日(一年中的第几天)
cal -3 -m 8 2026                        # 组合使用

输出示例:

    August 2026
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

1.7 时间不同步引发的典型故障案例

现象 根本原因 排查/解决
yum 报 Peer's Certificate has expired 或 certificate is not yet valid 系统时间偏离证书有效期 date 检查时间,用 ntpdate 校准
git clone https://... 报 SSL 证书错误 同上 校准时间
Kerberos / Windows 域认证失败:Clock skew too great 与 KDC 时间偏差超过 5 分钟 客户端与 KDC 同步同一个 NTP 源
ELK 中查不到刚产生的日志 机器时间超前,日志 @timestamp 落在未来;或落后,落在查询窗口之外 全集群统一 NTP,Kibana 时间范围放宽验证
MySQL 主从 Seconds_Behind_Master 显示为负数或巨大值 主从服务器时间不一致 主从统一 NTP 源
凌晨备份脚本一晚跑了两次 时间被回拨,crond 重新触发 禁止 date -s 大幅回拨,改用 chronyd slew
凌晨对账任务整晚没跑 时间被快进跳过了触发点 同上;用 anacron 补跑
Keepalived 频繁主备切换、ZooKeeper 频繁重新选主 时钟跳变导致会话超时误判 集群统一 NTP,禁用宿主机时间同步与 NTP 同时生效
上传到 OSS/S3 报 RequestTimeTooSkewed 签名时间戳与服务端偏差超过 15 分钟 校准客户端时间
find -mtime / tar 增量备份漏文件 文件 mtime 因时间跳变而错乱 校准时间后重建全量基线
日志文件里时间戳突然"倒流" 中途执行了 date -s 或发生 step 调整 检查 /var/log/messages 中 ntpd 的 time reset 记录
双系统切换后时间差 8 小时 RTC 的 UTC/LOCAL 解释不一致 timedatectl set-local-rtc 或改 Windows 注册表
虚拟机休眠/挂起恢复后时间大幅落后 挂起期间系统时钟停止 开启 chronyd 的 makestep,或 VMware Tools 时间同步

在 /var/log/messages 中可以看到时间跳变的痕迹:

grep -Ei 'ntpd|chronyd|time reset|time step|Clock' /var/log/messages | tail -30
Aug 22 10:15:02 web01 ntpd[2314]: 0.0.0.0 061c 0c clock_step +146586394.753774 s
Aug 22 10:15:02 web01 ntpd[2314]: 0.0.0.0 0615 05 clock_sync
Aug 22 10:15:02 web01 chronyd[1122]: System clock wrong by 146586394.753774 seconds, adjustment started

二、NTP 协议与 ntpd 服务

2.1 NTP 协议原理

NTP(Network Time Protocol,网络时间协议)默认使用 UDP 123 端口,用于在分组交换、可变延迟的网络中同步计算机时钟,理论精度在局域网内可达亚毫秒级,公网上一般为几十毫秒。

分层结构(stratum)

NTP 采用分层(stratum)树状结构组织时间源,层级号 0 到 15:

层级 说明
stratum 0 参考时钟本身。原子钟(铯钟、铷钟)、GPS/北斗接收机、长波授时接收机。不直接接入网络,通过串口/PPS 信号连接到 stratum 1 服务器
stratum 1 主时间服务器,直连 stratum 0 设备。精度最高,如国家授时中心的对外服务器
stratum 2 从一台或多台 stratum 1 同步。大多数公共 NTP 服务器(pool.ntp.org 成员)属于此层
stratum 3 从 stratum 2 同步。企业内网 NTP 服务器通常位于这一层
stratum 4 ~ 15 依次向下。层级越高精度越低
stratum 16 表示未同步 / 不可用。ntpq -p 中看到 st = 16 说明该源本身没同步上,不能用

每向下一层,误差累积一次。生产环境一般建议不超过 stratum 4。

设计原则:

  • 客户端应配置 至少 3 ~ 4 个上游时间源。NTP 使用交叉验证算法(intersection algorithm + clustering algorithm)剔除"假票(falseticker)"。只配 1 个源无法判断其是否说谎;配 2 个源出现分歧时无法裁决;3 个以上才能通过多数派选出真值。
  • 不要把某台机器同时既作为上游又作为下游,避免形成时间环路。

时钟源类型

类型 说明 典型精度
原子钟(铯/铷) 物理基准,国家授时中心使用 1e-13 ~ 1e-15
GPS / 北斗接收机 从卫星获取 UTC,配合 PPS 秒脉冲 微秒级
长波/短波授时(BPM、BPL) 无线电授时信号 毫秒级
本地时钟(Local Clock) 服务器自身晶振,server 127.127.1.0 依赖晶振稳定度,日漂移数秒
上游 NTP 服务器 通过网络同步 局域网亚毫秒,公网几十毫秒

时间偏移、延迟与抖动的计算

NTP 通过一次往返交换 4 个时间戳来计算:

T1 = 客户端发出请求的本地时间
T2 = 服务端收到请求的本地时间
T3 = 服务端发出响应的本地时间
T4 = 客户端收到响应的本地时间
  • 往返延迟(delay / round-trip delay):

    delay = (T4 - T1) - (T3 - T2)

    即总耗时减去服务端处理耗时,得到网络双向传输时间。

  • 时间偏移(offset):本地时钟相对于服务端时钟的差值。

    offset = ((T2 - T1) + (T3 - T4)) / 2

    这里假设去程与回程延迟对称。offset 为正表示本地时钟慢于服务端,需要往前拨;为负表示快于服务端。

  • 抖动(jitter / dispersion):多次测量得到的 offset 的离散程度(标准差或相邻差值的均方根)。jitter 越小说明网络越稳定、测量越可信。jitter 很大时 NTP 会降低该源的权重甚至剔除。

ntpq -p 输出中的 delay / offset / jitter 三列就是上面三个量,单位均为毫秒(ms)。

渐进调整(slew)与跳变(step)

这是理解"为什么生产不能直接 date -s"的核心。

方式 机制 特点
slew(渐进调整) 通过 adjtime() 系统调用微调内核时钟的走时快慢(tick 频率),让时钟以略快或略慢的速度运行,慢慢把偏差抹平。ntpd 的最大调整速率约 500 ppm,即每秒最多修正 0.5 毫秒 时间单调递增,绝不回退。应用程序无感知。修正 1 秒的偏差约需 2000 秒(约 33 分钟)
step(跳变) 直接调用 settimeofday() 一次性把时钟设为目标值 瞬间完成,但时间可能向前跳或向后跳。可能引发定时任务重复/漏跑、数据库异常、集群误判

ntpd 的默认策略:

  • 偏差 < 128 ms:始终用 slew 渐进调整。
  • 偏差在 128 ms ~ 1000 s 之间:执行一次 step 跳变,之后转为 slew。
  • 偏差 > 1000 s(约 17 分钟):出于安全考虑直接拒绝同步并退出,在日志中记录 time correction of xxx seconds exceeds sanity limit (1000); set clock manually。此时必须先用 ntpdate 或 date -s 手工把时间拉到 1000 秒误差以内,或用 ntpd -g 允许首次大幅跳变。

对应的命令行选项与配置:

ntpd -g                                 # 允许首次启动时无视 1000s 限制做一次大跳变(panic 阈值)
ntpd -q                                 # 类似 ntpdate,同步一次后立即退出
ntpd -x                                 # 强制只使用 slew,永不 step(偏差 <600s 时)
tinker panic 0                           # 在 /etc/ntp.conf 中取消 1000s 的 panic 限制

为什么生产环境不能直接 date -s:

  1. date -s 是纯 step,时间可能向后回拨。数据库(MySQL、Redis)、Java 应用(System.currentTimeMillis())、消息队列的顺序判定、TTL 过期计算全部依赖时间单调递增,回拨会导致数据错乱、锁超时判断失效、缓存永不过期。
  2. crond 在时间回拨后会重新执行已跑过的任务;时间快进则会跳过任务。
  3. 集群心跳/租约(Keepalived、ZooKeeper、etcd)以时间差判断节点存活,跳变会引发误判下线、脑裂、频繁选主。
  4. date -s 只改系统时钟,不写回 RTC,重启后偏差又回来了。
  5. 正确做法:让 ntpd / chronyd 长期在线做 slew 微调,把偏差始终控制在毫秒级,永远不需要大幅跳变。仅在首次上线、偏差极大时才允许一次 step(ntpdate 或 chronyc makestep),且应在业务未启动前完成。

2.2 公共时间源

RHEL 默认的外网时间服务器:

0.rhel.pool.ntp.org
1.rhel.pool.ntp.org
2.rhel.pool.ntp.org
服务器 说明
pool.ntp.org NTP Pool Project 全球轮询池,DNS 轮询返回附近的服务器
0.pool.ntp.org ~ 3.pool.ntp.org 池中的 4 个独立轮询入口,建议同时配置多个
cn.pool.ntp.org 中国区轮询池,国内机器优先使用
0.cn.pool.ntp.org ~ 3.cn.pool.ntp.org 中国区的 4 个入口
ntp.aliyun.com 阿里云公共 NTP(阿里云 ECS 内网可用 ntp.cloud.aliyuncs.com)
ntp1.aliyun.com ~ ntp7.aliyun.com 阿里云多个独立节点
ntp.ntsc.ac.cn 中国科学院国家授时中心(NTSC),权威国家授时源,stratum 1
ntp.tencent.com 腾讯云公共 NTP
time.windows.com 微软
time.apple.com 苹果
time.nist.gov 美国国家标准与技术研究院
ntp.fudan.edu.cn 复旦大学时间服务器
s1a.time.edu.cn / s2c.time.edu.cn 教育网时间服务器(北京邮电大学等)
202.112.31.197 教育网可用 IP(部分环境)
210.72.145.44 国家授时中心早期公布的 IP(现已不稳定,仅作参考)

常用公网时间服务器示例:

ntpdate 202.112.31.197
ntpdate 0.rhel.pool.ntp.org
ntpdate time.nist.gov
ntpdate ntp.fudan.edu.cn          # 复旦大学时间服务器
ntpdate cn.pool.ntp.org
ntpdate ntp.aliyun.com
ntpdate ntp.ntsc.ac.cn

公网服务器 IP 较多,部分可能无法同步,可多试几个。国内机器优先选阿里云或国家授时中心,延迟低且稳定。

选源建议:

  • 云主机:优先用云厂商提供的内网 NTP(阿里云 ntp.cloud.aliyuncs.com、腾讯云 time.tencentyun.com),走内网不消耗公网带宽且延迟极低。
  • 自建机房:架 1 ~ 2 台内网 NTP 服务器同步公网(或接 GPS/北斗授时设备),其余机器只与内网服务器同步。
  • 纯内网无外网环境:选一台机器以本地时钟为源(server 127.127.1.0 + fudge),其余机器与之同步,保证全网一致(虽然不一定绝对准确)。

2.3 ntpdate:一次性同步公网时间

ntpdate 是一个一次性的时间同步客户端工具:执行一次、同步一次、立即退出,不驻留后台。

安装:

yum install ntpdate -y                  # CentOS 6/7,ntpdate 由 ntpdate 包提供
yum install ntp -y                      # ntpd 服务由 ntp 包提供(ntp 包依赖 ntpdate)
rpm -qa | grep ntp
which ntpdate

基本用法:

ntpdate <公网时间服务器域名或 IP>
ntpdate cn.pool.ntp.org
ntpdate ntp.aliyun.com
ntpdate 10.0.0.2                        # 同步内网 NTP 服务器
ntpdate ntp1.aliyun.com ntp2.aliyun.com # 可指定多个,依次尝试

ntpdate 常用选项:

选项 说明
-u 使用非特权的高位随机源端口发出请求(而非 123 端口)。穿越防火墙/NAT 时必须加,也可避免与本机 ntpd 占用的 123 端口冲突
-d debug 调试模式。打印完整的协议交互过程与计算细节,但只模拟、不真正修改时间
-q query only,只查询并显示 offset,不修改系统时间。用于检测偏差
-b 强制使用 step(跳变)方式设置时间,而不是 slew
-B 强制使用 slew(渐进)方式,即使偏差较大
-s 将输出写入 syslog(/var/log/messages)而不是标准输出。crontab 中常用,避免产生邮件
-v 详细输出
-p <n> 每个服务器采样 n 次(默认 4,取值 1~8)
-t <秒> 每次请求的超时时间(默认 0.5 秒)
-U <用户> 以指定用户身份运行(如 -U ntp)
-o <版本> 指定 NTP 协议版本(1/2/3/4)
-a <keyid> 使用指定 key 进行认证

常用组合:

ntpdate -u cn.pool.ntp.org              # 最常用写法,穿越防火墙
ntpdate -q ntp.aliyun.com               # 只看偏差多少,不改时间
ntpdate -d ntp.aliyun.com               # 详细调试,排查为什么同步不上
ntpdate -u -s cn.pool.ntp.org           # crontab 中用,输出进 syslog
ntpdate -b -u ntp.aliyun.com            # 强制跳变,偏差很大时首次同步用

同步成功的输出:

26 Aug 14:43:29 ntpdate[7117]: step time server 61.129.42.44 offset 146586394.753774 sec

-q 模式输出示例(不改时间):

server 203.107.6.88, stratum 2, offset 0.001234, delay 0.03215
22 Aug 10:48:46 ntpdate[3212]: adjust time server 203.107.6.88 offset 0.001234 sec

输出中的关键词:

  • step time server ... offset X sec:偏差较大(>128ms),执行了跳变。
  • adjust time server ... offset X sec:偏差较小(<128ms),执行了渐进调整。
  • no server suitable for synchronization found:所有服务器都不可用。

与 ntpd 的端口冲突:必须先停 ntpd

ntpdate 默认使用 UDP 123 源端口,而 ntpd 运行时已独占绑定该端口,因此两者不能同时使用。

本机启动 ntpd 后,再在本机执行 ntpdate 同步其他服务器会因 socket 被占用而失败:

26 Aug 14:42:52 ntpdate[7417]: the NTP socket is in use, exiting

三种解决办法:

# 办法 1(推荐):加 -u,使用非特权高位随机端口,不与 ntpd 冲突
ntpdate -u cn.pool.ntp.org

# 办法 2:先停 ntpd,同步完再启动
/etc/init.d/ntpd stop                   # CentOS 6
ntpdate cn.pool.ntp.org
hwclock -w                              # 别忘了写回 RTC
/etc/init.d/ntpd start

systemctl stop ntpd                     # CentOS 7
ntpdate cn.pool.ntp.org
hwclock -w
systemctl start ntpd

# 办法 3:用 ntpd 自己的一次性同步模式(ntpd -q,需先停服务)
systemctl stop ntpd
ntpd -gq                                # 允许大跳变,同步一次后退出
systemctl start ntpd

排查思路:看到 the NTP socket is in use, exiting,先 netstat -ntlup | grep :123 或 ss -unlp | grep 123 确认是谁占用了端口。

配合 crontab 定时同步及其缺点

CentOS 6 时代常见的"穷人版"时间同步方案:不跑 ntpd 服务,只用 crond 定期执行 ntpdate。

crontab -e
# 每 5 分钟同步一次时间(推荐加 -u 与 -s)
*/5 * * * * /usr/sbin/ntpdate -u cn.pool.ntp.org > /dev/null 2>&1

# 每天凌晨 1 点同步一次,并写回硬件时钟
0 1 * * * /usr/sbin/ntpdate -u ntp.aliyun.com >/dev/null 2>&1 && /sbin/hwclock -w

# 更完整的写法:同步 + 写回 RTC + 记录日志
*/10 * * * * /usr/sbin/ntpdate -u ntp.aliyun.com >> /var/log/ntpdate.log 2>&1 ; /sbin/hwclock -w
crontab -l                              # 查看
/etc/init.d/crond restart               # CentOS 6,重启 crond
systemctl restart crond                 # CentOS 7

crontab 中必须使用命令的绝对路径(/usr/sbin/ntpdate),因为 cron 的 PATH 环境变量非常有限。 详见 阶段05(计划任务 crond)

crontab + ntpdate 方案的缺点(生产环境不推荐):

  1. 跳变式修改:ntpdate 偏差超过 128ms 就直接 step 跳变,可能造成时间回拨,引发前述各类故障。而 ntpd/chronyd 是持续 slew 微调。
  2. 同步间隔粗糙:两次同步之间时钟自由漂移,偏差在同步瞬间被一次性抹平,形成"锯齿状"误差曲线。精度只能到秒级,而 ntpd 常态可保持在毫秒级。
  3. 不做交叉验证:只信任命令行里的那一个服务器。若该服务器时间错误(falseticker),会把错误时间照搬到本机,且无从察觉。
  4. 无漂移补偿:ntpd 会把本机晶振的固有漂移率学习并记录到 driftfile,即使断网也能按漂移率补偿;ntpdate 完全没有这个能力。
  5. 网络抖动敏感:单次采样受网络抖动影响大,ntpd 会用多次采样的统计滤波(filter)剔除坏样本。
  6. 失败无感知:cron 中重定向到 /dev/null 后,同步失败也不会有任何告警,时间可能悄悄偏离很久。
  7. ntpdate 已被官方标记为废弃(deprecated),RHEL 8 / CentOS 8 起不再提供,官方推荐用 chronyd 或 chronyc makestep。

合理的使用场景:仅在服务器初始化时(业务启动前)执行一次,把时间拉到正确范围,随后启动 ntpd/chronyd 长期维持。

2.4 ntpd 服务完整配置

安装

yum install ntp -y                      # 安装 ntpd 服务与相关工具
rpm -qa | grep ^ntp
rpm -ql ntp | head -30                  # 查看包内文件列表

ntp 包提供的主要文件:

文件 说明
/usr/sbin/ntpd 主守护进程
/usr/sbin/ntpdate 一次性同步工具(由 ntpdate 子包提供)
/usr/sbin/ntpq 交互式查询工具,最常用
/usr/bin/ntpstat 简要同步状态(由 ntpstat 包提供)
/usr/sbin/ntptime 查看内核时间变量
/usr/sbin/ntpdc 旧版控制/查询工具
/usr/sbin/ntp-keygen 生成认证密钥
/etc/ntp.conf 主配置文件
/etc/ntp/keys 认证密钥文件
/etc/ntp/step-tickers 服务启动前先用 ntpdate 同步的服务器列表
/var/lib/ntp/drift 漂移文件
/etc/sysconfig/ntpd 服务启动参数(CentOS 6/7)

/etc/ntp.conf 逐项详解

编辑 NTP 服务配置文件:

vim /etc/ntp.conf
cp /etc/ntp.conf /etc/ntp.conf.bak      # 修改前务必备份

CentOS 默认配置(带完整注释):

# ---------- 1. 漂移文件 ----------
driftfile /var/lib/ntp/drift

# ---------- 2. 默认访问策略(最严格,拒绝一切) ----------
restrict default nomodify notrap nopeer noquery
restrict -6 default nomodify notrap nopeer noquery

# ---------- 3. 允许本机完全访问(本地管理必需) ----------
restrict 127.0.0.1
restrict -6 ::1

# ---------- 4. 放行内网网段,允许其向本机同步时间 ----------
# restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
restrict 10.0.0.0 mask 255.255.255.0 nomodify notrap

# ---------- 5. 上游时间源 ----------
server 0.centos.pool.ntp.org iburst
server 1.centos.pool.ntp.org iburst
server 2.centos.pool.ntp.org iburst
server 3.centos.pool.ntp.org iburst

# ---------- 6. 本地时钟作为备用源(无外网时启用) ----------
# server 127.127.1.0
# fudge  127.127.1.0 stratum 10

# ---------- 7. 广播/组播(一般不用) ----------
# broadcast 192.168.1.255 autokey
# broadcastclient
# multicastclient 239.255.254.254
# manycastserver 239.255.254.254
# manycastclient 239.255.254.254 autokey

# ---------- 8. 认证与日志 ----------
includefile /etc/ntp/crypto/pw
keys /etc/ntp/keys
# trustedkey 1
# requestkey 1
# controlkey 1
logfile /var/log/ntp.log
statsdir /var/lib/ntp/stats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable

# ---------- 9. 其他 ----------
disable monitor
tinker panic 0

逐项说明:

driftfile /var/lib/ntp/drift

指定漂移文件路径。ntpd 会长期观测本机晶振相对于标准时间的固有偏差率,并把这个"频率补偿值"(单位 PPM,百万分率)写入该文件,默认每小时更新一次。

  • 作用:重启 ntpd 后可直接读取上次学到的漂移率,立刻开始补偿,而不必重新学习几小时。断网期间也能靠漂移率维持较高精度。
  • 文件内容就是一个浮点数,如 -12.345,表示本机时钟每秒慢 12.345 微秒。
  • 该文件所在目录必须对 ntp 用户可写(chown ntp:ntp /var/lib/ntp),否则日志中会出现 can't open /var/lib/ntp/drift: Permission denied。
cat /var/lib/ntp/drift
ls -l /var/lib/ntp/

restrict —— 访问控制

语法:

restrict <地址> [mask <掩码>] [选项...]
restrict default [选项...]          # default 等价于 0.0.0.0 mask 0.0.0.0,即所有地址
restrict -6 default [选项...]       # -6 表示 IPv6

默认策略这一行是安全基线,含义是"对所有来源,默认施加以下全部限制":

restrict default nomodify notrap nopeer noquery

各选项含义:

选项 含义
ignore 完全忽略该来源的所有 NTP 包,连时间查询都不响应。最严格
nomodify 允许查询时间,但禁止通过 ntpq/ntpdc 修改本服务器的配置或状态。防止别人远程篡改你的 NTP 配置。必加
notrap 拒绝 mode 6 的 trap 服务(远程日志陷阱/事件订阅)。trap 是给远程监控程序用的,正常业务不需要,且是历史漏洞点。必加
noquery 拒绝 mode 6/7 的查询请求(即禁止对方用 ntpq -p、ntpdc 探测本机状态),但仍然允许正常的时间同步请求(mode 3/4)。这是最容易误解的一项:加了 noquery 客户端依然能对时,只是查不到你的内部状态。可有效防止 NTP 放大反射 DDoS 攻击
nopeer 拒绝建立对等(peer / symmetric active)关系,即不允许对方把本机当作同级节点来互相同步。防止未授权主机成为你的时间对等体
noserve 拒绝提供时间服务,但允许查询与控制。与 noquery 相反
kod Kiss-Of-Death,对超出速率限制的客户端发送 KoD 报文让其降速。需配合 limited 使用
limited 对超过 discard 速率限制的请求直接丢弃
version 拒绝不符合当前 NTP 版本的报文
noepeer 拒绝早期的对等模式(NTP 4.2.8p7+)
notrust 拒绝未经认证的报文(要求必须带 key)

因此默认策略这一行的实际效果是:任何主机都不能修改配置、不能用 trap、不能查询状态、不能建立对等关系,但可以正常对时(因为没加 ignore 也没加 noserve)。若要彻底禁止外部对时,需写 restrict default ignore。

允许本机完全访问(管理必需,否则本机 ntpq -p 会被自己的 noquery 挡住):

restrict 127.0.0.1
restrict -6 ::1

不带任何选项 = 无限制。

放行内网网段,允许指定网段的客户端向本服务器同步:

restrict 10.0.0.0 mask 255.255.255.0 nomodify notrap

将第 18 行注释打开,并将网段改为本机所在局域网网段。该配置表示允许 10.0.0.0/24 网段的主机向本服务器进行时间同步,但不允许修改服务器时间(nomodify),也不允许 trap(notrap)。

更多写法示例:

restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap        # 放行 192.168.1.0/24
restrict 172.16.0.0 mask 255.240.0.0 nomodify notrap           # 放行 172.16.0.0/12
restrict 10.0.0.0 mask 255.0.0.0 nomodify notrap nopeer        # 放行整个 10 段
restrict 10.0.0.100 mask 255.255.255.255 nomodify              # 只放行单台主机
restrict 1.2.3.4 ignore                                        # 明确屏蔽某个 IP
restrict default kod nomodify notrap nopeer noquery limited    # 加上限速防反射攻击

匹配规则:ntpd 采用最长前缀匹配(最精确匹配优先),与配置文件中的书写顺序无关。即 restrict 10.0.0.100 mask 255.255.255.255 会优先于 restrict 10.0.0.0 mask 255.255.255.0 生效。

server —— 指定上游时间源

语法:

server <地址> [选项...]
选项 含义
iburst 首次启动时连续发送 8 个查询包(间隔 2 秒),而非按默认 64 秒轮询慢慢来。可把首次同步时间从十几分钟缩短到几十秒。强烈建议所有 server 行都加上
burst 每次轮询都发 8 个包。会给上游造成较大压力,公共服务器禁止使用,仅在自己的内网服务器间使用
prefer 标记为首选源。在多个源的质量相近时优先采用该源的时间。通常给延迟最低、最可信的源(如本机房的 stratum 2 服务器)加上。注意:只能给一个源加 prefer,且 prefer 源被判定为 falseticker 时仍会被剔除
minpoll <n> 最小轮询间隔,2 的 n 次方秒。默认 6(64 秒),范围 3~17
maxpoll <n> 最大轮询间隔,2 的 n 次方秒。默认 10(1024 秒,约 17 分钟)
key <n> 使用指定编号的密钥对该源进行认证
noselect 只监测该源的质量,但不用它来调整时钟。用于评估新时间源
true 无条件信任该源,不参与 falseticker 剔除算法
version <n> 指定与该源通信使用的 NTP 版本
dynamic 允许该源在启动时暂时无法解析(动态添加)

示例:

server ntp.aliyun.com iburst
server ntp1.aliyun.com iburst
server cn.pool.ntp.org iburst
server ntp.ntsc.ac.cn iburst prefer     # 国家授时中心作为首选
server 10.0.0.2 iburst minpoll 4 maxpoll 6   # 内网服务器,加快轮询(16~64 秒)

pool 指令(NTP 4.2.6+)可自动从一个 DNS 名解析出多个地址并维持连接:

pool 2.centos.pool.ntp.org iburst maxsources 4

server 127.127.1.0 与 fudge —— 以本地时钟为源(内网无外网场景)

server 127.127.1.0
fudge  127.127.1.0 stratum 10

这是纯内网、完全无法访问外网环境下搭建 NTP 服务器的关键配置。

  • 127.127.1.0 是 NTP 的特殊地址,代表 Local Clock 驱动(本机主板晶振)。地址格式为 127.127.<驱动类型>.<设备号>,127.127.1.x 即 type 1 = Undisciplined Local Clock。其他常见的还有 127.127.20.0(NMEA GPS 接收机)、127.127.22.0(PPS 秒脉冲)。
  • fudge 用于人为修正/伪装某个时钟源的属性,stratum 10 把本地时钟声明为第 10 层。

为什么必须是 stratum 10(或 5~12 之间的较大值)?

  1. 本地晶振精度很差,不该被当成高层可信源。设为 10 使其优先级低于任何真实的外部源(外部源通常是 stratum 2~3)。这样:外网可用时 ntpd 采用外网源;外网断了 ntpd 自动降级为使用本地时钟,客户端仍能同步上(stratum 11),不会因为服务端"未同步"而全网对时失败。
  2. 若不加 fudge,本地时钟默认 stratum 为 0 或 3,会与真实外部源竞争,可能导致 ntpd 采用不准的本地时钟。
  3. 不能设成 16,因为 16 表示"未同步",客户端会拒绝使用。

fudge 的其他常用参数:

参数 说明
stratum <n> 伪装该源的层级号(0~15)
refid <字符串> 伪装参考标识(最多 4 字符),如 refid LOCL、refid GPS
time1 <秒> 对该源的时间偏移做固定补偿(校正固有延迟)
time2 <秒> 第二个校正值,含义由具体驱动定义
flag1 ~ flag4 驱动特定的标志位

纯内网 NTP 服务器的完整最小配置:

driftfile /var/lib/ntp/drift
restrict default nomodify notrap nopeer noquery
restrict 127.0.0.1
restrict ::1
restrict 10.0.0.0 mask 255.255.255.0 nomodify notrap

# 无外网,只用本地时钟
server 127.127.1.0
fudge  127.127.1.0 stratum 10

logfile /var/log/ntp.log

broadcast 与 multicast —— 广播/组播模式

在大规模网络中,让服务器主动广播时间,客户端被动接收,可减少配置工作量与服务器压力。但无法做交叉验证、易被伪造,生产环境很少使用。

# 服务端:向指定广播地址周期性广播时间(必须带认证)
broadcast 10.0.0.255 autokey
broadcast 10.0.0.255 key 1 ttl 4

# 客户端:接收广播的时间
broadcastclient

# 组播服务端(239.255.254.254 是 NTP 的 IANA 保留组播地址)
multicastclient 239.255.254.254
broadcast 239.255.254.254 autokey

# manycast:客户端主动发组播搜索附近的时间服务器(自动发现)
manycastserver 239.255.254.254
manycastclient 239.255.254.254 autokey

相关参数:

参数 说明
ttl <n> 广播/组播包的 TTL,限制传播范围
autokey 使用 Autokey 公钥认证协议
key <n> 使用对称密钥认证
broadcastdelay <秒> 客户端假定的广播传播延迟,默认 0.004 秒

广播模式强烈建议必须启用认证,否则任何人都能在局域网内广播假时间,把全网时钟带偏。

keys 与认证

NTP 支持对称密钥认证,防止时间源被伪造(中间人攻击)。

keys /etc/ntp/keys                       # 密钥文件路径
trustedkey 1 2 3                         # 声明哪些 key 编号是可信的
requestkey 1                             # ntpdc 请求时使用的 key
controlkey 1                             # ntpq 控制操作使用的 key
includefile /etc/ntp/crypto/pw            # Autokey 的密码文件
crypto pw <密码>                          # Autokey 公钥认证的密码

/etc/ntp/keys 文件格式:<编号> <类型> <密钥>

# keyid  type  key
1        M     MySecretNtpKey
2        SHA1  a1b2c3d4e5f60718293a4b5c6d7e8f9012345678
3        MD5   AnotherSecret123

类型说明:M/MD5 = MD5 摘要(ASCII 密钥),SHA1/SHA = SHA1(十六进制密钥),AES128CMAC 等。

生成密钥:

ntp-keygen -M                            # 生成 MD5/SHA 密钥文件
ntp-keygen -T                            # 生成信任证书(Autokey)
chown ntp:ntp /etc/ntp/keys
chmod 600 /etc/ntp/keys                  # 密钥文件必须严格权限

客户端使用认证:

keys /etc/ntp/keys
trustedkey 1
server 10.0.0.2 key 1 iburst             # 与该服务器通信时用 key 1 认证

其他常用配置项

配置项 说明
logfile /var/log/ntp.log 指定 ntpd 自己的日志文件(不写则记入 /var/log/messages)
logconfig =syncall +clockall 控制日志详细程度
statsdir /var/lib/ntp/stats/ 统计数据输出目录
statistics loopstats peerstats clockstats 启用哪些统计
filegen loopstats file loopstats type day enable 统计文件按天切分
disable monitor 关闭 monlist 监控功能,防止 NTP 放大反射 DDoS 攻击(CVE-2013-5211)。生产环境建议开启此项
tinker panic 0 取消 1000 秒的 panic 限制,允许启动时做任意大的跳变(虚拟机常用)
tinker step 0 禁止 step,永远只做 slew
tinker stepout 900 step 后的观察期秒数
interface ignore wildcard / interface listen 10.0.0.2 限制 ntpd 只监听指定网卡/地址
enable ntp / disable ntp 启用/禁用时钟调整(disable 后只查询不改时间)
peer 10.0.0.3 与另一台服务器建立对等关系,互为备份,常用于两台内网 NTP 服务器互备
orphan 7 当所有上游都不可用时,进入孤儿模式,以 stratum 7 对外服务
leapfile /etc/ntp/leap-seconds.list 闰秒文件,提前告知内核闰秒调整

/etc/ntp/step-tickers —— CentOS 6 的启动前同步列表:

cat /etc/ntp/step-tickers
0.centos.pool.ntp.org
ntp.aliyun.com

CentOS 6 的 ntpd init 脚本在启动 ntpd 之前,会先用 ntpdate 同步这个文件中列出的服务器(做一次 step),把时间快速拉到正确范围,避免 ntpd 因偏差超过 1000 秒而拒绝启动。

/etc/sysconfig/ntpd —— 服务启动参数:

vim /etc/sysconfig/ntpd
# Command line options for ntpd
OPTIONS="-u ntp:ntp -p /var/run/ntpd.pid -g"
# Set to 'yes' to sync hw clock after successful ntpdate
SYNC_HWCLOCK=yes
  • -u ntp:ntp:以 ntp 用户运行(降权,安全)。
  • -p:PID 文件路径。
  • -g:允许首次启动时跳过 1000 秒的 panic 限制。虚拟机长期关机后开机必须有这个选项。
  • SYNC_HWCLOCK=yes:同步成功后自动把系统时间写回 RTC。

启动与开机自启

CentOS 6(SysV init + chkconfig):

/etc/init.d/ntpd start                  # 启动
/etc/init.d/ntpd stop                   # 停止
/etc/init.d/ntpd restart                # 重启
/etc/init.d/ntpd status                 # 查看状态
/etc/init.d/ntpd reload                 # 重载配置

# 等价的 service 写法
service ntpd start
service ntpd stop
service ntpd restart
service ntpd status

# 开机自启
chkconfig ntpd on                       # 设置开机自启
chkconfig ntpd off                      # 取消开机自启
chkconfig --list ntpd                   # 查看各运行级别的自启状态
chkconfig --level 35 ntpd on            # 只在运行级别 3 和 5 自启
ntpd            0:off   1:off   2:on    3:on    4:on    5:on    6:off

CentOS 7 及以上(systemd):

systemctl start ntpd                    # 启动
systemctl stop ntpd                     # 停止
systemctl restart ntpd                  # 重启
systemctl reload ntpd                   # 重载
systemctl status ntpd                   # 查看状态
systemctl enable ntpd                   # 开机自启
systemctl disable ntpd                  # 取消开机自启
systemctl is-enabled ntpd               # 查询是否已设置自启
systemctl is-active ntpd                # 查询是否正在运行
systemctl enable --now ntpd             # 一条命令同时启动并设置自启
systemctl list-unit-files | grep ntp
journalctl -u ntpd -f                   # 实时查看 ntpd 日志
journalctl -u ntpd --since "10 min ago"

CentOS 7 注意:CentOS 7 默认安装的是 chronyd,并已 enable。若要使用 ntpd,必须先停用并禁用 chronyd,否则两者争抢 UDP 123 端口,后启动的会失败。详见第三章 3.7 节。 详见 08-01-系统启动与服务管理.md

# CentOS 7 从 chronyd 切换到 ntpd
systemctl stop chronyd
systemctl disable chronyd
yum install ntp -y
systemctl enable --now ntpd

确认 123 端口已监听:

netstat -ntlup | grep :123              # CentOS 6 常用
ss -unlp | grep 123                     # CentOS 7 推荐(NTP 是 UDP,用 -u)
lsof -i:123
ps -ef | grep ntpd

输出示例:

udp        0      0 10.0.0.2:123     0.0.0.0:*      2314/ntpd
udp        0      0 127.0.0.1:123    0.0.0.0:*      2314/ntpd
udp        0      0 0.0.0.0:123      0.0.0.0:*      2314/ntpd
udp        0      0 :::123           :::*           2314/ntpd

NTP 只使用 UDP,不会有 TCP 监听。用 netstat -ntlp(只看 TCP)是查不到的,必须带 -u。

状态检查与判读

ntpq -p —— 最重要的排查命令

ntpq -p                                 # 列出所有时间源及其状态(peers)
ntpq -pn                                # -n 不做反向 DNS 解析,直接显示 IP,更快
ntpq -p -4                              # 只看 IPv4
ntpq -c "peers" -c "associations"       # 执行多条命令
ntpq                                    # 进入交互模式

输出示例:

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*203.107.6.88    100.107.25.114   2 u   45   64  377    5.617    0.383   1.276
+120.25.115.20   10.137.38.86     2 u   38   64  377    6.238   -0.914   2.031
-84.16.73.33     .GPS.            1 u  102  128  376  212.334   12.882   4.517
x91.189.94.4     193.79.237.14    2 u   57   64  377  244.116  -85.221  15.033
 10.0.0.99       .INIT.          16 u    -   64    0    0.000    0.000   0.000
 127.127.1.0     .LOCL.          10 l   12   64  377    0.000    0.000   0.000

每一列的含义:

列 含义
remote 上游时间服务器的地址(或主机名)。行首第一个字符是状态符号,见下表
refid 该服务器自己所同步的上游的标识。可能是 IP 地址,也可能是 4 字符代号:.GPS.(GPS 接收机)、.LOCL./.LCL.(本地时钟)、.PPS.(秒脉冲)、.INIT.(尚未建立联系/初始化中)、.STEP.(正在做 step 调整)、.XFAC.(网络接口刚变化)、.CDMA.、.PTB.、.DCFa. 等
st stratum,该服务器的层级号。16 表示该源自身未同步,不可用。数字越小越接近基准
t 源的类型:u = unicast 单播(最常见)、l = local 本地时钟、b = broadcast 广播、m = multicast 组播、p = pool、s = symmetric peer 对等、- = 未知
when 距离上一次收到该服务器响应已过去的秒数。正常应小于 poll 值。若持续增大或显示 -,说明该源已失联。数值后带 m 表示分钟,h 表示小时,d 表示天
poll 当前的轮询间隔(秒)。ntpd 会自适应调整:刚启动时为 64 秒,稳定后逐步增大到 1024 秒(最大 maxpoll)。poll 变大说明时钟已稳定,是好现象
reach 可达性寄存器,8 进制表示的 8 位移位寄存器。每次轮询把结果左移一位入队:成功记 1,失败记 0。因此:377(八进制)= 二进制 11111111 = 最近 8 次全部成功,完美;0 = 8 次全失败,完全不可达;1 = 二进制 00000001,只有最早那次成功(刚启动时的正常状态);376 = 11111110,最近一次失败;177 = 01111111,8 次前那次失败。这一列是判断链路是否稳定的关键,只要不是 377 就说明有丢包
delay 与该服务器之间的网络往返延迟(毫秒)。局域网应 < 1ms,公网几十毫秒正常。0.000 表示本地时钟
offset 本机时钟与该服务器的时间偏差(毫秒)。这是最关键的指标。正数表示本机慢,负数表示本机快。同步良好时应在 ±10ms 以内(局域网内应 < 1ms)。持续大于 100ms 说明有问题
jitter 抖动(毫秒),多次测量 offset 的离散程度。越小越稳定。持续很大说明网络质量差或该源不可靠

remote 列前缀符号(tally code)的含义 —— 判断是否同步成功的核心:

符号 名称 含义
* sys.peer 当前正在使用的时间源。本机时钟正在以它为基准调整。*必须有且只有一个 ``,否则说明还没同步上**
+ candidate 候选源,通过了所有质量检验,可以作为备选。若 * 源失效会被提升为 *
# selected 质量合格但由于源数量超限(超过 10 个)而未被选中
- outlier 被聚类算法(clustering algorithm)剔除的离群源,其时间与多数派差异较大
x falsetick 被交叉验证算法判定为"假票",即该源的时间明显错误(说谎的服务器)。看到 x 说明这个源的时间不可信,不会被采用
. excess 超出了候选列表长度限制而被丢弃
blank(空格) reject 该源被拒绝。可能原因:不可达(reach=0)、stratum=16(自身未同步)、延迟过大、认证失败、还在初始化中(refid=.INIT.)
o pps.peer 正在使用 PPS 秒脉冲信号同步(精度最高)

判读速查:

第 1 步:有 * 号吗?        没有 → 未同步,继续查
第 2 步:reach 是 377 吗?   不是 → 网络不通/丢包/被防火墙拦
第 3 步:st 是 16 吗?       是   → 上游自己没同步,换源
第 4 步:offset 大吗?       > 100ms → 偏差过大,可能被 panic 限制卡住
第 5 步:都是空格前缀?      → 全部源被拒绝,检查网络与配置
第 6 步:有 x 号?           → 该源在说谎,删掉它

ntpstat —— 简要同步状态

yum install ntpstat -y
ntpstat

已同步的输出:

synchronised to NTP server (203.107.6.88) at stratum 3
   time correct to within 51 ms
   polling server every 1024 s

未同步的输出:

unsynchronised
   time server re-starting
   polling server every 64 s

或:

unsynchronised
   polling server every 64 s

ntpstat 的退出码可直接用于监控脚本:

退出码 含义
0 时钟已同步(synchronised)
1 时钟未同步(unsynchronised)
2 状态不确定,如 ntpd 未运行
# 监控脚本示例
ntpstat > /dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "警告:NTP 未同步" | mail -s "NTP Alert on $(hostname)" ops@example.com
fi

ntptime —— 查看内核时间变量

ntptime
ntp_gettime() returns code 0 (OK)
  time e4c7a4b2.9c3f1000  Sat, Aug 22 2026 10:48:46.610, (.610294),
  maximum error 51000 us, estimated error 1276 us, TAI offset 0
ntp_adjtime() returns code 0 (OK)
  modes 0x0 (),
  offset 383.000 us, frequency -12.345 ppm, interval 1 s,
  maximum error 51000 us, estimated error 1276 us,
  status 0x2001 (PLL,NANO),
  time constant 10, precision 0.001 us, tolerance 500 ppm,

关键字段:

字段 含义
returns code 0 (OK) 内核报告时钟已同步。若返回 5 (ERROR) 表示 STA_UNSYNC 置位,未同步
offset 当前内核记录的时间偏移(微秒)
frequency 当前的频率补偿值(ppm),对应 driftfile 中的值
maximum error 最大误差估计(微秒)
estimated error 估算误差(微秒)
status 0x2001 (PLL,NANO) 内核时钟状态标志。含 UNSYNC 表示未同步;PLL 表示锁相环启用;NANO 表示纳秒精度模式
tolerance 500 ppm 内核允许的最大频率调整速率,即 slew 的上限
time constant PLL 时间常数,影响调整的响应速度

其他查询命令

ntpq -c rv                              # 查看系统变量(system variables)
ntpq -c "rv 0"                          # 同上
ntpq -c as                              # 查看关联状态(associations),可看到每个源的 condition
ntpq -c sysinfo                          # 系统信息汇总
ntpq -c sysstats                         # 报文统计
ntpq -c "lpeers"                         # 列出所有 peer(含隐藏的)
ntpq -c kerninfo                         # 内核信息

ntpdc -c sysinfo                        # 旧版工具,功能类似
ntpdc -c iostats
ntpdc -c loopinfo

ntpq -c as 输出示例:

ind assid status  conf reach auth condition  last_event cnt
===========================================================
  1 34567  963a   yes   yes  none  sys.peer    sys_peer  3
  2 34568  941a   yes   yes  none candidate    sys_peer  1
  3 34569  9024   yes    no  none    reject    reachable 2

condition 列直接给出文字形式的状态:sys.peer(对应 *)、candidate(+)、outlier(-)、falsetick(x)、reject(空格)。

关键点:同步需要几分钟才生效

这是初学者最容易踩的坑。

  • 刚搭建的 NTP 服务器需约 5 分钟(实际上是几个轮询周期)才能对外提供同步服务。这段时间内服务端自己还在与上游对时、还在做统计滤波,stratum 仍显示 16,对外表现为"未同步",因此会拒绝为客户端提供时间。
  • 过早在客户端同步会出现:
1 Sep 12:00:03 ntpdate[3127]: no server suitable for synchronization found
  • 客户端 ntpd 首次同步同样需要几分钟。若配置了 iburst,可缩短到几十秒。
  • 正确的验证节奏:启动服务端 → 等 5~10 分钟 → ntpq -p 确认服务端已有 * 号且 st 小于 16 → 再在客户端测试。
# 服务端启动后循环观察,直到出现 * 号
watch -n 5 'ntpq -pn'

日志中的对应记录:

tail -f /var/log/messages | grep ntpd
tail -f /var/log/ntp.log
journalctl -u ntpd -f                   # CentOS 7
Aug 22 10:15:02 ntp01 ntpd[2314]: ntpd 4.2.6p5@1.2349-o Mon Jan  1 00:00:00 UTC 2026 (1)
Aug 22 10:15:02 ntp01 ntpd[2314]: Listening on interface #0 wildcard, 0.0.0.0:123
Aug 22 10:15:02 ntp01 ntpd[2314]: Listening on interface #2 eth0, 10.0.0.2:123
Aug 22 10:15:03 ntp01 ntpd[2314]: 0.0.0.0 c016 06 restart
Aug 22 10:15:03 ntp01 ntpd[2314]: 0.0.0.0 c012 02 freq_set kernel -12.345 PPM
Aug 22 10:19:31 ntp01 ntpd[2314]: 0.0.0.0 c615 05 clock_sync   ← 这一行才表示同步成功
Aug 22 10:19:31 ntp01 ntpd[2314]: 0.0.0.0 c614 04 freq_mode

2.5 实验:搭建内网 NTP 服务器(ntpd)

环境规划:

角色 IP 说明
NTP 服务端 10.0.0.2 可访问外网,同步公网时间源,对内网提供服务
NTP 客户端 10.0.0.3 仅内网,向 10.0.0.2 同步

第 1 步:服务端安装

yum install ntp ntpdate -y
rpm -qa | grep ntp

第 2 步:服务端先手工把时间拉正

ntpdate -u cn.pool.ntp.org              # 先同步一次,避免偏差超过 1000 秒导致 ntpd 拒绝启动
date
hwclock -w                              # 写回硬件时钟

第 3 步:服务端配置 /etc/ntp.conf

编辑 NTP 服务配置文件:

cp /etc/ntp.conf /etc/ntp.conf.bak
vim /etc/ntp.conf

配置允许同步的客户端网段(将第 18 行注释打开,并将网段改为本机所在局域网网段):

restrict 10.0.0.0 mask 255.255.255.0 nomodify notrap

该配置表示允许 10.0.0.0/24 网段的主机向本服务器进行时间同步,但不允许修改服务器时间。

完整的服务端配置:

driftfile /var/lib/ntp/drift

# 默认策略:禁止修改、禁止 trap、禁止对等、禁止查询
restrict default nomodify notrap nopeer noquery
restrict -6 default nomodify notrap nopeer noquery

# 本机完全放行
restrict 127.0.0.1
restrict -6 ::1

# 放行内网网段,允许其同步时间
restrict 10.0.0.0 mask 255.255.255.0 nomodify notrap

# 上游公网时间源
server ntp.aliyun.com iburst
server ntp1.aliyun.com iburst
server cn.pool.ntp.org iburst
server ntp.ntsc.ac.cn iburst prefer

# 本地时钟作为兜底(外网断了也能继续对内服务)
server 127.127.1.0
fudge  127.127.1.0 stratum 10

keys /etc/ntp/keys
logfile /var/log/ntp.log
disable monitor

也可用 sed 快速改(谨慎):

sed -i 's/^#restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap/restrict 10.0.0.0 mask 255.255.255.0 nomodify notrap/' /etc/ntp.conf
grep -n '^restrict' /etc/ntp.conf

检查配置语法(ntpd 没有专门的语法检查工具,用调试前台运行代替):

ntpd -n -d -c /etc/ntp.conf             # 前台调试运行,Ctrl+C 退出

第 4 步:服务端启动服务

CentOS 6:

/etc/init.d/ntpd restart
chkconfig ntpd on
chkconfig --list ntpd

CentOS 7:

systemctl stop chronyd                  # 先停掉 chronyd 避免端口冲突
systemctl disable chronyd
systemctl restart ntpd
systemctl enable ntpd
systemctl status ntpd

第 5 步:服务端确认 123 端口已监听

netstat -ntlup | grep :123
ss -unlp | grep 123                     # CentOS 7
lsof -i:123
udp   0   0 10.0.0.2:123     0.0.0.0:*   2314/ntpd
udp   0   0 127.0.0.1:123    0.0.0.0:*   2314/ntpd
udp   0   0 0.0.0.0:123      0.0.0.0:*   2314/ntpd

第 6 步:服务端防火墙放行 UDP 123

CentOS 6(iptables):

iptables -I INPUT -p udp --dport 123 -j ACCEPT
iptables -I INPUT -s 10.0.0.0/24 -p udp --dport 123 -j ACCEPT   # 只放行内网,更安全
service iptables save
service iptables restart
iptables -L -n | grep 123

# 测试环境也可直接关闭
service iptables stop
chkconfig iptables off

CentOS 7(firewalld):

firewall-cmd --permanent --add-service=ntp
# 或按端口放行
firewall-cmd --permanent --add-port=123/udp
# 只放行内网网段(推荐)
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="123" protocol="udp" accept'
firewall-cmd --reload
firewall-cmd --list-all

# 测试环境也可直接关闭
systemctl stop firewalld
systemctl disable firewalld

防火墙与 SELinux 的配置详解,详见 07-03(防火墙与 SELinux)

第 7 步:服务端等待并验证自身已同步

# 等待 5~10 分钟
watch -n 5 'ntpq -pn'

直到看到某一行前面出现 *:

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*203.107.6.88    100.107.25.114   2 u   45   64  377    5.617    0.383   1.276
+120.25.115.20   10.137.38.86     2 u   38   64  377    6.238   -0.914   2.031
 127.127.1.0     .LOCL.          10 l   12   64  377    0.000    0.000   0.000
ntpstat                                 # 应显示 synchronised to NTP server ...
ntpq -c rv | grep stratum               # 确认本机 stratum 已从 16 变为 3

第 8 步:客户端验证

方式 A:客户端用 ntpdate 一次性同步(验证服务端可用)

# 在 10.0.0.3 上执行
date                                    # 先看一下当前时间
ntpdate -q 10.0.0.2                     # 只查询偏差,不改时间
ntpdate -u 10.0.0.2                      # 真正同步
date                                    # 确认时间已正确
hwclock -w

成功输出:

22 Aug 10:25:31 ntpdate[3401]: adjust time server 10.0.0.2 offset 0.003215 sec

方式 B:客户端配置 ntpd 长期同步(推荐)

yum install ntp -y
vim /etc/ntp.conf

客户端最小配置:

driftfile /var/lib/ntp/drift
restrict default nomodify notrap nopeer noquery
restrict 127.0.0.1
restrict -6 ::1

# 只指向内网 NTP 服务器
server 10.0.0.2 iburst prefer
# 可再配一台内网备用服务器
# server 10.0.0.4 iburst

logfile /var/log/ntp.log
ntpdate -u 10.0.0.2                     # 先手工同步一次
systemctl restart ntpd                  # CentOS 7;CentOS 6 用 /etc/init.d/ntpd restart
systemctl enable ntpd
sleep 300
ntpq -pn                                # 应看到 *10.0.0.2
ntpstat

客户端验证成功的输出:

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*10.0.0.2        203.107.6.88     3 u   21   64  377    0.312    0.021   0.045

注意 st 为 3(服务端是 2,客户端比它低一层),delay 极小(局域网),offset 亚毫秒级。

第 9 步:客户端加定时任务兜底(可选)

crontab -e
0 4 * * * /usr/sbin/ntpdate -u 10.0.0.2 >/dev/null 2>&1 && /sbin/hwclock -w

常见问题排查

现象 原因与解决
客户端 no server suitable for synchronization found 服务端自身还未同步(等 5~10 分钟);或服务端 stratum=16;或 restrict 未放行客户端网段
客户端 the NTP socket is in use, exiting 客户端自己的 ntpd 正在运行,加 -u 或先停 ntpd
客户端 Server dropped: no data / Network is unreachable 防火墙未放行 UDP 123;或网络不通(ping 10.0.0.2 测试)
ntpq -p 全是空格前缀 所有源都被拒绝,检查网络、stratum、restrict

2.6 企业时间同步方案

让一台能访问外网的服务器同步公网时间服务器,再将其配置为内网 NTP 服务器;内网其他机器通过内网定时(crond + ntpdate)或长驻 ntpd/chronyd 同步该服务器,从而实现全公司服务器时间与北京时间一致。

推荐架构:

公网时间源(阿里云 / 国家授时中心,stratum 1~2)
        │
        ▼
内网 NTP 服务器 ntp01 / ntp02(stratum 3,互为 peer 备份,可访问外网)
        │
        ├──► Web 服务器组(stratum 4)
        ├──► 数据库服务器组
        ├──► 应用服务器组
        └──► 网络设备(交换机、路由器、防火墙)

要点:

  1. 内网至少部署 2 台 NTP 服务器,用 peer 互为备份,避免单点。
  2. 内网服务器配置 server 127.127.1.0 + fudge stratum 10 作为兜底,外网断了全网仍能保持一致。
  3. 所有客户端只指向内网服务器,不要直连公网(省带宽、减少公网依赖、便于统一管控)。
  4. 客户端使用 ntpd/chronyd 长驻服务而非 crontab + ntpdate。
  5. 把 ntpstat / chronyc tracking 的结果接入 Zabbix 监控,对 offset 超阈值告警。

    详见 08-05-Zabbix监控.md

  6. 新机器上线的初始化脚本中,先 ntpdate -u <内网NTP> 一次性拉正,再启动长驻服务。
  7. 网络设备也要配 NTP(Cisco:ntp server 10.0.0.2),否则设备日志时间对不上。

2.7 旁支:使用 xinetd 提供 time 服务(rdate 方式)

除 ntpd 外,还可通过超级守护进程 xinetd 托管 time 服务提供时间同步(使用 TCP/UDP 37 端口,RFC 868 Time Protocol)。这是一种非常古老、精度只到秒级的协议,现代环境已基本淘汰,此处保留作为 xinetd 托管小服务的示例。

安装并启用:

yum install xinetd -y

vim /etc/xinetd.d/time-dgram        # UDP,将 disable = yes 改为 no
vim /etc/xinetd.d/time-stream       # TCP,将 disable = yes 改为 no

两份配置中均修改:

disable = no

/etc/xinetd.d/time-stream 完整内容:

# default: off
# description: An RFC 868 time server. This protocol provides a
# site-independent, machine readable date and time. The Time service sends back
# to the originating source the time in seconds since midnight on January first
# 1900.
service time
{
        type            = INTERNAL
        id              = time-stream
        socket_type     = stream
        protocol        = tcp
        user            = root
        wait            = no
        disable         = no
}

/etc/xinetd.d/time-dgram 完整内容:

service time
{
        type            = INTERNAL
        id              = time-dgram
        socket_type     = dgram
        protocol        = udp
        user            = root
        wait            = yes
        disable         = no
}

注意 type = INTERNAL:time 服务由 xinetd 内部实现,不需要 server 指定外部程序。

重启服务并设置开机自启:

/etc/init.d/xinetd restart              # CentOS 6
chkconfig xinetd on

systemctl restart xinetd                # CentOS 7
systemctl enable xinetd

确认 37 端口监听:

netstat -ntlup | grep :37
ss -tunlp | grep :37                    # CentOS 7

输出:

tcp  0  0 :::37  :::*  LISTEN  5740/xinetd
udp  0  0 :::37  :::*          5740/xinetd

客户端使用 rdate 同步:

yum install rdate -y
rdate 10.0.0.2                          # 只显示服务器时间,不修改
rdate -s 10.0.0.2                       # -s 表示 set,设置本机时间
rdate -p 10.0.0.2                       # -p 只打印时间
rdate -u 10.0.0.2                       # 使用 UDP 而非 TCP
hwclock -w                              # 别忘了写回硬件时钟

不推荐生产使用:RFC 868 协议精度只有秒级,无延迟补偿、无渐进调整、无认证,且 37 端口的 time 服务历史上被用于放大攻击。仅作为教学演示。


三、chrony 与 CentOS 7 时间同步

3.1 chrony 相比 ntpd 的优势

CentOS 7 起,chrony 取代 ntp 成为默认的时间同步实现(RHEL 7 官方文档明确推荐)。CentOS 6 仍以 ntpd 为主(chrony 可从 EPEL 安装)。

对比项 ntpd chronyd
首次同步速度 数分钟到十几分钟 通常几秒到几十秒
间断网络适应性 差,长时间断网后需重新学习 好,会记录漂移率并在恢复后快速收敛
大偏差处理 > 1000s 直接拒绝启动 makestep 可自动跳变修正任意偏差
时钟频率补偿 支持,精度一般 更精确,可补偿本机时钟增益误差
slew 最大速率 500 ppm 可达 100000 ppm(默认 83333 ppm),修正快得多
虚拟机/笔记本 不适应频繁休眠、时钟跳变 专为此设计,休眠恢复后快速纠正
是否需要 ntpdate 需要(先拉正时间再启动) 不需要,chronyd 自身即可处理大偏差
网络抖动下的精度 一般 更好,算法对不对称延迟更鲁棒
资源占用 略高 更低
可作为服务端 是 是(3.x 起功能完整,支持 NTS 加密)
需要网络常在线 是(否则精度下降快) 否
隔离网络/临时上线 不适合 适合

核心原因:ntpd 的设计假设是"服务器 7×24 联网、时钟平稳",chronyd 的设计假设是"网络可能断、机器可能休眠、时钟可能跳",后者更符合虚拟化与云环境的现实。

3.2 安装与服务管理

yum install chrony -y
rpm -qa | grep chrony
rpm -ql chrony

chrony 提供两个主要程序:

程序 说明
chronyd 守护进程,实际执行时间同步
chronyc 命令行客户端,用于查询状态、下发指令(相当于 ntpd 的 ntpq + ntpdc)

关键文件:

路径 说明
/etc/chrony.conf 主配置文件
/etc/chrony.keys 认证密钥文件
/etc/sysconfig/chronyd 服务启动参数
/var/lib/chrony/drift 漂移文件
/var/lib/chrony/rtc RTC 漂移记录
/var/log/chrony/ 日志目录
/usr/lib/systemd/system/chronyd.service systemd 单元文件

服务管理(CentOS 7+):

systemctl start chronyd
systemctl stop chronyd
systemctl restart chronyd
systemctl reload chronyd
systemctl status chronyd
systemctl enable chronyd                # 开机自启(CentOS 7 默认已 enable)
systemctl disable chronyd
systemctl is-active chronyd
systemctl enable --now chronyd
journalctl -u chronyd -f                # 查看日志
journalctl -u chronyd --since today

CentOS 6(从 EPEL 安装 chrony 后):

yum install epel-release -y
yum install chrony -y
/etc/init.d/chronyd start
/etc/init.d/chronyd restart
/etc/init.d/chronyd status
chkconfig chronyd on

确认监听(chronyd 作为纯客户端时不监听 123 端口,只有配置了 allow 才会监听):

ss -unlp | grep chrony
netstat -unlp | grep chrony
ps -ef | grep chronyd
udp  UNCONN  0  0  0.0.0.0:123    0.0.0.0:*  users:(("chronyd",pid=1122,fd=6))
udp  UNCONN  0  0  127.0.0.1:323  0.0.0.0:*  users:(("chronyd",pid=1122,fd=5))
udp  UNCONN  0  0  [::1]:323      [::]:*     users:(("chronyd",pid=1122,fd=4))

端口 323 是 chronyd 的本地控制端口(供 chronyc 使用),默认只绑定在 127.0.0.1 与 ::1。端口 123 是标准 NTP 服务端口,只有配置了 allow 提供服务时才监听。

/etc/sysconfig/chronyd 启动参数:

OPTIONS="-F 2"
选项 说明
-F <n> seccomp 系统调用过滤级别(2 = 最严格,增强安全)
-q 同步一次后立即退出(类似 ntpdate)
-Q 只测量偏差,不修改时钟后退出
-d 前台运行并输出调试信息
-r 启动时从 driftfile 恢复上次的测量数据
-s 启动时从 RTC 设置系统时钟
-u <用户> 以指定用户运行
-f <文件> 指定配置文件路径

一次性同步(替代 ntpdate 的推荐方式):

systemctl stop chronyd
chronyd -q 'server ntp.aliyun.com iburst'      # 同步一次后退出
chronyd -Q 'server ntp.aliyun.com iburst'      # 只测量不修改
systemctl start chronyd

3.3 /etc/chrony.conf 逐项详解

cp /etc/chrony.conf /etc/chrony.conf.bak
vim /etc/chrony.conf

CentOS 7 默认配置:

# 使用 pool 指令自动从 DNS 获取多个时间源
server 0.centos.pool.ntp.org iburst
server 1.centos.pool.ntp.org iburst
server 2.centos.pool.ntp.org iburst
server 3.centos.pool.ntp.org iburst

# 漂移文件:记录系统时钟的增益/损失率
driftfile /var/lib/chrony/drift

# 若偏差大于 1 秒,在前 3 次更新中允许直接跳变
makestep 1.0 3

# 启用内核的 RTC 同步(周期性把系统时间写回硬件时钟)
rtcsync

# 若时钟频率误差大于此值(ppm),启用硬件时间戳
# hwtimestamp *

# 至少有几个时间源可用时才认为可以同步
# minsources 2

# 允许哪些客户端向本机同步(默认全部拒绝)
# allow 192.168.0.0/16
# deny 192.168.10.1

# 即使没有外部同步源,也把本机声明为 stratum 10 的可用源
# local stratum 10

# 认证密钥文件
keyfile /etc/chrony.keys

# 记录闰秒信息的时区数据库
leapsectz right/UTC

# 命令端口访问控制
# bindcmdaddress 10.0.0.2

# 日志目录
logdir /var/log/chrony

# 记录哪些测量数据
# log measurements statistics tracking

逐项详解:

server / pool —— 上游时间源

server ntp.aliyun.com iburst
server ntp1.aliyun.com iburst
pool 2.centos.pool.ntp.org iburst maxsources 4
pool cn.pool.ntp.org iburst maxsources 3
peer 10.0.0.3 iburst                     # 对等关系,互为备份
指令/选项 说明
server <地址> 指定一个时间服务器
pool <地址> 指定一个服务器池。chronyd 会解析该 DNS 名得到的所有 IP,并从中选取若干个使用。适合 *.pool.ntp.org
peer <地址> 建立对等(symmetric)关系,双方互相同步,用于两台内网 NTP 服务器互备
iburst 首次启动时快速发送 4 个(默认)请求包(间隔约 2 秒),使首次同步在几秒内完成,而非等待默认的 64 秒轮询。强烈建议所有源都加
maxsources <n> 仅用于 pool,限制从该池中最多使用 n 个服务器。默认 4
minpoll <n> 最小轮询间隔(2^n 秒),默认 6(64 秒)
maxpoll <n> 最大轮询间隔(2^n 秒),默认 10(1024 秒)
prefer 优先使用该源
noselect 只测量不使用,用于评估新源
trust 无条件信任该源(跳过 falseticker 检测)
require 必须有该源可用才允许调整时钟
key <n> 使用指定密钥认证
nts 使用 NTS(Network Time Security)加密(chrony 4.0+)
offline 启动时标记为离线,需 chronyc online 手工上线
presend <n> 在真正测量前先发 n 个包"预热"(唤醒按需拨号链路/填充 ARP 缓存)
xleave 使用 interleaved 模式提高精度(需服务端支持)
polltarget <n> 每个轮询周期期望的采样数,默认 6

driftfile /var/lib/chrony/drift

记录系统时钟的增益/损失率(gain/loss rate)以及误差估计。chronyd 停止时把当前学到的频率补偿值写入,下次启动立即读取并应用,从而跳过重新学习阶段,几秒内即可达到较高精度。

cat /var/lib/chrony/drift
-12.345678 0.123456

第一个数是频率偏差(ppm),第二个是偏差的不确定度估计。

makestep <阈值> <次数> —— 关键配置

makestep 1.0 3

这是 chrony 最重要也最容易误解的配置项,两个参数含义:

  • 第 1 个参数 1.0(阈值,秒):当测量到的时间偏差绝对值大于该秒数时,允许使用 step(跳变) 方式一次性修正,而不是慢慢 slew。
  • 第 2 个参数 3(次数限制):这种跳变最多只允许发生在 chronyd 启动后的前 3 次时钟更新中。第 4 次及以后,无论偏差多大都只用 slew 渐进调整。

为什么这样设计? 兼顾"启动时快速纠正"与"运行期时间单调"两个矛盾的需求:

  • 机器刚开机时时间可能差很多(虚拟机长期关机、RTC 电池耗尽),必须跳变才能快速可用。
  • 一旦业务开始运行,就绝不能再跳变(尤其不能回拨),因此限制次数。

常见取值:

makestep 1.0 3                           # 默认,推荐生产使用
makestep 0.1 3                           # 更激进,偏差超 100ms 就跳变
makestep 1.0 -1                          # -1 表示不限次数,永远允许跳变。仅用于对时间准确性要求极高、
                                          # 且能容忍跳变的场合(如虚拟机模板、开发测试环境)。生产慎用
makestep 1000 10                          # 只在偏差极大时跳变

手工触发一次立即跳变:

chronyc makestep                         # 立即执行一次 step 修正当前偏差
chronyc makestep 0.1 1                    # 临时设置阈值和次数

rtcsync —— 自动同步硬件时钟

rtcsync

启用后,chronyd 会调用内核的 RTC 同步机制,让内核每 11 分钟自动把系统时间写回 RTC。这样就不需要手工执行 hwclock -w,也不怕异常断电丢失时间。

注意:rtcsync 与 rtcfile 互斥,不能同时启用。

  • rtcsync:交给内核处理(推荐,简单)。
  • rtcfile /var/lib/chrony/rtc + rtcautotrim + rtconutc:由 chronyd 自己跟踪并补偿 RTC 漂移(精度更高,但配置复杂,且在虚拟机中通常无效)。
# 高精度 RTC 管理(与 rtcsync 二选一)
rtcfile /var/lib/chrony/rtc              # 记录 RTC 漂移参数
rtcautotrim 30                            # RTC 误差超过 30 秒时自动校正
rtconutc                                  # 声明 RTC 保存的是 UTC

allow / deny —— 放行客户端(作为服务端时必需)

chronyd 默认不对外提供 NTP 服务(不监听 123 端口),必须显式 allow 才会开启服务端功能。

allow 10.0.0.0/24                        # 放行 10.0.0.0/24 网段
allow 192.168.0.0/16                     # 放行 192.168.0.0/16
allow 10.0.0.100                         # 只放行单台主机
allow all 10.0.0.0/24                    # all 表示包含该网段下的所有子网
allow                                     # 不带参数 = 放行所有地址(不推荐)
allow 2001:db8::/32                       # IPv6

deny 10.0.0.200                          # 明确拒绝某台主机
deny 10.0.0.0/28                          # 拒绝某个小网段
deny all 192.168.10.0/24

匹配规则:最长前缀匹配优先(更精确的规则胜出),与书写顺序无关。因此可以先 allow 大网段再 deny 其中的个别 IP。

也可用 chronyc 在运行时临时调整(重启失效):

chronyc allow 10.0.0.0/24
chronyc deny 10.0.0.200
chronyc allow all
chronyc deny all                         # 立即停止对外提供服务

local stratum 10 —— 内网无外网时的关键配置

local stratum 10

作用等价于 ntpd 的 server 127.127.1.0 + fudge stratum 10:即使 chronyd 自身没有同步到任何外部源,也对外声明自己是 stratum 10 的可用时间源,使内网客户端能够正常同步。

  • 若不加这一行,纯内网环境下服务端因未同步而对外报 stratum 16,客户端会拒绝使用,全网无法对时。
  • 数值选 10 的理由与 ntpd 相同:低于任何真实外部源,外网恢复后自动优先使用外部源。

chrony 3.0+ 的增强选项:

local stratum 10 orphan                  # orphan 模式:多台服务器互为 peer 时,
                                          # 自动选出参考 ID 最小的那台作为最终源,避免时间环路
local stratum 8 distance 0.1              # distance 指定声明的根距离

logdir 与 log —— 日志

logdir /var/log/chrony
log measurements statistics tracking rtc refclocks tempcomp
日志类型 生成的文件 内容
measurements measurements.log 每次与服务器交互的原始测量数据
statistics statistics.log 回归分析的统计结果
tracking tracking.log 时钟调整过程的详细记录
rtc rtc.log RTC 相关操作
refclocks refclocks.log 参考时钟(GPS/PPS)数据
tempcomp tempcomp.log 温度补偿数据
ls -l /var/log/chrony/
tail -f /var/log/chrony/tracking.log

keyfile 与认证

keyfile /etc/chrony.keys

/etc/chrony.keys 格式:

# keyid  hash-type  password
1        SHA256     MyVerySecretPassword
2        MD5        AnotherSecret

生成随机密钥:

echo "1 SHA256 $(head -c 32 /dev/urandom | base64)" >> /etc/chrony.keys
chown root:chrony /etc/chrony.keys
chmod 640 /etc/chrony.keys
chronyc keygen 1 SHA256 256              # chrony 3.x 提供的生成命令

客户端使用认证:

keyfile /etc/chrony.keys
server 10.0.0.2 key 1 iburst

bindcmdaddress —— 命令端口绑定地址

bindcmdaddress 127.0.0.1                 # 默认,只允许本机 chronyc 连接(端口 323)
bindcmdaddress ::1
bindcmdaddress 10.0.0.2                  # 允许通过该地址远程管理(需配合 cmdallow)
bindcmdaddress /var/run/chrony/chronyd.sock   # Unix socket

配合远程管理的访问控制:

cmdallow 10.0.0.0/24                     # 允许该网段远程执行 chronyc 命令
cmddeny 10.0.0.200
cmdport 323                               # 命令端口号,设为 0 可完全禁用

远程管理:

chronyc -h 10.0.0.2 tracking             # 从远程查询
chronyc -h 10.0.0.2 -p 323 sources

安全建议:生产环境保持默认(只绑 127.0.0.1),不要开放命令端口。

bindaddress —— NTP 服务监听地址

bindaddress 10.0.0.2                     # 只在指定地址上提供 NTP 服务
bindacqaddress 10.0.0.2                   # 指定发出 NTP 请求时使用的源地址
port 123                                  # NTP 服务端口,设为 0 表示完全不提供服务(纯客户端)

leapsectz —— 闰秒处理

leapsectz right/UTC

指定一个包含闰秒信息的时区数据库(/usr/share/zoneinfo/right/UTC),chronyd 从中读取即将发生的闰秒,提前告知内核,使闰秒平滑过渡而不产生时间跳变。

相关选项:

leapsecmode slew                          # 闰秒时用 slew 缓慢抹平(推荐,避免 23:59:60 造成应用崩溃)
leapsecmode step                          # 直接跳变(默认,让内核插入闰秒)
leapsecmode slew                          # 强烈推荐生产使用 slew 模式
leapsecmode ignore                        # 忽略闰秒,交给上游 NTP 慢慢修正

历史上多次闰秒(2012、2015、2017)曾导致 Linux 内核死锁、Java 应用 CPU 100%、MySQL 挂起等大规模故障。leapsecmode slew 是规避这类风险的推荐做法。

其他常用配置项

配置项 说明
minsources <n> 至少有 n 个源可用时才允许调整时钟(默认 1)。设为 2 可防止单个坏源带偏时钟
maxupdateskew <ppm> 频率估计的最大允许误差,超过则不更新(默认 1000)
maxdistance <秒> 源的最大根距离,超过则不使用(默认 3.0)
maxjitter <秒> 源的最大抖动阈值
maxchange <秒> <起始次数> <忽略次数> 限制单次调整的最大幅度,超过则忽略(防御恶意时间源)
maxslewrate <ppm> slew 的最大速率,默认 83333.333 ppm(远高于 ntpd 的 500)
corrtimeratio <比率> slew 修正时间与偏差的比率,影响修正速度与平滑度
stratumweight <值> 选源时 stratum 的权重
dumpdir /var/lib/chrony 保存测量历史的目录,重启后可快速恢复
dumponexit 退出时保存测量历史(chrony 3.x 起为默认行为)
noclientlog 不记录客户端信息(大量客户端时节省内存,但 chronyc clients 将无输出)
clientloglimit <字节> 客户端记录占用的最大内存
ratelimit interval 3 burst 8 对客户端请求限速,防御 DDoS
cmdratelimit 对命令端口限速
hwtimestamp * 启用网卡硬件时间戳(需网卡支持),可达纳秒级精度
refclock PPS /dev/pps0 lock NMEA 配置 PPS/GPS 等本地参考时钟
initstepslew 10 10.0.0.2 启动时先向指定服务器同步一次(chrony 早期选项,现已被 makestep 取代)
lock_all 把 chronyd 锁定在内存中,防止被 swap 出去影响精度
sched_priority <n> 设置实时调度优先级,提高时间处理精度
user chrony 以指定用户运行
pidfile /var/run/chrony/chronyd.pid PID 文件
confdir /etc/chrony.d 附加配置目录(chrony 4.x)
sourcedir /run/chrony-dhcp 动态源目录,DHCP 下发的 NTP 服务器写入此处

3.4 chronyc 常用子命令

chronyc 既可直接带子命令执行,也可进入交互模式:

chronyc                                  # 进入交互模式,输入 help 查看全部命令,quit 退出
chronyc <子命令>                          # 直接执行
chronyc -n <子命令>                       # -n 不做 DNS 反解,直接显示 IP
chronyc -c <子命令>                       # -c 以 CSV 格式输出,便于脚本解析
chronyc -m "cmd1" "cmd2"                 # 执行多条命令
chronyc -h <主机> <子命令>                # 远程查询

sources —— 查看时间源(最常用)

chronyc sources                          # 简要
chronyc sources -v                        # -v 带列名说明,推荐
chronyc -n sources -v                     # 不解析域名
210 Number of sources = 4
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 203.107.6.88                  2   6   377    41   +286us[ +302us] +/-   14ms
^+ 120.25.115.20                 2   6   377    38   -1256us[-1240us] +/-   21ms
^- 84.16.73.33                   1   6   377   102  +12882us[+12898us] +/-   31ms
^? 10.0.0.99                     0   6     0     -     +0ns[   +0ns] +/-    0ns

M 列(第 1 个字符)—— 源的模式:

符号 含义
^ server,普通的 NTP 服务器(通过 server/pool 配置)
= peer,对等节点(通过 peer 配置)
# 本地参考时钟(GPS、PPS 等硬件时钟)

S 列(第 2 个字符)—— 源的状态,最关键:

符号 含义
* 当前正在同步的源(选中的源)。必须有一个 * 才说明同步正常
+ 可接受的候选源,与选中源结合使用
- 被合并算法排除的源(时间与多数派不一致)
? 失去连接的源,或刚启动还未建立连接(也表示丢包严重)
x falseticker,chronyd 认为该源的时间明显错误(在说谎)
~ 该源的时间变化性过大(jitter 太大),不可靠

其余各列含义:

列 含义
Name/IP address 源的主机名或 IP
Stratum 该源的层级号。0 表示无法确定(不可达)
Poll 轮询间隔的 log2 值(不是秒数!)。6 = 2^6 = 64 秒,10 = 1024 秒
Reach 可达性寄存器,八进制。377 = 最近 8 次全部成功(理想);0 = 完全不可达
LastRx 距上次收到该源响应的秒数(m=分,h=时,d=天)。应小于 Poll 对应的秒数
Last sample 上次测量的详细结果,格式为 <偏移>[<修正后偏移>] +/- <误差范围>。第一个值是原始测量偏移,方括号中是经过 slew 修正后的实际偏移,+/- 后是该测量的误差边界。单位自动缩放:ns/us/ms/s

Last sample 判读要点:方括号中的值应尽可能接近 0。若持续在毫秒甚至秒级,说明同步不良。

sourcestats —— 源的统计分析

chronyc sourcestats
chronyc sourcestats -v
210 Number of sources = 4
Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
===============================================================================
203.107.6.88               18  10   17m     -0.012      0.253   +312us    89us
120.25.115.20              15   8   15m     +0.045      0.412  -1180us   142us
84.16.73.33                 9   5    8m     -0.234      1.821 +12903us  1245us
列 含义
NP Number of sample Points,当前保留的采样点数量(最多 64)
NR Number of Runs,回归残差的符号连续段数量。理想情况约为 NP/2;数值过小说明线性拟合不佳(测量有系统性偏差)
Span 最早与最晚采样点之间的时间跨度
Frequency 估算出的本机时钟相对该源的频率偏差(ppm)。正值表示本机时钟走得快
Freq Skew 频率估计的误差范围(ppm)。越小说明估计越可信
Offset 由回归线推算出的当前时间偏移
Std Dev 采样的标准差,反映测量的离散程度/网络抖动

tracking —— 系统时钟状态(最重要的诊断命令)

chronyc tracking
Reference ID    : CB6B0658 (203.107.6.88)
Stratum         : 3
Ref time (UTC)  : Sat Aug 22 02:48:46 2026
System time     : 0.000000312 seconds slow of NTP time
Last offset     : -0.000000156 seconds
RMS offset      : 0.000004521 seconds
Frequency       : 12.345 ppm slow
Residual freq   : -0.002 ppm
Skew            : 0.253 ppm
Root delay      : 0.005617 seconds
Root dispersion : 0.000891 seconds
Update interval : 64.2 seconds
Leap status     : Normal

每个字段的含义:

字段 含义
Reference ID 当前参考源的 ID(十六进制的 IP)与地址。若显示 7F7F0101 () 表示使用本地时钟;显示 00000000 () 表示尚未同步任何源
Stratum 本机当前的层级号。比参考源高 1 层。16 表示未同步
Ref time (UTC) 最后一次从参考源接收到测量数据的 UTC 时间。若很久没更新说明失联
System time 系统时钟与 NTP 真实时间的当前差值。slow of NTP time 表示本机慢,fast of NTP time 表示本机快。这是最关键的指标,正常应在微秒到毫秒级。注意:这是"尚未修正完"的残余偏差,因为 chronyd 是渐进修正的
Last offset 上一次时钟更新时测得的偏移量。负值表示本机快于参考源
RMS offset 偏移量的长期均方根值,反映整体同步质量。越小越好,理想 < 1ms
Frequency 本机时钟的固有频率误差(ppm),即"如果 chronyd 不干预,时钟每秒会偏差多少"。12.345 ppm slow 表示每秒慢 12.345 微秒(每天约慢 1.07 秒)。这个值会被写入 driftfile
Residual freq 针对当前参考源的残余频率偏差。反映测量值与当前采用的频率补偿之间的差异,应接近 0
Skew 频率估计的误差范围(ppm)。越小说明频率估计越可信
Root delay 到 stratum 1 根源的累计网络往返延迟总和(秒)
Root dispersion 到根源的累计离散度(所有中间服务器误差的累加)。Root delay/2 + Root dispersion 决定了理论最大误差
Update interval 最近两次时钟更新之间的间隔(秒)
Leap status 闰秒状态:Normal(正常)、Insert second(将插入闰秒)、Delete second(将删除闰秒)、Not synchronised(未同步,异常)

快速判断是否同步正常:Leap status 为 Normal 且 Stratum 不为 16 且 System time 在毫秒级以内。

脚本化检查:

# 检查是否同步
chronyc tracking | grep "Leap status" | grep -q Normal && echo OK || echo "NOT SYNCED"

# 提取当前偏差(秒)
chronyc tracking | awk '/System time/{print $4}'

# 用 CSV 格式便于解析
chronyc -c tracking

makestep —— 立即跳变

chronyc makestep                         # 立即把当前偏差用 step 方式一次性修正
chronyc makestep 0.1 1                    # 临时设置阈值 0.1 秒、次数 1

用途:偏差较大且不想等 slew 慢慢修正时(如刚开机、刚从休眠恢复、虚拟机快照恢复后)。

生产慎用:会造成时间跳变,可能回拨。应在业务停止或流量摘除后执行。

activity —— 源的活动概览

chronyc activity
200 OK
4 sources online
0 sources offline
0 sources doing burst (return to online)
0 sources doing burst (return to offline)
0 sources with unknown address
行 含义
sources online 当前在线(正在轮询)的源数量
sources offline 被标记为离线的源(通过 chronyc offline 或配置 offline)
doing burst (return to online) 正在执行 burst 且完成后回到 online 状态
doing burst (return to offline) 正在执行 burst 且完成后回到 offline 状态
sources with unknown address 域名无法解析的源

配合 online/offline 使用(笔记本/拨号环境):

chronyc offline                          # 把所有源标记为离线(断网前执行)
chronyc offline 10.0.0.0/24               # 只对某网段的源
chronyc online                            # 恢复在线(联网后执行)
chronyc online 10.0.0.2

clients —— 查看哪些客户端在向本机同步(服务端用)

chronyc clients
Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
===============================================================================
10.0.0.3                      142      0   6   -    31       0      0   -     -
10.0.0.4                       98      0   6   -    12       0      0   -     -
10.0.0.5                       35      0   7   -   105       0      0   -     -
列 含义
Hostname 客户端主机名/IP
NTP 收到的 NTP 请求包数量
Drop(NTP 侧) 因限速被丢弃的包数
Int 客户端平均轮询间隔的 log2 值
IntL 限速后的实际间隔
Last(NTP 侧) 距上次收到该客户端请求的秒数
Cmd 收到的命令请求(chronyc 远程管理)数量
Drop/Int/Last(Cmd 侧) 命令请求对应的统计

若配置了 noclientlog,此命令无输出(报 506 Cannot talk to daemon 或空列表)。

ntpdata —— 查看与某个源的最近一次 NTP 报文细节

chronyc ntpdata                          # 所有源
chronyc ntpdata 203.107.6.88              # 指定源
Remote address  : 203.107.6.88 (CB6B0658)
Remote port     : 123
Local address   : 10.0.0.2 (0A000002)
Leap status     : Normal
Version         : 4
Mode            : Server
Stratum         : 2
Poll interval   : 6 (64 seconds)
Precision       : -24 (0.000000060 seconds)
Root delay      : 0.001923 seconds
Root dispersion : 0.000534 seconds
Reference ID    : 646B1972 (100.107.25.114)
Reference time  : Sat Aug 22 02:47:42 2026
Offset          : +0.000286312 seconds
Peer delay      : 0.005617021 seconds
Peer dispersion : 0.000000954 seconds
Response time   : 0.000041231 seconds
Jitter asymmetry: +0.00
NTP tests       : 111 111 1110
Interleaved     : No
Authenticated   : No
TX timestamping : Kernel
RX timestamping : Kernel
Total TX        : 142
Total RX        : 142
Total valid RX  : 142

用于深入排查协议层问题:NTP tests 三组测试位全为 1 表示通过所有校验;Authenticated 显示是否使用了认证;TX/RX timestamping 显示时间戳的获取层次(Hardware > Kernel > Daemon,越靠底层精度越高)。

waitsync —— 等待同步完成(脚本中非常有用)

chronyc waitsync                          # 阻塞等待,直到时钟同步完成
chronyc waitsync 60                       # 最多等 60 次检查(默认每次间隔 10 秒)
chronyc waitsync 30 0.01                  # 最多 30 次,且要求 offset 小于 0.01 秒
chronyc waitsync 10 0.1 0.0 5             # 参数:最大次数 最大偏差 最大偏斜 检查间隔(秒)

四个参数依次为:max-tries、max-correction(秒)、max-skew(ppm)、interval(秒)。

退出码:0 = 同步成功,1 = 超时未同步。适合在自动化部署脚本中使用:

systemctl start chronyd
if chronyc waitsync 30 0.1; then
    echo "时间同步完成,继续部署"
else
    echo "时间同步失败,中止部署" >&2
    exit 1
fi

burst —— 立即发起密集测量

chronyc burst 4/4                        # 对所有源发 4 个包(至少收到 4 个响应)
chronyc burst 2/10                        # 最多发 10 个,至少要 2 个成功
chronyc burst 4/4 10.0.0.2                # 只对指定源

用于加速同步或临时提高测量精度。执行后需配合 chronyc makestep 才会立即应用。

add server / delete —— 运行时增删源

chronyc add server ntp.aliyun.com iburst          # 动态添加一个服务器
chronyc add server 10.0.0.2 iburst minpoll 4 maxpoll 6
chronyc add peer 10.0.0.3 iburst                   # 添加对等节点
chronyc add pool cn.pool.ntp.org iburst maxsources 3

chronyc delete 10.0.0.99                          # 删除指定源
chronyc delete ntp.aliyun.com

注意:这些改动仅在内存中生效,重启 chronyd 后丢失。要永久生效必须写入 /etc/chrony.conf。适合临时测试新时间源。

其他常用子命令

chronyc reload sources                    # 重新加载 sourcedir 中的源(不重启服务)
chronyc rekey                             # 重新读取 keyfile
chronyc dump                              # 把测量历史写入 dumpdir
chronyc smoothtime activate                # 激活时间平滑(leap smear)
chronyc smoothing                          # 查看平滑状态
chronyc serverstats                        # 服务端统计(收到多少请求、丢弃多少)
chronyc rtcdata                            # 查看 RTC 跟踪数据(需 rtcfile)
chronyc trimrtc                            # 立即校正 RTC
chronyc writertc                           # 把 RTC 参数写入 rtcfile
chronyc settime "Aug 22 10:30:00 2026"     # 手工设置时间(需 manual 配置项启用)
chronyc manual on                           # 启用手工设置时间功能
chronyc local stratum 10                    # 运行时设置 local stratum
chronyc local off                           # 关闭 local 模式
chronyc refresh                             # 重新解析所有源的 DNS
chronyc reset sources                       # 重置所有源的测量数据
chronyc shutdown                            # 让 chronyd 退出
chronyc authdata                            # 查看认证状态
chronyc selectdata                          # 查看选源算法的详细判定过程(chrony 4.x)
chronyc help                                # 查看全部命令

chronyc serverstats 输出(排查是否被 DDoS 时用):

NTP packets received       : 15234
NTP packets dropped        : 0
Command packets received   : 42
Command packets dropped    : 0
Client log records dropped : 0
NTS-KE connections accepted: 0
NTS-KE connections dropped : 0
Authenticated NTP packets  : 0

3.5 timedatectl 与 chronyd 的配合

timedatectl 是 systemd 提供的统一入口,它并不自己同步时间,而是调度底层的时间同步服务。

timedatectl set-ntp true                 # 启用 NTP 同步
timedatectl set-ntp false                # 关闭 NTP 同步
timedatectl status

set-ntp true 的实际行为:systemd 会查找并启动系统上已安装的时间同步服务。查找顺序由 /usr/lib/systemd/ntp-units.d/ 目录下的文件决定:

ls /usr/lib/systemd/ntp-units.d/
cat /usr/lib/systemd/ntp-units.d/*.list
chronyd.service
ntpd.service
  • 若安装了 chrony,set-ntp true 等价于 systemctl enable --now chronyd。
  • 若只安装了 ntp,则等价于 systemctl enable --now ntpd。
  • 若两者都装了,按列表顺序取第一个可用的(通常是 chronyd),并停止其他时间同步服务,从而自动避免冲突。

timedatectl status 中与同步相关的两个字段:

     NTP enabled: yes            ← set-ntp 的状态:是否有时间同步服务被 systemd 管理
NTP synchronized: yes            ← 内核报告的实际同步状态
组合 含义
NTP enabled: yes + NTP synchronized: yes 正常,服务在跑且已同步
NTP enabled: yes + NTP synchronized: no 服务在跑但还没同步上。可能刚启动(等几分钟),或网络/配置有问题(查 chronyc tracking)
NTP enabled: no + NTP synchronized: yes 曾经同步过(内核标志位仍保留),但同步服务已停止。时间会逐渐漂移
NTP enabled: no + NTP synchronized: no 完全没有时间同步,需要配置

NTP synchronized 的底层来源是 adjtimex() 返回的 STA_UNSYNC 标志位,由 chronyd/ntpd 在同步成功后清除。因此这个字段比 NTP enabled 更能反映真实状态。

重要:启用 NTP 同步时,手工设置时间会被拒绝:

timedatectl set-time "2026-01-01 00:00:00"
Failed to set time: Automatic time synchronization is enabled

必须先关闭:

timedatectl set-ntp false
timedatectl set-time "2026-01-01 00:00:00"
timedatectl set-ntp true                 # 记得改回来

3.6 实验:搭建内网 chrony 服务器

环境规划:

角色 IP 系统 说明
chrony 服务端 10.0.0.2 CentOS 7 可访问外网,对内网提供 NTP 服务
chrony 客户端 10.0.0.3 CentOS 7 仅内网

第 1 步:服务端安装

yum install chrony -y
rpm -qa | grep chrony

第 2 步:服务端配置 /etc/chrony.conf

cp /etc/chrony.conf /etc/chrony.conf.bak
vim /etc/chrony.conf

服务端完整配置:

# ---------- 上游时间源(国内优先) ----------
server ntp.aliyun.com iburst
server ntp1.aliyun.com iburst
server ntp.ntsc.ac.cn iburst
pool cn.pool.ntp.org iburst maxsources 2

# ---------- 漂移文件 ----------
driftfile /var/lib/chrony/drift

# ---------- 启动后前 3 次更新允许偏差 >1 秒时跳变 ----------
makestep 1.0 3

# ---------- 自动把系统时间写回硬件时钟 ----------
rtcsync

# ---------- 至少 2 个源可用才调整时钟,防止单个坏源带偏 ----------
minsources 2

# ---------- 关键:放行内网客户端 ----------
allow 10.0.0.0/24
# 明确拒绝某台可疑主机(可选)
# deny 10.0.0.200

# ---------- 关键:无外网时仍以 stratum 10 对外服务 ----------
local stratum 10

# ---------- 闰秒用 slew 平滑处理,避免应用崩溃 ----------
leapsectz right/UTC
leapsecmode slew

# ---------- 限速,防御 NTP 反射放大攻击 ----------
ratelimit interval 3 burst 8

# ---------- 认证与日志 ----------
keyfile /etc/chrony.keys
logdir /var/log/chrony
log measurements statistics tracking

# ---------- 命令端口只允许本机(安全) ----------
bindcmdaddress 127.0.0.1
bindcmdaddress ::1

第 3 步:服务端启动服务

# 若之前用 ntpd,必须先停掉
systemctl stop ntpd
systemctl disable ntpd

systemctl restart chronyd
systemctl enable chronyd
systemctl status chronyd

第 4 步:服务端验证 123 端口已监听

这是与 ntpd 最大的差异点:chronyd 只有配置了 allow 才会监听 123 端口。

ss -unlp | grep chrony
netstat -unlp | grep chrony
udp  UNCONN  0  0  0.0.0.0:123    0.0.0.0:*  users:(("chronyd",pid=1122,fd=6))
udp  UNCONN  0  0  127.0.0.1:323  0.0.0.0:*  users:(("chronyd",pid=1122,fd=5))

若只看到 323 而没有 123,说明 allow 没配置或没生效,客户端将无法同步。

第 5 步:服务端防火墙放行 UDP 123

firewall-cmd --permanent --add-service=ntp
# 或更精细地只放行内网
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="123" protocol="udp" accept'
firewall-cmd --reload
firewall-cmd --list-all

详见 07-03(防火墙与 SELinux)

第 6 步:服务端验证自身已同步(chrony 通常几十秒即可)

chronyc sources -v
chronyc tracking
chronyc waitsync 30 0.1                  # 等待同步完成,最多 5 分钟
timedatectl status

期望结果:

MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 203.107.6.88                  2   6   377    41   +286us[ +302us] +/-   14ms
^+ 120.25.115.20                 2   6   377    38   -1256us[-1240us] +/-   21ms
Reference ID    : CB6B0658 (203.107.6.88)
Stratum         : 3
Leap status     : Normal

第 7 步:客户端配置

# 在 10.0.0.3 上
yum install chrony -y
cp /etc/chrony.conf /etc/chrony.conf.bak
vim /etc/chrony.conf

客户端最小配置:

# 只指向内网 chrony 服务器(建议配两台)
server 10.0.0.2 iburst prefer
# server 10.0.0.4 iburst

driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync

# 纯客户端:不对外提供服务,也不监听 123 端口
# 不写 allow 即可;更彻底可写 port 0
port 0

logdir /var/log/chrony
systemctl restart chronyd
systemctl enable chronyd

第 8 步:客户端验证

chronyc sources -v
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 10.0.0.2                      3   6   377    21     +21us[  +19us] +/-  5.8ms

注意 Stratum 为 3(服务端),本机为 4;Last sample 应为微秒级(局域网)。

chronyc tracking
Reference ID    : 0A000002 (10.0.0.2)
Stratum         : 4
System time     : 0.000000021 seconds slow of NTP time
Leap status     : Normal
timedatectl status                       # NTP synchronized 应为 yes
date

第 9 步:服务端查看有哪些客户端在同步

# 在服务端 10.0.0.2 上
chronyc clients
chronyc serverstats
Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
===============================================================================
10.0.0.3                       12      0   6   -    31       0      0   -     -

第 10 步:故障演练 —— 断开外网验证 local stratum 生效

# 在服务端模拟外网不可达
iptables -I OUTPUT -p udp --dport 123 -d 203.107.6.88 -j DROP
# 或直接把 server 行注释掉后重启

sleep 600
chronyc sources -v                       # 外网源应变为 ^? 或 ^-
chronyc tracking                          # Reference ID 应变为 7F7F0101 (即本地时钟)
Reference ID    : 7F7F0101 ()
Stratum         : 10
Leap status     : Normal          ← 关键:仍为 Normal,说明仍能对外服务

此时客户端仍应能正常同步(stratum 变为 11):

# 客户端
chronyc sources -v                       # 仍有 ^* 10.0.0.2

恢复:

iptables -D OUTPUT -p udp --dport 123 -d 203.107.6.88 -j DROP
systemctl restart chronyd

第 11 步:两台服务端互备(可选进阶)

在 ntp01(10.0.0.2)与 ntp02(10.0.0.4)上分别添加:

# ntp01 的配置中加
peer 10.0.0.4 iburst
local stratum 10 orphan

# ntp02 的配置中加
peer 10.0.0.2 iburst
local stratum 10 orphan

orphan 选项确保两台互为 peer 时不会形成时间环路:当外网都不可用时,自动选出参考 ID 数值最小的那台作为最终基准,另一台跟随它。

systemctl restart chronyd
chronyc sources -v                       # 应看到 =+ 或 =* 前缀的 peer 行

3.7 ntpd 与 chronyd 的冲突处理

核心原则:ntpd 与 chronyd 绝对不能同时运行。

原因:

  1. 两者都要绑定 UDP 123 端口,后启动的会失败并报 Address already in use。
  2. 更危险的是,若一方以纯客户端模式运行(不绑定 123),两者会同时调整系统时钟,互相"打架",导致时钟剧烈震荡、offset 无法收敛。

检查当前使用的是哪个:

systemctl status chronyd
systemctl status ntpd
systemctl is-active chronyd ntpd
systemctl list-unit-files | grep -E 'ntpd|chronyd'
ss -unlp | grep :123                     # 看是谁占用了 123 端口
ps -ef | grep -E 'ntpd|chronyd' | grep -v grep
timedatectl status | grep NTP

从 chronyd 切换到 ntpd(CentOS 7 上使用旧的 ntpd):

# 1. 停止并禁用 chronyd
systemctl stop chronyd
systemctl disable chronyd
systemctl mask chronyd                   # 可选:彻底屏蔽,防止被其他单元拉起

# 2. 确认 123 端口已释放
ss -unlp | grep :123

# 3. 安装并配置 ntp
yum install ntp -y
vim /etc/ntp.conf

# 4. 先手工同步一次(避免偏差 >1000s 导致 ntpd 拒绝启动)
ntpdate -u ntp.aliyun.com

# 5. 启动 ntpd
systemctl enable --now ntpd
systemctl status ntpd

# 6. 验证
sleep 300
ntpq -pn
ntpstat
timedatectl status

从 ntpd 切换到 chronyd(推荐方向):

# 1. 停止并禁用 ntpd
systemctl stop ntpd
systemctl disable ntpd
systemctl mask ntpd                      # 可选

# 2. 确认端口释放
ss -unlp | grep :123

# 3. 安装并配置 chrony
yum install chrony -y
vim /etc/chrony.conf

# 4. 启动(chronyd 无需先 ntpdate,自己会处理大偏差)
systemctl enable --now chronyd

# 5. 验证
chronyc waitsync 30 0.1
chronyc sources -v
chronyc tracking
timedatectl status

用 timedatectl 统一切换(最简洁):

timedatectl set-ntp false                # 停掉所有时间同步服务
yum remove ntp -y                         # 卸载不需要的那个(可选,最彻底)
yum install chrony -y
timedatectl set-ntp true                  # systemd 自动启动 chronyd 并停掉 ntpd
timedatectl status

检查是否被 mask 卡住:

systemctl unmask chronyd                 # 若之前 mask 过,需先 unmask 才能启动
systemctl unmask ntpd

如果发现两个服务都在运行且时间剧烈震荡(chronyc tracking 的 offset 反复大幅正负跳动),立即停掉其中一个,然后 systemctl restart 保留的那个。

3.8 虚拟机时间同步的特殊问题

虚拟机的时间问题比物理机复杂得多,因为存在两套独立的时间源在竞争:宿主机(通过虚拟化工具)与 NTP 服务器。

为什么虚拟机时间容易不准

  1. CPU 时钟中断被"偷走":虚拟机不独占 CPU,当宿主机 CPU 争抢激烈时,Guest 的定时器中断可能被延迟或丢失,导致 Guest 时钟变慢(这就是 %st/steal time 高时时间容易跑慢的原因)。
  2. 挂起/恢复(Suspend/Resume):虚拟机被挂起时 vCPU 完全停止,Guest 时钟不再前进。恢复后时间会落后于真实时间,落后量等于挂起时长。
  3. 快照恢复(Snapshot Revert):恢复到旧快照会把时间倒退回快照创建时刻,可能一次倒退几天甚至几个月。
  4. vMotion / 热迁移:迁移过程中会有短暂的时钟停顿。
  5. 虚拟机模板克隆:从旧模板克隆出的机器,时间是模板创建时的时间。

VMware Tools / open-vm-tools 的时间同步

VMware 提供了让 Guest 时间跟随 Host 的机制:

# 安装(CentOS 7 推荐用发行版自带的 open-vm-tools)
yum install open-vm-tools -y
systemctl enable --now vmtoolsd
systemctl status vmtoolsd

# 查看时间同步是否开启
vmware-toolbox-cmd timesync status

# 开启 / 关闭 Host-Guest 时间同步
vmware-toolbox-cmd timesync enable
vmware-toolbox-cmd timesync disable

# 查看工具版本与状态
vmware-toolbox-cmd -v
vmware-toolbox-cmd stat hosttime         # 查看宿主机时间
vmware-toolbox-cmd stat speed

配置文件 /etc/vmware-tools/tools.conf:

[timesync]
# 是否启用周期性时间同步
enable = false
# 同步间隔(秒)
tolerance = 0

宿主机同步与 NTP 的冲突(关键坑)

问题:如果同时启用了 VMware Tools 时间同步和 chronyd/ntpd,就有两个进程在改系统时钟:

  • VMware Tools 从宿主机取时间,用 step 方式设置。
  • chronyd 从 NTP 服务器取时间,用 slew 方式调整。

若宿主机时间本身不准(宿主机没配 NTP,或宿主机时区/RTC 设置错误),两者会反复互相覆盖,表现为:

  • chronyc tracking 的 System time 反复跳动,永远无法收敛。
  • 日志中频繁出现 System clock was stepped by ...。
  • 时间在两个值之间来回摆动。

解决方案(二选一,绝不能都开):

方案 A(推荐,生产环境标准做法):关闭虚拟化工具同步,只用 NTP

# 1. 关闭 VMware Tools 的时间同步
vmware-toolbox-cmd timesync disable

# 2. 在 VMware 虚拟机配置中也要关闭(vmx 文件,需关机后编辑)
#    tools.syncTime = "FALSE"
#    time.synchronize.continue = "FALSE"
#    time.synchronize.restore = "FALSE"
#    time.synchronize.resume.disk = "FALSE"
#    time.synchronize.shrink = "FALSE"
#    time.synchronize.tools.startup = "FALSE"
#    time.synchronize.tools.enable = "FALSE"
#    time.synchronize.resume.host = "FALSE"

# 3. 只依赖 chronyd
systemctl enable --now chronyd
chronyc tracking

方案 B:只用宿主机同步,关闭 Guest 内的 NTP(仅适合宿主机时间绝对可信、且 Guest 不做严格时序业务的场景)

systemctl stop chronyd
systemctl disable chronyd
vmware-toolbox-cmd timesync enable

KVM 环境的对应设置(宿主机 XML 中配置 kvm-clock,Guest 内仍推荐跑 chronyd):

# Guest 内确认使用的时钟源
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
kvm-clock
# 可选时钟源
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
kvm-clock tsc acpi_pm

Hyper-V 环境:

# Hyper-V 的时间同步集成服务
lsmod | grep hv_utils
# 关闭方式:在 Hyper-V 管理器中取消勾选"时间同步"集成服务

虚拟机休眠恢复后时间跳变的处理

现象:虚拟机挂起(Suspend)几小时后恢复,date 显示的还是挂起前的时间,落后几小时。

为什么 ntpd 处理不好:ntpd 发现偏差超过 1000 秒会直接拒绝并退出,日志中报:

ntpd[2314]: time correction of 14400 seconds exceeds sanity limit (1000); set clock manually

chronyd 的处理:由 makestep 控制。但注意默认的 makestep 1.0 3 只允许启动后前 3 次跳变,休眠恢复时 chronyd 已运行很久,早已超过 3 次,因此不会自动跳变,只会用 slew 慢慢修正——修正 4 小时的偏差按最大 slew 速率也要很久。

解决方案:

# 方案 1(推荐):恢复后手工触发一次跳变
chronyc makestep

# 方案 2:配置无限次跳变(开发测试环境,或能容忍跳变的场合)
vim /etc/chrony.conf
makestep 1.0 -1                          # -1 = 不限次数,任何时候偏差 >1 秒都跳变
systemctl restart chronyd
# 方案 3:写一个恢复后自动执行的钩子(配合 systemd sleep hook)
vim /usr/lib/systemd/system-sleep/99-chrony-makestep
#!/bin/bash
case "$1" in
    post)
        /usr/bin/chronyc makestep
        /usr/bin/chronyc burst 4/4
        ;;
esac
chmod +x /usr/lib/systemd/system-sleep/99-chrony-makestep
# 方案 4:ntpd 环境下,加 -g 选项与 tinker panic 0
vim /etc/sysconfig/ntpd
OPTIONS="-u ntp:ntp -p /var/run/ntpd.pid -g"
# /etc/ntp.conf 中加
tinker panic 0

快照恢复(时间倒退)的处理:这是最危险的情况,时间向后跳变会破坏数据一致性。恢复快照后应:

# 1. 先不启动业务
systemctl stop mysqld nginx tomcat

# 2. 校正时间
chronyc makestep
# 或
systemctl stop chronyd && chronyd -q 'server ntp.aliyun.com iburst' && systemctl start chronyd

# 3. 确认时间正确后再启动业务
date
chronyc tracking
systemctl start mysqld nginx tomcat

3.9 systemd-timesyncd 简述

systemd-timesyncd 是 systemd 内置的一个极简 SNTP 客户端(Simple NTP),是 systemd 套件的一部分。

特点:

特性 说明
定位 只做客户端,不能作为 NTP 服务端
协议 SNTP(NTP 的简化子集),只做基本的时间查询与调整
精度 比 chronyd/ntpd 低,通常在几十毫秒级
资源 极轻量,内存占用最小
功能 无交叉验证、无统计滤波、无漂移学习(早期版本)、无认证、不能做 peer
适用场景 桌面系统、容器、嵌入式设备、IoT、对精度要求不高的场合
不适用 生产服务器、数据库、集群节点、需要毫秒级精度的场景

在各发行版中的地位:

  • CentOS 7 / RHEL 7:不提供 systemd-timesyncd(被 Red Hat 从构建中移除,官方只支持 chrony)。
  • CentOS 8 / RHEL 8+:同样不提供,官方仍推荐 chrony。
  • Ubuntu 16.04+ / Debian 9+:默认启用 systemd-timesyncd(可安装 chrony 替换)。

常用命令(在支持的发行版上):

systemctl status systemd-timesyncd
systemctl enable --now systemd-timesyncd
timedatectl show-timesync                # 查看同步详情(systemd 239+)
timedatectl timesync-status               # 查看当前同步状态(systemd 239+)
journalctl -u systemd-timesyncd -f

timedatectl timesync-status 输出示例:

       Server: 185.125.190.56 (ntp.ubuntu.com)
Poll interval: 34min 8s (min: 32s; max 34min 8s)
         Leap: normal
      Version: 4
      Stratum: 2
    Reference: 8FDD4C2C
    Precision: 1us (-24)
Root distance: 5.212ms (max: 5s)
       Offset: -1.322ms
        Delay: 8.115ms
       Jitter: 2.041ms
 Packet count: 12
    Frequency: -12.345ppm

配置文件 /etc/systemd/timesyncd.conf:

[Time]
NTP=ntp.aliyun.com ntp1.aliyun.com
FallbackNTP=cn.pool.ntp.org 0.pool.ntp.org
RootDistanceMaxSec=5
PollIntervalMinSec=32
PollIntervalMaxSec=2048
配置项 说明
NTP 主时间服务器列表(空格分隔)
FallbackNTP 主服务器都不可用时的备用列表
RootDistanceMaxSec 可接受的最大根距离(秒),超过则不使用该源
PollIntervalMinSec / PollIntervalMaxSec 轮询间隔的上下限
ConnectionRetrySec 连接失败后的重试间隔
SaveIntervalSec 保存时钟状态的间隔
systemctl restart systemd-timesyncd

注意:systemd-timesyncd 与 chronyd/ntpd 同样互斥,systemd 的 ntp-units.d 机制会自动确保只有一个运行。在 Ubuntu 上安装 chrony 会自动禁用 timesyncd。

三者选择建议:

场景 推荐
CentOS 7+ / RHEL 7+ 生产服务器 chronyd
CentOS 6 / RHEL 6 生产服务器 ntpd(chrony 需从 EPEL 装)
需要对外提供 NTP 服务(内网时间服务器) chronyd 或 ntpd
虚拟机、云主机、笔记本 chronyd
桌面系统、容器、嵌入式 systemd-timesyncd 足够
需要 GPS/PPS 硬件授时、纳秒级精度 chronyd(配合 hwtimestamp)或 ptp4l(PTP/IEEE 1588)
数据库、金融交易、集群节点 chronyd,且配置 minsources 2 与至少 4 个源

排错清单

5.1 时间同步不生效的通用排查流程

按顺序逐步排查,不要跳步:

# 第 1 步:确认当前时间与时区
date
timedatectl                                # CentOS 7
cat /etc/sysconfig/clock                    # CentOS 6
ls -l /etc/localtime

# 第 2 步:确认同步服务在运行,且只有一个
systemctl status chronyd ntpd               # CentOS 7
/etc/init.d/ntpd status                     # CentOS 6
ps -ef | grep -E 'ntpd|chronyd' | grep -v grep
ss -unlp | grep :123                        # 看是谁占了 123 端口

# 第 3 步:确认配置文件语法与内容正确
grep -v '^#' /etc/ntp.conf | grep -v '^$'
grep -v '^#' /etc/chrony.conf | grep -v '^$'

# 第 4 步:确认能连通上游时间源
ping -c 3 ntp.aliyun.com
ntpdate -q -u ntp.aliyun.com                # 只查询不修改
chronyd -Q 'server ntp.aliyun.com iburst'   # chrony 的等价操作

# 第 5 步:确认防火墙放行了 UDP 123(出站与入站)
iptables -L -n | grep 123                   # CentOS 6
firewall-cmd --list-all                     # CentOS 7
# 抓包确认有无往返
tcpdump -i any -nn udp port 123 -c 20

# 第 6 步:查看同步状态
ntpq -pn ; ntpstat ; ntptime                # ntpd
chronyc sources -v ; chronyc tracking       # chrony

# 第 7 步:查看日志
grep -Ei 'ntpd|chronyd' /var/log/messages | tail -40
tail -50 /var/log/ntp.log
journalctl -u chronyd -n 50                 # CentOS 7
journalctl -u ntpd -n 50

# 第 8 步:等待足够时间(这一步最常被忽略!)
# ntpd 需要 5~15 分钟;chrony 通常几十秒
watch -n 5 'ntpq -pn'
chronyc waitsync 30 0.1

5.2 ntpq -p 全是空前缀或有 x 标记

现象 可能原因 排查/解决
所有行前缀都是空格(全部被 reject) 服务刚启动,还在初始化 等 5~15 分钟。refid 显示 .INIT. 就是还在初始化
网络不通 / DNS 解析失败 ping <源>、nslookup <源>、host <源>
防火墙拦截了 UDP 123 tcpdump -nn udp port 123 看有无回包;检查 iptables/firewalld/安全组
上游源的 st = 16(自身未同步) 换其他时间源
上游源的 reach = 0 完全不可达,换源
本机 offset 超过 1000 秒,被 panic 限制卡住 先 ntpdate -u <源> 手工拉正,或 ntpd -g,或配置 tinker panic 0
延迟过大(delay > 1000ms) 换延迟更低的源(国内用阿里云)
认证失败(配了 key 但密钥不匹配) 检查 /etc/ntp/keys 与 trustedkey
restrict 把源自己也挡住了 确认有 restrict 127.0.0.1 这一行
有 x 标记(falsetick) 该源的时间明显错误,被交叉验证算法判为"假票" 用 ntpdate -q <该源> 单独测该源的偏差,确认后从配置中删除该源
只配了 2 个源且互相矛盾 增加到 4 个以上源,让多数派算法能正常工作
网络路径严重不对称(去程回程延迟差异大) 换源或换网络路径
有 - 标记(outlier) 该源与多数派差异较大但没到 falsetick 程度 通常无害,若持续出现可考虑换源
reach 不是 377 有丢包 ping -c 100 <源> 看丢包率;检查网络质量与 MTU
when 持续增大或显示 - 已失联 检查网络与防火墙
只有一个源且是 .LOCL. 所有外部源都不可用,退化到本地时钟 修网络;确认这是不是 fudge stratum 10 的预期兜底行为

对应的 chrony 版本:

现象 原因/解决
chronyc sources 全是 ^? 不可达。等待、检查网络、检查防火墙
^x falseticker,该源时间错误,删掉它
^~ 该源抖动过大,网络质量差
^- 被合并算法排除,通常无害
*没有任何 `^`** 未同步。chronyc tracking 看 Reference ID 是否为 00000000
chronyc tracking 显示 Leap status : Not synchronised 未同步,按 5.1 流程排查
minsources 2 但只有 1 个源可用 不会调整时钟。降低 minsources 或修复其他源

5.3 offset 持续很大

现象 原因 解决
offset 稳定在几十到几百毫秒 网络延迟大且不对称(公网、跨境) 换本地/国内时间源;用局域网内的 NTP 服务器
offset 持续在秒级且不收敛 ntpd 只能 slew(500ppm),修正 1 秒需 33 分钟 耐心等待,或先 ntpdate 一次性拉正
被 tinker step 0 或 ntpd -x 限制只能 slew 检查配置与启动参数
offset 剧烈正负跳动 有两个进程同时在改时钟(ntpd + chronyd,或 NTP + VMware Tools) ps -ef | grep -E 'ntpd|chronyd';vmware-toolbox-cmd timesync status。只保留一个
也可能是 crontab 里还有 ntpdate 定时任务在跳变 crontab -l、cat /etc/cron.*/* 检查并删除
offset 缓慢单向增大 本机晶振漂移严重,driftfile 未正常工作 检查 /var/lib/ntp/drift 的权限与内容;ls -l /var/lib/ntp/
虚拟机 offset 大且不稳定 steal time 高,定时器中断被延迟 top 看 %st;宿主机减负;换 kvm-clock/tsc 时钟源
虚拟机休眠恢复后 chronyc makestep 手工跳变
chronyc tracking 的 Frequency 值很大(>100 ppm) 硬件时钟质量差或温度影响 正常范围内 chrony 能补偿;超过 500ppm 需换硬件
RTC 与系统时钟偏差大 未写回 RTC hwclock -w;启用 rtcsync(chrony)或 SYNC_HWCLOCK=yes(ntpd)

查看 CPU steal time:

top -bn1 | head -5
vmstat 1 5
cat /proc/stat | head -2
sar -u 1 5                                 # 需要 sysstat 包

5.4 date -s 后被 ntpd/chronyd 改回

这是预期行为,不是故障。

原因:ntpd/chronyd 正在持续运行,它们会不断把系统时钟拉回与 NTP 源一致。你用 date -s 改的时间会在几秒到几十秒内被"纠正"回去。

# 现象复现
date -s "2020-01-01 00:00:00"
date                                        # 显示 2020-01-01
sleep 60
date                                        # 又变回正确时间了

日志中的痕迹:

grep -Ei 'step|clock' /var/log/messages | tail
journalctl -u chronyd | tail
chronyd[1122]: System clock wrong by 208915200.123456 seconds, adjustment started
chronyd[1122]: System clock was stepped by 208915200.123456 seconds
ntpd[2314]: 0.0.0.0 061c 0c clock_step -208915200.123456 s

如果确实需要手工设置时间(测试用途):

# 方法 1(推荐):先停服务
systemctl stop chronyd                     # 或 systemctl stop ntpd
date -s "2020-01-01 00:00:00"
hwclock -w                                  # 若希望重启后保持
# 测试完毕后恢复
systemctl start chronyd

# 方法 2:用 timedatectl(CentOS 7)
timedatectl set-ntp false
timedatectl set-time "2020-01-01 00:00:00"
# 恢复
timedatectl set-ntp true

# 方法 3:CentOS 6
/etc/init.d/ntpd stop
date -s "2020-01-01 00:00:00"
/etc/init.d/ntpd start

在容器/虚拟机中改不了时间:

date -s "2020-01-01"
date: cannot set date: Operation not permitted

原因与解决:

原因 解决
非 root 用户 用 root 或 sudo
Docker 容器(默认没有 CAP_SYS_TIME 能力,且与宿主机共享时钟) 容器不能也不应该改时间;应在宿主机上校准。若确实需要:docker run --cap-add SYS_TIME(不推荐)
SELinux 阻止 ausearch -m avc -ts recent、getenforce,必要时调整策略
只读文件系统 影响 hwclock -w(写 /etc/adjtime),需要重新挂载为读写

5.5 时区改了但 date 显示不变

原因 排查/解决
/etc/localtime 没有真正更新 ls -l /etc/localtime 确认软链接指向正确;md5sum /etc/localtime /usr/share/zoneinfo/Asia/Shanghai 对比是否一致
只改了 /etc/sysconfig/clock 而没改 /etc/localtime /etc/sysconfig/clock 只影响 hwclock 和部分启动脚本,不影响 date。必须同时更新 /etc/localtime
TZ 环境变量覆盖了系统时区 echo $TZ;若有值则 unset TZ;检查 ~/.bashrc、~/.bash_profile、/etc/profile、/etc/profile.d/*.sh 中是否 export 了 TZ
当前 shell 会话缓存了旧时区 退出重新登录,或 exec bash
已运行的服务缓存了旧时区(服务在启动时读取一次时区后就缓存了) 必须重启相关服务:systemctl restart crond rsyslog httpd mysqld tomcat。这是最常见的原因,表现为 date 已正确但日志时间戳仍是旧时区
Java 应用有自己的时区设置 JVM 有独立的时区处理。加 -Duser.timezone=Asia/Shanghai 启动参数
数据库有自己的时区设置 MySQL:SELECT @@global.time_zone, @@session.time_zone;,在 my.cnf 中设 default-time-zone='+08:00' 后重启
tzselect 只是打印建议,没有真正修改 tzselect 的输出需要自己写入配置。用 timedatectl set-timezone(CentOS 7)或 ln -sf(CentOS 6)
tzdata 包过旧,时区规则不对 yum update tzdata -y
Docker 容器时区与宿主机不同 挂载宿主机时区:-v /etc/localtime:/etc/localtime:ro -v /etc/timezone:/etc/timezone:ro,或设置 -e TZ=Asia/Shanghai

完整的时区修改与验证流程:

# CentOS 7
timedatectl set-timezone Asia/Shanghai
ls -l /etc/localtime                       # 确认软链接
date +'%F %T %Z %z'                        # 应显示 CST +0800
unset TZ
echo $TZ                                    # 应为空
systemctl restart crond rsyslog             # 重启依赖时区的服务
timedatectl status

# CentOS 6
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
sed -i 's|^ZONE=.*|ZONE="Asia/Shanghai"|' /etc/sysconfig/clock
cat /etc/sysconfig/clock
date +'%F %T %Z %z'
/etc/init.d/crond restart
/etc/init.d/rsyslog restart

验证服务是否使用了新时区:

# 让 cron 输出一条时间,1 分钟后查看
echo '* * * * * date >> /tmp/crontime.log' | crontab -
sleep 70
cat /tmp/crontime.log
crontab -r                                  # 清理

5.9 防火墙与 SELinux 的影响

防火墙(最常见的"莫名连不上"原因):

# ===== CentOS 6:iptables =====
iptables -L -n --line-numbers               # 查看规则
iptables -L INPUT -n -v                     # 带计数器,看是否有包被匹配
service iptables status
# 临时关闭测试(确认是否为防火墙问题)
service iptables stop
# 放行
iptables -I INPUT -p tcp --dport 23 -j ACCEPT
iptables -I INPUT -p udp --dport 123 -j ACCEPT
service iptables save
service iptables restart
chkconfig iptables on

# ===== CentOS 7:firewalld =====
firewall-cmd --state
firewall-cmd --list-all
firewall-cmd --list-ports
firewall-cmd --list-services
# 临时关闭测试
systemctl stop firewalld
# 放行
firewall-cmd --permanent --add-port=23/tcp
firewall-cmd --permanent --add-port=123/udp
firewall-cmd --permanent --add-service=ntp
firewall-cmd --reload
# 查看被拒绝的包(需先开启日志)
firewall-cmd --set-log-denied=all
tail -f /var/log/messages | grep -i reject

# ===== 通用:抓包确认 =====
tcpdump -i any -nn 'port 23' -c 20          # 服务端执行,看包有没有到
tcpdump -i any -nn 'udp port 123' -c 20

云平台安全组(极易被忽略):阿里云、腾讯云、AWS 的安全组是独立于系统防火墙的一层。系统内 firewall-cmd --list-all 显示已放行,但安全组没放行同样连不上。必须在云控制台检查入方向规则。

SELinux:

getenforce                                  # Enforcing / Permissive / Disabled
sestatus

# 临时切换到 Permissive 测试(不重启,立即生效)
setenforce 0
# 若问题消失 → 确认是 SELinux 导致
setenforce 1                                # 测完改回

# 查看被拒绝的记录
ausearch -m avc -ts recent
ausearch -m avc -ts today | audit2why       # 解释原因
grep denied /var/log/audit/audit.log | tail -20
sealert -a /var/log/audit/audit.log         # 需 setroubleshoot 包,给出修复建议

# 正确的修复方式:生成并加载策略模块(而不是永久关闭 SELinux)
ausearch -m avc -ts recent | audit2allow -M myservice
semodule -i myservice.pp

# 常见的布尔值调整
getsebool -a | grep -E 'tftp|rsync|ftp|nis'
setsebool -P tftp_home_dir on
setsebool -P tftp_anon_write on
setsebool -P rsync_full_access on
setsebool -P nis_enabled on                  # 某些 xinetd 服务需要

# 非标准端口需要打端口标签
semanage port -l | grep -E 'ssh|telnet|tftp'
semanage port -a -t ssh_port_t -p tcp 2222
semanage port -m -t ssh_port_t -p tcp 2222   # 若已存在用 -m 修改
semanage port -d -t ssh_port_t -p tcp 2222   # 删除

# 文件上下文
ls -Z /var/lib/tftpboot
restorecon -Rv /var/lib/tftpboot
semanage fcontext -a -t tftpdir_rw_t "/data/tftp(/.*)?"
restorecon -Rv /data/tftp

SELinux 对时间同步的影响:

getsebool -a | grep ntp
ls -Z /etc/ntp.conf /var/lib/ntp/drift
restorecon -v /etc/ntp.conf
restorecon -Rv /var/lib/ntp/
ls -Z /etc/chrony.conf /var/lib/chrony/
restorecon -Rv /var/lib/chrony/

常见的时间同步 SELinux 问题:手工创建或从其他机器复制 /etc/ntp.conf、/etc/chrony.conf、driftfile 后,文件的 SELinux 上下文不正确,导致服务无法读写。用 restorecon 修复。

防火墙与 SELinux 的完整配置详解,详见 07-03(防火墙与 SELinux)

填空与简答

1. 写出 UTC、GMT、RTC、Localtime 四个缩写的全称与含义。

缩写 全称 含义
UTC Coordinated Universal Time 世界统一时间 / 世界标准时间 / 国际协调时间
GMT Greenwich Mean Time 格林尼治标准时间;UTC 基于 GMT 提供更准确的时间,简单意义上二者等同
RTC Real-Time Clock 实时时钟,由主板上的晶振及相关电路生成脉冲
Localtime 本地时钟 本地所在时区的当前时间

2. Linux 上有哪两类时钟?分别由谁维护?

  • 硬件时钟(RTC / BIOS 时钟):由主板电路维护,靠纽扣电池供电,关机后仍在走,在 BIOS 画面中可设定。
  • 系统时钟(软件时钟):由 Linux 内核(kernel)维护,开机时从 RTC 读入初值,之后独立走时。

两者互相同步用 hwclock --hctosys(硬件 → 系统)与 hwclock --systohc(系统 → 硬件)。

3. NTP 协议使用哪个端口?chrony 的本地监控端口是哪个?

NTP 使用 UDP 123(服务端监听,客户端也从 123 发起,所以 ntpdate 与 ntpd 会抢同一个 socket)。chronyd 额外监听 UDP 323 的 cmdport,只绑在 127.0.0.1 与 ::1 上,供 chronyc 本地管理使用,不对外提供时间服务。

11. 本机已经启动了 ntpd,再执行 ntpdate 会报什么错?为什么?

26 Aug 14:42:52 ntpdate[7417]: the NTP socket is in use, exiting

因为 ntpd 已经独占了 UDP 123 端口,而 ntpdate 也要用 123 端口向外发请求,socket 被占用故直接退出。解决办法:要么 systemctl stop ntpd 后再 ntpdate,要么让 ntpd 自己去同步(正规做法),要么用 ntpdate -u 改用非特权端口发包。

12. 刚搭好的 NTP 服务器为什么不能马上给客户端同步?客户端会看到什么报错?

ntpd 需要先与上游服务器交换若干轮报文,完成时钟频率校准并让自身进入 synchronized 状态(约 5 分钟),期间它的 stratum 为 16(不可用),不会对外提供服务。客户端过早同步会看到:

1 Sep 12:00:03 ntpdate[3127]: no server suitable for synchronization found

13. stratum 是什么?ntpq -p 输出里的 offset、jitter 分别表示什么?

  • stratum(层级):与参考时钟的距离。原子钟/GPS 等参考源为 stratum 0,直连它们的服务器为 1,向下逐层 +1,16 表示未同步/不可用。
  • offset:本地时钟与该服务器的时间差(毫秒),越接近 0 越好。
  • jitter:多次测量的抖动(毫秒),反映网络与服务器的稳定性,越小越好。
  • 行首的 * 表示当前选定的同步源,+ 表示备选,x/-/空白表示被剔除或不可用。

14. ntpd 与 chronyd 能同时运行吗?CentOS 7 默认用哪个?

不能。两者都要绑定 UDP 123,同时启动后启动晚的那个会失败。CentOS 7 默认使用 chronyd,切换到 ntpd 前必须 systemctl stop chronyd && systemctl disable chronyd。

命令练习

二、命令练习

练习 1(源题):使用 date 查看并修改系统时间,使用 hwclock 在系统时钟与硬件时钟之间互相同步。

# 查看
date +'%F %T'                           # 2016-01-19 10:48:46
date +'%Y-%m-%d %H:%M:%S'               # 等效写法
date +'%F %T %Z %z'                     # 额外看时区名与 UTC 偏移

# 修改系统时间
date -s 2015-09-02                      # 只改日期,不改时间
date -s 11:08:00                        # 只改时间,不改日期
date -s "2015-09-18 04:53:00"           # 同时改日期和时间

# 硬件时钟
hwclock -r                              # 读取硬件时间(等价 hwclock --show)
hwclock -w                              # 系统时间 → 硬件时间(等价 --systohc)
hwclock --set --date="07/07/16 10:19"   # 直接设定硬件时间(月/日/年 时:分)
hwclock --hctosys                       # 硬件 → 系统(hc=硬件,sys=系统)
hwclock --systohc                       # 系统 → 硬件

要点:改完系统时间后一定要 hwclock -w 落到 RTC,否则重启后时间又回去了。

练习 2(源题):使用 tzselect / timedatectl 查看并设置系统时区为 Asia/Shanghai。

# CentOS 6 及通用做法
cat /etc/sysconfig/clock                # 查看配置中的时区(对 hwclock 生效)
tzselect                                # 交互式选择:5)Asia → China → Beijing Time → 1
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime      # 真正生效靠这一步
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime   # 或做软链接

# CentOS 7
timedatectl                             # 系统时间、RTC 时间、时区、NTP 状态一屏看全
timedatectl list-timezones | grep -i shanghai
timedatectl set-timezone Asia/Shanghai
date +'%F %T %Z %z'                     # 验证:CST +0800

练习 3(源题):使用 ntpdate 同步至少两个公网 NTP 服务器,观察输出差异。

ntpdate 202.112.31.197
ntpdate 0.rhel.pool.ntp.org             # RHEL 默认池,另有 1./2. 前缀
ntpdate time.nist.gov
ntpdate ntp.fudan.edu.cn                # 复旦大学时间服务器
ntpdate ntp.aliyun.com                  # 国内推荐,延迟低

同步成功的输出形如:

26 Aug 14:43:29 ntpdate[7117]: step time server 61.129.42.44 offset 146586394.753774 sec

观察点:

  • step 表示时间被跳变校正(偏差大于 128ms),adjust 表示平滑校正(偏差小)。
  • offset 就是校正量,正数说明本地时间慢了。
  • 公网服务器 IP 很多,部分不可达属正常,多试几个;不改时间只测试可加 -q:ntpdate -q ntp.aliyun.com。

练习 4(补充):用 timedatectl 修改时区,并开关 NTP 自动同步,观察状态字段变化。

timedatectl                             # 记录当前 "NTP enabled" / "NTP synchronized"

timedatectl set-timezone Asia/Tokyo     # 先改成东京,date 应显示 JST,比北京快 1 小时
date +'%F %T %Z %z'
timedatectl set-timezone Asia/Shanghai  # 再改回上海

timedatectl set-ntp false               # 关闭自动同步(CentOS 7 上等于停 chronyd)
systemctl is-active chronyd             # 应为 inactive
timedatectl set-timezone ... ; date -s "2020-01-01 08:00:00"   # 此时才能手工改时间

timedatectl set-ntp true                # 打开自动同步
systemctl is-active chronyd             # 应为 active
timedatectl                             # NTP synchronized: yes(可能要等十几秒)

要点:

  1. set-ntp true/false 本质是在启停 chronyd(或 ntpd / systemd-timesyncd)。
  2. NTP 处于启用状态时,date -s 与 timedatectl set-time 会被拒绝,报 Failed to set time: Automatic time synchronization is enabled——必须先 set-ntp false。
  3. 只改时区不会改变 UTC 时刻,只改变显示;hwclock 是否受影响取决于 RTC 记的是 UTC 还是 localtime(看 timedatectl 的 RTC in local TZ)。

练习 5(补充):手工用 ntpdate 同步,并解释为什么 ntpd 运行时它一定失败。

# 第 1 步:确认 ntpd 在跑,然后直接同步 —— 必然失败
systemctl start ntpd                    # CentOS 6: /etc/init.d/ntpd start
ntpdate ntp.aliyun.com
# the NTP socket is in use, exiting

# 第 2 步:看是谁占了 123 端口
ss -unlp | grep :123                    # CentOS 6: netstat -ntlup | grep :123
# UNCONN 0 0 *:123 *:*  users:(("ntpd",pid=7188,fd=16))

# 第 3 步:停掉 ntpd 再同步
systemctl stop ntpd
ntpdate ntp.aliyun.com                  # 成功,输出 step/adjust
hwclock -w                              # 写回硬件时钟

# 第 4 步:把同步交还给 ntpd(正规做法)
systemctl start ntpd
ntpq -p                                 # 等几分钟出现 * 号
ntpstat                                 # synchronised to NTP server ...

解释:ntpdate 与 ntpd 都要绑定 UDP 123,端口只能被一个进程独占,因此 ntpd 运行时 ntpdate 拿不到 socket 就退出。三种化解方式:

  1. 先停服务再同步(最常用);
  2. ntpdate -u ntp.aliyun.com:-u 让 ntpdate 用非特权高位端口发包,可与 ntpd 共存;
  3. 别用 ntpdate,改用 ntpd -gq(同步一次即退出)或 chrony 的 chronyc makestep。

练习 6(源题):安装并启用 xinetd,开启 time-dgram 与 time-stream,在客户端用 rdate -s 完成时间同步。

# server 端
yum install xinetd -y
vim /etc/xinetd.d/time-dgram            # UDP:disable = yes 改为 no
vim /etc/xinetd.d/time-stream           # TCP:disable = yes 改为 no

# CentOS 6
/etc/init.d/xinetd restart
chkconfig xinetd on
# CentOS 7
systemctl restart xinetd
systemctl enable xinetd

# 确认 37 端口已监听
netstat -ntlup | grep :37
tcp  0  0 :::37  :::*  LISTEN  5740/xinetd
udp  0  0 :::37  :::*          5740/xinetd
# 放行防火墙
firewall-cmd --permanent --add-port=37/tcp --add-port=37/udp
firewall-cmd --reload

# client 端
yum install rdate -y
rdate -p 10.0.0.2                       # 只打印服务器时间,不改本机
rdate -s 10.0.0.2                       # 用服务器时间设置本机
date

要点:37 端口的 time 协议只有秒级精度且无鉴权,chkconfig --list 中 xinetd based services 段落可一次看全所有托管服务的开关状态。


本章小结

一、命令速查表

命令 一句话用途
date 查看系统(软件)时间,+'%F %T' 指定格式
date -s "2015-09-18 04:53:00" 手工设置系统时间(NTP 启用时会被拒绝)
hwclock -r 读取硬件时钟(RTC/BIOS 时间)
hwclock -w / --systohc 把系统时间写入硬件时钟(改完时间必做,否则重启失效)
hwclock --hctosys 反向:以硬件时钟为准校正系统时钟
hwclock --set --date="07/07/16 10:19" 直接设定硬件时钟(月/日/年 时:分)
tzselect 交互式查看时区列表(只提示,真正生效要改 /etc/localtime)
timedatectl CentOS 7 一屏看全系统时间、RTC 时间、时区、NTP 状态
timedatectl list-timezones 列出所有可用时区
timedatectl set-timezone Asia/Shanghai 设置时区
timedatectl set-ntp true/false 开关自动时间同步(等于启停 chronyd/ntpd)
ntpdate ntp.aliyun.com 一次性同步指定 NTP 服务器(ntpd 运行时会因 123 端口被占而失败)
ntpdate -q <server> 只查询偏差,不修改本机时间
ntpdate -u <server> 用非特权端口发包,可与运行中的 ntpd 共存
ntpq -p 查看 ntpd 的上游列表、stratum、offset、jitter,* 为当前选定源
ntpstat 一行判断 ntpd 是否已同步及误差范围
chronyc tracking 查看 chronyd 的参考源、stratum、系统时间偏差、频率漂移
chronyc sources -v 查看所有时间源状态,^* 为当前选定源
chronyc sourcestats 查看各源的偏移与频率统计,判断稳定性
chronyc makestep 立即跳变校正一次时间(相当于 chrony 版的 ntpdate)
rdate -s 10.0.0.2 用 37 端口 time 协议同步时间;-p 只打印不修改
systemctl status/start/enable chronyd CentOS 7 管理时间同步服务
systemctl restart xinetd 修改 /etc/xinetd.d/* 后重载配置(不重启不生效)
chkconfig --list \| grep xinetd CentOS 6 查看 xinetd 及其托管服务的开关状态
ss -unlp \| grep :123 确认 NTP 端口监听及占用进程(旧写法 netstat -ntlup)
lsof -i:22 确认端口的监听进程是 sshd 还是 xinetd(验证托管是否成功)

二、CentOS 6 与 CentOS 7 对照表

对比项 CentOS 6 CentOS 7
时间同步服务 ntpd(主流)+ ntpdate 定时任务 chronyd(默认,推荐);ntpd 需手工安装并停掉 chronyd
服务管理 /etc/init.d/ntpd restart、service ntpd status systemctl restart ntpd、systemctl status chronyd
开机自启 chkconfig ntpd on systemctl enable chronyd
时区设置方式 tzselect 提示 + cp/ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime,另写 /etc/sysconfig/clock timedatectl set-timezone Asia/Shanghai(自动维护 /etc/localtime 软链接)
时区配置文件 /etc/sysconfig/clock、/etc/localtime /etc/localtime(/etc/sysconfig/clock 已废弃)
主配置文件 /etc/ntp.conf /etc/chrony.conf(ntpd 仍用 /etc/ntp.conf)
状态查询 ntpq -p、ntpstat chronyc tracking、chronyc sources -v、timedatectl
立即校正 ntpdate <server>(需先停 ntpd) chronyc makestep,或 makestep 1.0 3 自动生效
漂移文件 /var/lib/ntp/drift /var/lib/chrony/drift
密钥文件 /etc/ntp/keys /etc/chrony.keys
客户端访问控制 restrict 10.0.0.0 mask 255.255.255.0 nomodify notrap allow 10.0.0.0/24 / deny all(语法更简洁)
防火墙放行 iptables -I INPUT -p udp --dport 123 -j ACCEPT + service iptables save firewall-cmd --permanent --add-service=ntp + --reload
xinetd 状态 chkconfig --list 的 xinetd based services 段落 systemctl status xinetd + grep -l 'disable.*no' /etc/xinetd.d/*

chrony 相对 ntpd 的优势:在网络间断、间歇联网、虚拟机休眠唤醒等场景下收敛更快,能用更少的报文达到更高精度,还支持时钟频率的长期学习。生产环境 CentOS 7+ 优先用 chrony。

四、排错口诀

  1. 先看服务在不在跑,再看能不能通 123 端口,再看 stratum 与 offset。 systemctl status chronyd|ntpd → ss -unlp | grep :123 → chronyc sources -v / ntpq -p。三步走完,八成的问题已经定位。

  2. 123 端口只有一个主人:ntpd、chronyd、ntpdate 三者不能同台共舞。 看到 the NTP socket is in use 就先停服务;看到服务起不来就查是不是另一个时间服务已经占了端口。

  3. UDP 不是 TCP:telnet 测不出 123 通不通,防火墙也别只放 TCP。 用 ntpdate -q、chronyc sources、nmap -sU -p123 验证,放行写 --add-service=ntp 或 --add-port=123/udp。

  4. stratum 16 等于没同步:客户端报 no server suitable 先怀疑服务端还没准备好。 新建的 NTP 服务器要等约 5 分钟自身同步完成才能对外服务;fudge 127.127.1.0 stratum 10 可作外网断开时的兜底。

  5. 时间对不上先分清"时刻错"还是"时区错"。 date -u 与 date 对比:UTC 正确、本地时间偏整小时 → 时区问题(timedatectl set-timezone);UTC 本身就错 → 同步问题。改完时间别忘 hwclock -w,否则重启前功尽弃。

  6. xinetd 排错四问:disable 改了没、xinetd reload 了没、only_from/access_times 有没有把自己挡在外面、端口改了 /etc/services 跟了没。 被 xinetd 拒绝的连接不会唤起子进程,所以要去 xinetd 的日志(log_type 指定的文件或 /var/log/messages)找 FAIL: address / per_source_limit / time,而不是去翻服务自身的日志。

  7. 一层层剥:防火墙 → xinetd 的 only_from → TCP Wrappers(hosts.allow 先于 hosts.deny)→ 服务自身认证 → 权限与 SELinux。 多层控制是"与"的关系,任何一层拒绝都连不上;被 SELinux 拦住的特征是服务本身正常但操作被拒,用 ausearch -m avc -ts recent 确认。

最后修改:2026 年 09 月 20 日
如果觉得我的文章对你有用,请随意赞赏