一、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、日志、文件时间戳)读到的都是系统时钟。
两者的关系与工作流程:
- 开机:内核启动时读取 RTC,用它来初始化系统时钟(这一步由
hwclock --hctosys或 systemd 内部完成)。 - 运行期间:系统时钟由内核自行维护,与 RTC 完全脱钩,各走各的。此时用
date -s改系统时间不会影响 RTC,用hwclock --set改 RTC 也不会影响系统时间。 - 关机:正常关机流程会执行
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:
date -s是纯 step,时间可能向后回拨。数据库(MySQL、Redis)、Java 应用(System.currentTimeMillis())、消息队列的顺序判定、TTL 过期计算全部依赖时间单调递增,回拨会导致数据错乱、锁超时判断失效、缓存永不过期。crond在时间回拨后会重新执行已跑过的任务;时间快进则会跳过任务。- 集群心跳/租约(Keepalived、ZooKeeper、etcd)以时间差判断节点存活,跳变会引发误判下线、脑裂、频繁选主。
date -s只改系统时钟,不写回 RTC,重启后偏差又回来了。- 正确做法:让
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 方案的缺点(生产环境不推荐):
- 跳变式修改:
ntpdate偏差超过 128ms 就直接 step 跳变,可能造成时间回拨,引发前述各类故障。而ntpd/chronyd是持续 slew 微调。 - 同步间隔粗糙:两次同步之间时钟自由漂移,偏差在同步瞬间被一次性抹平,形成"锯齿状"误差曲线。精度只能到秒级,而 ntpd 常态可保持在毫秒级。
- 不做交叉验证:只信任命令行里的那一个服务器。若该服务器时间错误(falseticker),会把错误时间照搬到本机,且无从察觉。
- 无漂移补偿:
ntpd会把本机晶振的固有漂移率学习并记录到driftfile,即使断网也能按漂移率补偿;ntpdate完全没有这个能力。 - 网络抖动敏感:单次采样受网络抖动影响大,
ntpd会用多次采样的统计滤波(filter)剔除坏样本。 - 失败无感知:cron 中重定向到
/dev/null后,同步失败也不会有任何告警,时间可能悄悄偏离很久。 - 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 之间的较大值)?
- 本地晶振精度很差,不该被当成高层可信源。设为 10 使其优先级低于任何真实的外部源(外部源通常是 stratum 2~3)。这样:外网可用时 ntpd 采用外网源;外网断了 ntpd 自动降级为使用本地时钟,客户端仍能同步上(stratum 11),不会因为服务端"未同步"而全网对时失败。
- 若不加
fudge,本地时钟默认 stratum 为 0 或 3,会与真实外部源竞争,可能导致 ntpd 采用不准的本地时钟。 - 不能设成 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)
├──► 数据库服务器组
├──► 应用服务器组
└──► 网络设备(交换机、路由器、防火墙)
要点:
- 内网至少部署 2 台 NTP 服务器,用
peer互为备份,避免单点。 - 内网服务器配置
server 127.127.1.0+fudge stratum 10作为兜底,外网断了全网仍能保持一致。 - 所有客户端只指向内网服务器,不要直连公网(省带宽、减少公网依赖、便于统一管控)。
- 客户端使用
ntpd/chronyd长驻服务而非 crontab + ntpdate。 - 把
ntpstat/chronyc tracking的结果接入 Zabbix 监控,对 offset 超阈值告警。详见 08-05-Zabbix监控.md
- 新机器上线的初始化脚本中,先
ntpdate -u <内网NTP>一次性拉正,再启动长驻服务。 - 网络设备也要配 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 绝对不能同时运行。
原因:
- 两者都要绑定 UDP 123 端口,后启动的会失败并报
Address already in use。 - 更危险的是,若一方以纯客户端模式运行(不绑定 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 服务器。
为什么虚拟机时间容易不准
- CPU 时钟中断被"偷走":虚拟机不独占 CPU,当宿主机 CPU 争抢激烈时,Guest 的定时器中断可能被延迟或丢失,导致 Guest 时钟变慢(这就是
%st/steal time 高时时间容易跑慢的原因)。 - 挂起/恢复(Suspend/Resume):虚拟机被挂起时 vCPU 完全停止,Guest 时钟不再前进。恢复后时间会落后于真实时间,落后量等于挂起时长。
- 快照恢复(Snapshot Revert):恢复到旧快照会把时间倒退回快照创建时刻,可能一次倒退几天甚至几个月。
- vMotion / 热迁移:迁移过程中会有短暂的时钟停顿。
- 虚拟机模板克隆:从旧模板克隆出的机器,时间是模板创建时的时间。
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(可能要等十几秒)
要点:
set-ntp true/false本质是在启停 chronyd(或 ntpd / systemd-timesyncd)。- NTP 处于启用状态时,
date -s与timedatectl set-time会被拒绝,报Failed to set time: Automatic time synchronization is enabled——必须先set-ntp false。 - 只改时区不会改变 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 就退出。三种化解方式:
- 先停服务再同步(最常用);
ntpdate -u ntp.aliyun.com:-u让 ntpdate 用非特权高位端口发包,可与 ntpd 共存;- 别用 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。
四、排错口诀
-
先看服务在不在跑,再看能不能通 123 端口,再看 stratum 与 offset。
systemctl status chronyd|ntpd→ss -unlp | grep :123→chronyc sources -v/ntpq -p。三步走完,八成的问题已经定位。 -
123 端口只有一个主人:ntpd、chronyd、ntpdate 三者不能同台共舞。 看到
the NTP socket is in use就先停服务;看到服务起不来就查是不是另一个时间服务已经占了端口。 -
UDP 不是 TCP:
telnet测不出 123 通不通,防火墙也别只放 TCP。 用ntpdate -q、chronyc sources、nmap -sU -p123验证,放行写--add-service=ntp或--add-port=123/udp。 -
stratum 16 等于没同步:客户端报
no server suitable先怀疑服务端还没准备好。 新建的 NTP 服务器要等约 5 分钟自身同步完成才能对外服务;fudge 127.127.1.0 stratum 10可作外网断开时的兜底。 -
时间对不上先分清"时刻错"还是"时区错"。
date -u与date对比:UTC 正确、本地时间偏整小时 → 时区问题(timedatectl set-timezone);UTC 本身就错 → 同步问题。改完时间别忘hwclock -w,否则重启前功尽弃。 -
xinetd 排错四问:disable 改了没、xinetd reload 了没、only_from/access_times 有没有把自己挡在外面、端口改了
/etc/services跟了没。 被 xinetd 拒绝的连接不会唤起子进程,所以要去 xinetd 的日志(log_type指定的文件或/var/log/messages)找FAIL: address/per_source_limit/time,而不是去翻服务自身的日志。 -
一层层剥:防火墙 → xinetd 的 only_from → TCP Wrappers(hosts.allow 先于 hosts.deny)→ 服务自身认证 → 权限与 SELinux。 多层控制是"与"的关系,任何一层拒绝都连不上;被 SELinux 拦住的特征是服务本身正常但操作被拒,用
ausearch -m avc -ts recent确认。
此处评论已关闭