Linux 时间同步实战与排障:chrony 配置模板 + 5 个真实时钟漂移故障
一、时钟漂移为什么是生产隐形杀手
我干了快十年的 Linux 运维,处理过几百起线上事故,其中最让我头大的不是磁盘写满、不是内存 OOM,反而是最不起眼的系统时间。它平时安安静静,一旦出事,症状五花八门,排查方向却常常被带偏。
先说三个我真实踩过的坑,你会发现它们八竿子打不着,根子却是同一个。
1.1 TLS 证书报 "certificate is not yet valid"
有一年公司对外提供 HTTPS 服务,客户反馈浏览器直接红屏,报错 ERR_CERT_DATE_INVALID。我登录服务器一看,证书明明在有效期内,签发时间和到期时间都正常。再 date 一看——这台服务器的系统时间比真实时间快了 5 分多钟。
证书的 notBefore 是真实时间往后推几个月的,服务器时间快 5 分钟,在客户端眼里,当前时间已经越过 notBefore 吗?不会。但反过来的场景更常见:服务器时间慢了。客户端用服务器时间做本地验证,或者反向代理做双向校验,时间一错位,就会在握手阶段报:
或者干脆:
Verify return code: 9 对应的就是 X509_V_ERR_NOT_YET_VALID,意思是「证书尚未生效」。这几乎 100% 意味着某台机器的时间比真实时间慢,导致证书看起来还没到生效时刻。
1.2 Kerberos 认证 "Clock skew too great"
另一个高频事故是内部系统用 Kerberos 做 SSO,某天突然批量报:
Kerberos 协议本身就是基于时间戳的。客户端向 KDC 请求票据时,会带上自己的时间戳,KDC 校验这个时间戳与自身时间之差是否超过默认 300 秒(5 分钟) 的容差。一旦客户端与域控时间差超过 5 分钟,认证直接拒绝。
有一次我排查一台老旧的日志服务器,发现它的时间比域控慢了整整 40 分钟。原因很讽刺——这台机器 NTP 服务压根没跑,靠 RTC 电池苟着,主板电池快没电了,一断电时间就回退。
1.3 日志时间错乱与监控数据错位
第三类症状最隐蔽:日志和监控数据时间对不上。应用日志时间戳由 A 机生成,采集端打上 B 机的时间,存储端又打上 C 机的时间,三台机器时间各差几秒到几分钟,导致:
- 故障排查时,日志顺序颠倒,同一请求的日志散落在错误的时间窗;
- Prometheus 里
rate()、increase()这类按时间窗计算的函数,在时间戳回跳时产生负数或锯齿; - 告警规则的
for: 5m持续判定逻辑,因时间错位被反复触发或漏触发; - 分布式数据库(etcd、Cassandra、TiKV)的线性一致性依赖时间戳,时钟大幅跳变会破坏数据一致性。
结论先行:时间同步不是「锦上添花」,而是所有依赖时间戳的协议、证书、分布式系统、监控告警的地基。地基歪了,上面全塌。
二、60 秒决策:chrony vs ntpd vs systemd-timesyncd
选型之前,先搞清楚手上这台机器在同步链里的角色:是只做客户端,还是要当 NTP 服务端给内网其他机器供时?
| 特性 | chrony | ntpd | systemd-timesyncd |
|---|---|---|---|
| 定位 | 完整 NTP 客户端+服务端 | 传统 NTP 客户端+服务端 | 轻量客户端,只能当客户端 |
| 服务端能力 | ✅ 可做服务端 | ✅ 可做服务端 | ❌ 无服务端功能 |
| 高延迟/高抖动网络 | ✅ 最稳,支持 NTP over Unix socket | ⚠️ 容易丢同步源 | ⚠️ 依赖网络质量 |
| 启动收敛速度 | 快,秒级可同步 | 慢,分钟级起步 | 较快 |
| 一步跳变(makestep) | ✅ 原生支持 | ⚠️ 需 -x 或配置 |
✅ 支持 |
| 硬件时间戳 | ✅ 支持 | ✅ 支持 | ❌ 不支持 |
| 虚拟化环境 | ✅ 最佳选择 | ⚠️ 在 VM 里容易漂 | ✅ 可用 |
| 配置文件 | /etc/chrony/chrony.conf | /etc/ntp.conf | /etc/systemd/timesyncd.conf |
| 控制工具 | chronyc | ntpq | timedatectl |
| 默认安装 | 现代发行版(RHEL8+/Debian10+) | 老旧系统 | systemd 自带 |
我的选型结论:
- 新装系统一律 chrony。RHEL/CentOS 8 之后、Debian 10 之后、Ubuntu 18.04+ 都默认它。它对高延迟、高抖动的网络(比如跨境、跨运营商)抗性最好,收敛快,还支持一步校正。
- 老系统(CentOS 6、RHEL 7 默认装 ntpd) 如果迁移成本高,可以保留 ntpd,但要记得它默认不跳变,只做 slewing(渐进微调),漂移严重时收敛极慢。
- 最小化容器、嵌入式、无状态节点 用 systemd-timesyncd 足够,因为它只当客户端。但千万别指望它给内网其他机器供时。
一句话:能上 chrony 就上 chrony,需要做 NTP 服务端必须 chrony 或 ntpd,systemd-timesyncd 永远只能当客户端。
三、前置:确认时区与 RTC
装任何 NTP 软件之前,先确认两件事:时区对不对、硬件时钟(RTC)模式对不对。时区错了,同步得再准,显示出来的时间也是错的。
3.1 timedatectl 三行解读
timedatectl 不带参数,输出三块核心信息,一次看全:
期望输出(以一台阿里云 ECS 为例):
Local time: 三 2026-10-28 10:15:32 CST
Universal time: 三 2026-10-28 02:15:32 UTC
RTC time: 三 2026-10-28 02:15:32
Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
逐行解读:
- Local time / Universal time:本地时间与 UTC 时间。两者相差正是时区偏移,这里 +0800。
- RTC time:硬件时钟(CMOS/主板电池)当前时间。正常情况应等于 Universal time(UTC)。
- Time zone:时区,必须是你业务所在时区。
- System clock synchronized: yes:系统时钟已被同步过。这是关键指标。
- NTP service: active:NTP 服务是否在跑。看到
inactive说明同步根本没启动。 - RTC in local TZ: no:RTC 是否使用本地时区。生产环境必须是 no,让 RTC 跑 UTC,否则跨时区、跨 DST 会乱。
3.2 硬件时钟 vs 系统时钟
Linux 有两个「时钟」:
- 系统时钟(System Clock / kernel clock):内核维护的软件时钟,精度高,靠 NTP 持续校准,关机就没了。
- 硬件时钟(RTC / CMOS clock):主板电池供电的时钟,关机后靠电池走时,精度差,开机时被读入作为系统时钟的初始值。
开机流程是:BIOS/UEFI 读 RTC → 内核据此初始化系统时钟 → NTP 接手后持续校准系统时钟 → 定期把校准后的时间写回 RTC(chrony 的 rtcsync 干这事)。
3.3 设置时区与 UTC
生产环境推荐时区用 Asia/Shanghai,RTC 用 UTC。一条命令设置时区:
让 RTC 跑 UTC(生产必须这样,别学一些老教程把 RTC 设成本地时区):
set-local-rtc 0 表示 RTC 使用 UTC,timedatectl 里会显示 RTC in local TZ: no。
⚠️ 注意:设置时区后,
/etc/localtime会被指向/usr/share/zoneinfo/Asia/Shanghai。如果发现手动改过/etc/localtime(比如直接cp了某个 zoneinfo),建议用timedatectl规范操作,避免时区数据库和系统时区不一致。
时区数据校验,看本地时间是否与预期一致:
期望输出:
四、chrony 配置实战:完整可复制模板
4.1 安装
RHEL/CentOS 系:
Debian/Ubuntu 系:
确认版本:
期望输出:
4.2 完整 chrony.conf 模板
以下是生产环境可直接复制的 /etc/chrony/chrony.conf(RHEL/Debian 路径略有差异,Debian 是 /etc/chrony/chrony.conf,老版本在 /etc/chrony.conf,以实际为准)。我逐段注释,每项都是实测参数。
# ============================================================
# 生产环境 chrony 配置模板
# 适用:内网 NTP 服务端 + 客户端,CentOS/RHEL 7+/Debian 10+
# 作者:一线运维,实测于 2026-10
# ============================================================
# --- 上游时间源:国内主源 + 备用池 ---
server ntp.aliyun.com iburst
server cn.pool.ntp.org iburst
# 可选备用,避免单点
server ntp.tencentcloud.com iburst
server ntp1.aliyun.com iburst
# --- 漂移文件:chrony 据此估算本地时钟的固有漂移率,自动生成,勿手动改 ---
driftfile /var/lib/chrony/drift
# --- 步进校正策略:前 3 次同步若偏移 > 1 秒则一步跳变,之后渐进 slewing ---
# 关键参数,生产必配,详见第七章
makestep 1.0 3
# --- RTC 同步:chrony 定期把系统时间写回硬件时钟,防关机丢时间 ---
rtcsync
# --- 日志目录:chrony 的统计/测量日志(chronyc tracking 用)---
logdir /var/log/chrony
# --- 允许内网网段作为客户端:按实际网段修改,禁止 0.0.0.0/0 ---
# 只允许内网子网同步,不暴露公网
allow 192.168.0.0/24
allow 10.0.0.0/8
# --- 拒绝其他所有来源(默认拒绝,配合上面 allow 白名单)---
deny all
# --- 当无法到达所有上游源时,仍以本地 stratum 10 对外提供时间 ---
# 防止上游全挂后内网完全失去同步能力(仅内网服务端需要)
local stratum 10
# --- 网络绑定:只监听内网接口,最小化暴露面 ---
# 若有多个网卡,建议显式绑定内网 IP,例如 bindaddress 192.168.1.10
# bindaddress 192.168.1.10
# --- 客户端连接数上限,防资源滥用 ---
maxclients 256
# --- 客户端请求速率限制(单位:每秒),防 NTP 放大攻击 ---
ratelimit interval 4 burst 32
# --- 仅监听 UDP 123(NTP 默认端口),无需改 ---
4.3 关键指令逐条说明
| 指令 | 作用 | 生产建议 |
|---|---|---|
server <ip> iburst |
指定上游源,iburst 快速突发采样加速首次同步 |
2-4 个国内源 |
driftfile |
记录时钟漂移率,重启后能快速估算偏移 | 必配 |
makestep 1.0 3 |
前 3 次同步偏移 >1s 则跳变,之后 slewing | 生产必配 |
rtcsync |
定期写回硬件时钟 | 必配,防关机丢时 |
allow <网段> |
允许哪些客户端来同步 | 只允许内网 |
deny all |
默认拒绝所有 | 配合 allow 用 |
local stratum 10 |
上游全挂时仍提供本地时间 | 内网服务端配 |
bindaddress |
绑定监听地址 | 多网卡建议显式 |
maxclients |
并发客户端上限 | 防资源耗尽 |
ratelimit |
请求速率限制 | 防 DDoS/放大攻击 |
4.4 应用配置
改完配置,语法检查 + 重启:
chronyd -t是测试模式,会解析配置并打印错误(若有)而不真正启动,适合改配置后先验证。
4.5 放行防火墙
NTP 走 UDP 123,内网服务端需要放行。firewalld:
iptables(如果用的 iptables):
五、国内网络下的 NTP 源选择
这是国内生产环境最容易被忽略的一环。很多人习惯性抄国外教程写 server 0.pool.ntp.org,结果同步质量极差。我的经验是:在国内机房,选源第一优先级是「地理与网络路径近」,而不是「源本身准不准」。
5.1 推荐源清单
| 源 | 类型 | 延迟实测(华东 BGP 机房) | 评价 |
|---|---|---|---|
ntp.aliyun.com |
阿里云公共 NTP | 2-15 ms | 首选,国内多节点,准且快 |
ntp1.aliyun.com |
阿里云备用 | 2-15 ms | 常用备用 |
cn.pool.ntp.org |
中国 NTP 池 | 5-40 ms | 推荐,国内节点池 |
ntp.tencentcloud.com |
腾讯云公共 NTP | 3-20 ms | 推荐,腾讯内网友好 |
ntp.tencent.com |
腾讯 | 5-30 ms | 可用 |
ntp.aliyun.com(主)+ cn.pool.ntp.org(备) |
组合 | — | 我的默认组合 |
time.windows.com |
微软 | 30-80 ms | 一般,仅 Windows 用 |
time.nist.gov |
美国 NIST | 150-300+ ms,抖动大 | 不推荐,跨境 |
pool.ntp.org |
全球池 | 100-200 ms,抖动大 | 不推荐,跨境 |
time.google.com |
跨境被墙/高延迟 | 不推荐 |
5.2 为什么坚决不用 time.nist.gov / pool.ntp.org
我在一台华东机房服务器上实测过,对比国内源和国外源:
用 pool.ntp.org 时,典型输出:
===============================================================================
.-- Source remote - | Stratum ... Reach Last Rx | Offset Delay Disp
===============================================================================
^- 162.159.200.1 | 1 | 12% 20m | +38ms 185ms 12ms
^* 162.159.200.123 | 1 | 23% 15m | -52ms 210ms 20ms
Reach(可达性)只有 12%、23%,说明跨境包大量丢,Delay(延迟)180-210ms,Offset(偏移)几十毫秒来回跳。这种源对 chrony 来说是灾难——它需要稳定的低抖动输入来估算频率误差,跨境源抖动一上来,同步精度根本没法保证。
换 ntp.aliyun.com 后:
===============================================================================
.-- Source remote - | Stratum ... Reach Last Rx | Offset Delay Disp
===============================================================================
^* 120.25.115.20 | 2 | 100% 1s | +0.2ms 6ms 0.3ms
^- 182.92.12.11 | 2 | 100% 1s | -0.4ms 9ms 0.5ms
Reach 100%,Delay 6-9ms,Offset 0.2ms 级别。这才是国内该有的同步质量。
经验法则:Delay 超过 50ms 或 Reach 经常低于 50%,这个源就该换掉。国内机房用国内源,延迟个位数毫秒才是正常的。
5.3 源选择原则总结
- 优先就近:华东选阿里云/腾讯云华东节点,华北选华北节点,优先内网或同运营商。
- 至少配 2 个源:一个主源 + 一个备用,避免单点。别只配一个。
- 不同运营商各来一个:跨运营商互访质量差,最好选同运营商或 BGP 任播源(阿里云、腾讯云都是 BGP)。
- 内网若有更高级别的 NTP 服务器,直接指向它,别让所有服务器都打公网。
- 不要用 IP 直接写 server,除非有特殊原因;域名解析失败时 chrony 会重试,问题不大。
六、验证与监控:确认时间真的同步了
配完不是终点,必须验证。很多「配了不生效」的案例,都卡在验证这步。
6.1 chronyc sources -v:看上游源状态
这是最核心的验证命令。关注 Reach(可达性,100% 最好)、Offset(偏移,越小越好)、Delay(延迟)。
6.2 chronyc tracking:看同步详情
期望输出(同步正常时):
Reference ID : C633DC14 (120.51.220.20)
Stratum : 2
Ref time (UTC) : Wed Oct 28 02:15:32 2026
System time : 0.000000123 seconds fast of NTP time
Last offset : +0.000123456 seconds
RMS offset : 0.000234567 seconds
Frequency : 4.567 ppm
Residual freq : +0.012 ppm
Skew : 0.234 ppm
Root delay : 0.005678 seconds
Root dispersion : 0.001234 seconds
Update interval : 64.2 seconds
Leap status : Normal
关键字段解读:
- Stratum 2:表示这台机器离权威时间源还有 2 级,正常。stratum 越大,时间越「间接」。
- System time ... fast/slow of NTP time:当前系统时钟相对 NTP 时间的偏差。这是最关心的指标,正常应在微秒到毫秒级。
- Last offset / RMS offset:最近一次与平均偏移,越小越好。
- Frequency ... ppm:本地时钟漂移率,单位 ppm(百万分之一)。几 ppm 正常,若长期漂到几百 ppm,说明主板晶振老化或虚拟化时钟有问题。
- Leap status: Normal:闰秒状态,Normal 表示正常。
6.3 chronyc makestep:立即强制同步
想立刻做一次同步(不等下次轮询):
期望输出:
强制做一次跳变校正后,再 chronyc tracking 确认偏移归零。
6.4 用 chronyc 手动加/删源
临时测试某个源能不能用(不重启服务,即时生效):
6.5 timedatectl 二次确认
确认 System clock synchronized: yes 且 NTP service: active。如果 System clock synchronized: no,说明 chronyd 还没完成首次同步。
6.6 ntpd 的验证(老系统)
如果还在用 ntpd:
期望输出:
remote refid st t when poll reach delay offset jitter
==============================================================================
*120.25.115.20 120.25.115.20 2 u 62 128 377 6.234 0.123 0.456
+182.92.12.11 182.92.12.11 2 u 45 128 377 9.876 -0.234 0.321
* 表示当前选中的同步源,reach 377(八进制,377=0xFF,全可达),offset 越小越好。
6.7 时间偏移监控:接入 Prometheus
生产环境光靠人去看不够,要把时间偏移打进监控。两个思路:
思路一:用 node_exporter 的 time 指标。 node_exporter 自带两个时间相关指标:
node_time_seconds 是系统时钟的 Unix 时间戳,node_time_seconds_seconds(较新版本为 node_time_clock_seconds)是同一时刻读取的 clock_gettime。两者差值即偏移。但更直接的是加一个自定义 exporter。
思路二:写个简单脚本,暴露 offset 到 Prometheus。 用 chronyc 拿偏移,输出为 Prometheus 格式:
cat > /usr/local/bin/time_offset_exporter.sh <<'EOF'
#!/bin/bash
# 从 chronyc tracking 提取 System time 偏移,输出 Prometheus 指标
offset=$(chronyc tracking | awk -F'[ :]' '/System time/{print $4}')
echo "# HELP time_offset_seconds System clock offset from NTP time in seconds"
echo "# TYPE time_offset_seconds gauge"
echo "time_offset_seconds $offset"
EOF
chmod +x /usr/local/bin/time_offset_exporter.sh
配合 node_exporter 的 --collector.textfile.directory 定时执行,或直接用 chrony_exporter(社区有现成的 Prometheus exporter,直接部署即可)。
我实际用的告警规则(Prometheus):
groups:
- name: time-sync
rules:
- alert: SystemClockOffsetHigh
expr: abs(time_offset_seconds) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "系统时钟偏移超 100ms: {{ $labels.instance }}"
- alert: SystemClockOffsetCritical
expr: abs(time_offset_seconds) > 1
for: 5m
labels:
severity: critical
annotations:
summary: "系统时钟偏移超 1s: {{ $labels.instance }}"
0.1s(100ms)告警 warning,1s 告警 critical,这是我多年运维总结的阈值。因为一旦超过 1 秒,TLS/Kerberos/分布式一致性的风险就开始显现了。
七、时钟跳变 vs 渐进调整:makestep 的核心语义
这是很多运维没吃透、又最容易踩雷的点。理解它,你才知道为什么生产环境要避免大步跳变。
7.1 两种校正方式
时间同步校正系统时钟,有两种方式:
- 跳变(Step):直接把时钟「拨」到正确值。一瞬间时间跳了一大截,比如快 5 分钟就立刻回拨 5 分钟。
- 渐进调整(Slewing / slew):通过微调内核时钟频率,让时间慢慢逼近真实值,比如每秒调整一点点,几分钟到几十分钟内平滑收敛。时间永远向前走,只是快慢有微小变化。
7.2 为什么生产要避免大步跳变
跳变带来的问题,全是生产级的:
- K8s 证书:etcd、API Server、kubelet 之间基于时间戳签发/校验证书。时间向后跳(回拨)会让「刚签的证书」看起来还没生效,或「已签的证书」看起来已过期,引发集群证书风暴。
- 监控告警风暴:时间回拨会让 Prometheus 的
rate()、increase()在回拨窗口内产生负数、NaN 或突变,触发大量误告警。 - 数据库复制:MySQL/PostgreSQL 的复制、etcd 的 raft 心跳、TiKV 的 TSO 都依赖时间单调性。时间回拨会破坏主从复制判定,甚至引发复制中断。
- 日志排序:时间回拨会让新日志的时间戳小于旧日志,ELK/ClickHouse 里排序错乱,排查困难。
所以核心原则是:已经运行稳定、偏移小的系统,只用渐进 slewing,绝不做大跳变;只有刚开机、偏移巨大、或从未同步过的系统,才允许做跳变。
7.3 makestep 1.0 3 的精确语义
makestep <threshold> <limit>:
- threshold:跳变阈值(秒)。偏移超过这个值才跳变,小于等于这个值用 slewing。
- limit:在前多少次同步更新中允许跳变。超过这个次数后,即使偏移很大也只 slewing,不再跳变。
所以 makestep 1.0 3 的完整含义是:
在 chronyd 启动后的前 3 次时钟更新中,若偏移超过 1 秒,就一步跳变校正;第 3 次之后,无论偏移多大,都只做渐进 slewing,绝不跳变。
这正好满足生产需求:开机时机器时间可能乱(RTC 没电、VM 快照恢复),允许前几次跳变快速校准;运行稳定后(已过 3 次更新),哪怕漂了 2 秒也慢慢 slewing 回去,不引发跳变风暴。
反例:makestep 0 -1(或很多教程里的 makestep 1.0 -1)表示永远允许跳变。我强烈不建议在生产这么配,因为一旦某次网络异常后偏移累积到几秒,chrony 会直接跳变,瞬间回拨几秒,上面那堆灾难全来了。
7.4 ntpd 的对照
老 ntpd 默认不跳变,只 slewing。这意味着如果偏移很大(比如 30 分钟),它会以极慢速度(几 ppm 调整)收敛,可能要好几个小时。所以老系统若发现时间偏得离谱,要么等它慢慢收敛,要么手动:
(ntpdate 在新系统已废弃,老系统可用。)
八、虚拟化与容器:两个时间同步大坑
我在这上面栽过不止一次。虚拟化和容器的时钟行为跟物理机完全不一样,很多「时间越同步越不准」的诡异案例都出自这里。
8.1 VM 里的时钟漂移:KVM 与 VMware
虚拟机的时间有两个来源在打架:
- 虚拟化平台提供的虚拟时钟(KVM 的
kvm-clock、VMware 的虚拟 RTC); - VM 内部运行的 NTP 服务(chrony/ntpd)。
问题在于:如果虚拟化平台本身的时间不准,或者宿主与 VM 时间不同步,VM 里的 NTP 会一直跟一个「不准的虚拟时钟」较劲,表现就是 offset 老是降不下来,或者同步了又飘走。
KVM 场景,确认是否用了 kvm-clock(这是 KVM 里精度最好的时钟源):
期望输出:
如果输出是 tsc 或其他,说明没启用 kvm-clock,虚拟时钟精度差,建议在 KVM 层面启用(-machine accel=kvm + 开启 kvm-clock 通常默认启用)。
我的经验:
- VM 内跑 chrony 完全正常且必要,前提是虚拟化宿主时间准。VM 的虚拟时钟漂移率通常比物理机大,反而更需要 chrony 持续校准。
- 别让 VM 的 chrony 去跟宿主的某个「不准的虚拟 RTC」同步,要让它跟真实网络 NTP 源(或宿主里准的 NTP 服务)同步。
- VMware:如果 VM 漂移严重,可在 VM 里装
open-vm-tools,并用 chrony 配合,别同时依赖 VMware 的「同步客户机时间到主机」选项——二选一,我推荐只用 VM 内 chrony。 - 快照恢复后时间跳变:VM 从快照恢复,RTC 和系统时间可能回退到快照时刻。这时候 makestep 的前 3 次跳变机制就能派上用场——恢复后 chrony 检测到偏移巨大,直接跳变到正确时间。
8.2 容器内的时间:共享宿主,改不动
容器共享宿主机内核时钟,容器里没有独立的时钟。这意味着:
- 容器内执行
date -s会失败(权限拒绝)或无效,因为改的是宿主时钟,且通常被 seccomp/capability 拦住: - 容器内不需要也不能跑自己的 chronyd/ntpd——它没有权限调整时钟,跑了也白跑。容器的时间跟随宿主机。
- 所以容器的正确做法是:保证宿主机时间准,容器时间自然准。
- 有个特例:privileged 容器 或
--cap-add SYS_TIME的容器可以改时钟,但这等于改宿主时钟,极其危险,生产禁用。 - Docker 的
--clock-...相关选项(若有)也不会给容器独立时钟。
结论:容器时间 = 宿主机时间。要管容器时间,先管宿主机。别在容器里折腾 chrony。
九、NTP 安全:最小化暴露面
NTP 服务(UDP 123)历史上出过多次放大攻击漏洞(如 2014 年的 monlist 反射放大,单包放大几百倍),所以生产环境必须收敛暴露面。
9.1 只监听内网
多网卡机器,用 bindaddress 只绑定内网 IP,别监听 0.0.0.0:
9.2 白名单 allow / 默认 deny
9.3 请求速率限制
ratelimit 防 NTP 放大/洪泛:
含义:每 4 秒最多处理一个客户端的一批(最多 32 个)请求,超出的丢弃。
9.4 认证:symmetric key(可选)
如果内网有不可信网段,或想防中间人篡改 NTP 包,可启用对称密钥认证。生成密钥:
配置(服务端和客户端都要配):
chronyc keygen 会生成形如 1 SHA1 hexstring 的密钥行,key 1 表示用编号 1 的密钥。
注意:NTP 对称密钥认证不是加密,是「认证」,用来确认包来源可信,防篡改。内网环境如果网络可信,通常不用配;跨不可信网络建议配。
9.5 禁用 monlist(chrony 默认无此漏洞)
老 ntpd 的 monlist 查询命令曾导致反射放大,chrony 默认不开放这个,无需额外处理。但别在公网暴露 UDP 123——防火墙层面只允许内网来源访问 NTP 端口:
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=192.168.0.0/24 port port=123 protocol=udp accept'
firewall-cmd --permanent --remove-service=ntp # 移除全放行的 ntp 服务,改用更精确的规则
firewall-cmd --reload
十、5 个真实故障排查案例
以下案例都来自我或我带的团队在真实生产环境遇到并解决的,细节做了脱敏,但故障现象、排查路径、根因完全真实。
案例 A:TLS 报 "certificate is not yet valid"
现象:某对外 HTTPS 站点突然全部报 certificate is not yet valid,浏览器红屏,curl 也报错:
curl -v https://api.example.com
# 输出末尾:
# SSL: certificate verification failed (result: 9, error: certificate is not yet valid)
排查:
- 先看证书本身:
openssl x509 -in cert.pem -noout -dates,确认 notBefore/notAfter 都在合理范围。证书没问题。 - 看服务器时间:
date,发现系统时间比真实时间快了 5 分钟。 - 看是谁让它快了的:
timedatectl,NTP service: inactive——这台机器的 chronyd 根本没跑。
根因:这台服务器是刚克隆出来的 VM,克隆时把「源 VM 的时间」带过来了,而源 VM 时间偏快;更关键的是新 VM 没启用 NTP 服务,导致时间一直快着。服务器时间快 5 分钟,对客户端而言,请求到达时「服务器本地时间」已越过证书的某些校验窗口,或客户端把服务器时间当作参考,导致认为证书「尚未生效」。
解决:
跳变校正后,时间归位,HTTPS 恢复正常。
教训:克隆 VM 后第一件事就是确认 NTP 服务在跑,并强制 makestep 一次。
案例 B:Kerberos 认证失败 "Clock skew too great"
现象:内网一套基于 Kerberos 的 BI 系统,某天早上用户批量无法登录,日志大量报:
排查:
- 客户端和 KDC 各看时间:
date。发现一台客户端服务器(日志服务器)时间比 KDC 慢了 40 分钟。 - 看这台机器的 NTP:
chronyc tracking,报Unable to obtain synchronization from source——chrony 根本没同步上。 - 查原因:
chronyc sources -v,所有源Reach 0%;ping ntp.aliyun.com不通;查路由,这台机器被防火墙拦了 UDP 123 出方向。
根因:防火墙策略变更时,误拦了这台日志服务器到 NTP 源的 UDP 123 出站流量。它无法同步,靠 RTC 漂着,一星期下来慢了 40 分钟,超过 Kerberos 300 秒容差,认证全挂。
解决:放行防火墙 UDP 123 出站:
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 destination address=120.25.115.20 port port=123 protocol=udp accept'
firewall-cmd --reload
chronyc makestep
教训:防火墙变更务必检查 NTP 出站。Kerberos 容差只有 5 分钟,比 TLS 更敏感。
案例 C:K8s 集群证书签名时间异常
现象:K8s 集群某节点突然被判定 NotReady,kubectl get nodes 显示该节点 Ready <unknown>,kubelet 日志报证书相关错误:
或 etcd 报:
排查:
- 看 etcd/API Server 所在机器时间:
date。发现 etcd 节点时间比 API Server 慢了 3 秒,且漂移在持续增大。 chronyc tracking:etcd 节点Frequency高达 +450 ppm,严重偏离正常值(几 ppm),说明这个 VM 的虚拟时钟漂移极大。cat /sys/devices/system/clocksource/clocksource0/current_clocksource:输出tsc,没启用 kvm-clock。
根因:KVM 里没启用 kvm-clock,虚拟时钟精度差;etcd 节点上的 chrony 虽然跑了,但持续 slewing 追不上漂移,时间偏移累积到 3 秒。etcd 集群基于时间戳做 leader 选举和 raft 心跳,时间差一拉大,健康检查判定混乱,节点被标记 NotReady。
解决:
- 宿主机层面启用 kvm-clock(修改 VM 配置,
-machine accel=kvm并启用 kvm-clock)。 - 在 etcd 节点上强制跳变校准:
chronyc makestep。 - 检查 kubelet 证书重新签发。
教训:K8s 集群节点务必保证时间高度一致,偏差超过 1 秒就可能引发 etcd 选举异常。虚拟化环境务必启用虚拟时钟(kvm-clock)。
案例 D:监控数据错位
现象:Prometheus 告警规则 for: 5m 经常误触发,或者明明该告警了却没告;Grafana 图表上的时间轴和日志时间对不上;rate() 曲线出现突然的负值。
排查:
- 对比采集端、Prometheus 存储端、节点导出器所在机器时间:分别在各自机器
date +%s。 - 发现 采集端(node_exporter 所在机器)比 Prometheus 快了 1.2 秒。
- 查
node_time_seconds与time()(Prometheus 查询时)之差:query: time() - node_time_seconds,结果持续 ~1.2。
根因:node_exporter 所在机器 chrony 未正确配置,只配了一个 server pool.ntp.org,跨境源抖动大,chrony 判定源不可靠,一直没锁上,机器时间缓慢漂移了 1.2 秒。虽然 1.2 秒看似不大,但对 for: 5m 这种时间窗判定和 rate 计算已经足够产生误告警和图表错位。
解决:
# 换国内源
sed -i 's/^server pool.ntp.org/server ntp.aliyun.com iburst/' /etc/chrony/chrony.conf
systemctl restart chronyd
chronyc makestep
教训:监控体系的每台机器时间都必须准,1 秒的偏差就会让告警规则失真。node_exporter 的 time_seconds 与 time_offset_seconds 要进告警。
案例 E:systemd-timesyncd 不工作
现象:一台 Ubuntu 22.04 最小化服务器,timedatectl 显示 NTP service: inactive,时间一直不准,且没有安装 chrony。
排查:
timedatectl status:NTP service: inactive,System clock synchronized: no。systemctl status systemd-timesyncd:inactive (dead)。- 看配置:
cat /etc/systemd/timesyncd.conf,发现#NTP=和#FallbackNTP=都被注释掉了——用的是默认值。默认 FallbackNTP 是0.debian.pool.ntp.org等国外池,在这台机器上 DNS 解析到国外,跨境不可达,导致 timesyncd 一直连不上源,服务反复失败后停掉。 - 手动测:
systemd-resolve --status | grep -i ntp或ntpdate -q ntp.aliyun.com测源可达性。
根因:systemd-timesyncd 默认用 Debian/Ubuntu 的 pool.ntp.org(国外),国内网络不可达;且 NTP= 未显式配置,FallbackNTP 也是国外池。服务连不上源,就持续失败。
解决:显式配国内源。
cat > /etc/systemd/timesyncd.conf <<'EOF'
[Time]
NTP=ntp.aliyun.com cn.pool.ntp.org
FallbackNTP=ntp.tencentcloud.com ntp1.aliyun.com
EOF
systemctl restart systemd-timesyncd
timedatectl set-ntp true
timedatectl
教训:systemd-timesyncd 用之前必须显式配 NTP=,别依赖默认的国外 FallbackNTP。国内环境一定要指向国内源。
十一、排障决策树
把上面所有案例的排查路径收敛成一棵决策树,遇到时间问题照着走:
开始:现象是 TLS/Kerberos/监控/日志时间错乱
│
├─ 1. timedatectl
│ ├─ NTP service: inactive → 看步骤 2(服务没跑)
│ ├─ NTP service: active 但 System clock synchronized: no → 看步骤 3
│ ├─ System clock synchronized: yes → 看步骤 4
│ └─ RTC in local TZ: yes → 执行 timedatectl set-local-rtc 0(改回 UTC)
│
├─ 2. NTP 服务没跑
│ ├─ chrony → systemctl enable --now chronyd
│ ├─ ntpd → systemctl enable --now ntpd
│ └─ timesyncd → systemctl enable --now systemd-timesyncd
│
├─ 3. 服务在跑但没同步上
│ ├─ chronyc sources -v
│ │ ├─ 所有源 Reach 0% → 源不可达,看步骤 5
│ │ ├─ Reach 有值但 Offset 大 → 源质量差,换源(步骤 6)
│ │ └─ 有源被选中(*)但 Offset 仍大 → 检查 makestep/是否在 slewing
│ └─ 若是 ntpd → ntpq -p 同样分析
│
├─ 4. 已同步但 offset 还是大
│ ├─ chronyc tracking → 看 Frequency ppm 是否异常大(>100)
│ ├─ 虚拟化环境 → 检查 /sys/devices/system/clocksource/.../current_clocksource
│ │ └─ 不是 kvm-clock → 启用虚拟时钟(KVM)
│ └─ 硬件时钟坏 → 看步骤 7
│
├─ 5. 源不可达
│ ├─ ping 源域名 → 不通:DNS 解析问题,检查 /etc/resolv.conf
│ ├─ ping 源 IP → 通:域名正常,是 UDP 123 被拦 → 检查防火墙/安全组
│ ├─ nc -uvz 源IP 123 → 不通:UDP 123 被拦
│ └─ 出站被拦 → 防火墙放行 UDP 123 出站
│
├─ 6. 源质量差(高 Delay/低 Reach)
│ ├─ 换国内源 ntp.aliyun.com / cn.pool.ntp.org
│ └─ 重启 chronyd + chronyc makestep
│
└─ 7. 硬件时钟坏(RTC 电池没电/主板故障)
├─ 现象:每次断电重启时间都回到 2000 年或乱跳
├─ 检查 RTC 电池(纽扣电池电压)
├─ 更换主板电池
└─ 重启后用 chronyc makestep 立即校准
十二、15 条命令速查 Cheat Sheet
| # | 命令 | 用途 |
|---|---|---|
| 1 | timedatectl |
查看时区、RTC、NTP 状态三合一 |
| 2 | timedatectl set-timezone Asia/Shanghai |
设置时区 |
| 3 | timedatectl set-local-rtc 0 |
RTC 用 UTC |
| 4 | timedatectl set-ntp true |
启用 NTP 服务 |
| 5 | chronyc sources -v |
查看上游源状态(核心) |
| 6 | chronyc tracking |
查看同步详情、偏移、漂移率 |
| 7 | chronyc makestep |
强制一步跳变校正 |
| 8 | chronyc add server ntp.aliyun.com iburst |
临时加源 |
| 9 | chronyc delete server 0.pool.ntp.org |
临时删源 |
| 10 | ntpq -p |
ntpd 查看源(老系统) |
| 11 | systemctl status chronyd |
查看 chrony 服务状态 |
| 12 | chronyd -t /etc/chrony/chrony.conf |
配置语法测试 |
| 13 | date +%s |
快速看当前时间戳 |
| 14 | hwclock --show |
查看硬件时钟(RTC) |
| 15 | timedatectl show -p Timezone --value |
只输时区名 |
FAQ
Q1:时钟漂移会造成哪些生产故障?
A:最常见三类。第一,TLS 证书报 certificate is not yet valid(服务器时间偏快或偏慢导致证书校验窗口错位);第二,Kerberos 认证报 Clock skew too great,因为 Kerberos 容差只有 300 秒(5 分钟),超了就拒绝;第三,监控与日志时间错位,导致告警规则失真、rate() 计算异常、日志排序错乱。此外分布式系统(etcd、数据库复制)依赖时间戳,时钟跳变会破坏一致性。
Q2:chrony 和 ntpd 怎么选?
A:新系统(RHEL 8+/Debian 10+)首选 chrony。它对高延迟、高抖动网络更稳,收敛快,启动秒级可同步,原生支持 makestep 一步校正,还能当 NTP 服务端。ntpd 是传统方案,启动慢、收敛慢,适合老系统。systemd-timesyncd 最轻量,但只能当客户端,不能当服务端,适合最小化容器/无状态节点。
Q3:为什么不用 time.nist.gov 或 pool.ntp.org?
A:这些是国外源,国内机房访问要跨境,实测延迟 150-300ms 且抖动大,chrony 的 Reach 常低于 50%,同步质量差。国内生产环境应优先 ntp.aliyun.com、cn.pool.ntp.org、ntp.tencentcloud.com,延迟个位数毫秒,Reach 100%,准确度高得多。
Q4:makestep 1.0 3 是什么意思?
A:makestep <threshold> <limit>。含义是:在 chronyd 启动后的前 3 次时钟更新中,若偏移超过 1 秒,就一步跳变校正;第 3 次之后,即使偏移再大也只做渐进 slewing,绝不跳变。这样开机时能快速校准(允许跳变),运行稳定后避免大步跳变引发告警风暴、证书问题、数据库复制异常。
Q5:容器里能改时间吗?需要跑 chrony 吗?
A:容器共享宿主机内核时钟,没有独立时钟。容器内 date -s 会报 Operation not permitted(除非 privileged 或加 --cap-add SYS_TIME,那会改宿主时钟,极其危险,生产禁用)。容器不需要也不能跑 chrony,时间跟随宿主机。所以正确做法是保证宿主机时间准,容器时间自然准。
Q6:虚拟机时间老是不准怎么办?
A:先确认虚拟时钟。KVM 下 cat /sys/devices/system/clocksource/clocksource0/current_clocksource 应为 kvm-clock,不是 tsc;若不是,在 VM 配置启用 kvm-clock。然后 VM 内跑 chrony 对国内源同步,别依赖虚拟化平台的「同步客户机时间」选项(二选一)。VM 快照恢复后时间会回退,需 chronyc makestep 强制校准。
Q7:NTP 服务怎么防止被滥用/攻击?
A:三招。第一,最小化监听,多网卡用 bindaddress 只绑内网 IP,只监听 UDP 123;第二,白名单,allow 192.168.0.0/24 + deny all,只允许内网子网同步;第三,速率限制,ratelimit interval 4 burst 32 防 NTP 放大攻击。公网防火墙层面只放行内网来源的 UDP 123,绝不在公网暴露 NTP。
Q8:怎么监控时间偏移?
A:用 chronyc tracking 看 System time 字段的偏移,用 chrony_exporter 或自定义 exporter 把它暴露成 Prometheus 指标。我建议告警阈值:偏移超 100ms(0.1s) 告 warning,超 1 秒 告 critical。同时监控 Frequency(漂移率,正常几 ppm,异常超 100 ppm 提示硬件/虚拟时钟问题)。