适用场景
适用于运行在 Linux 虚拟机、物理机或容器节点上的 Web 服务。业务表现为同一版本在部分机器上间歇出现登录失效、JWT token is not active / token expired、HTTPS 握手失败或调用云 API 返回签名过期;重试、重启应用有时短暂恢复,但问题会再次出现。
本文以“应用节点时钟慢了 90 秒”为例,给出不依赖猜测的排查和修复流程。时间问题的风险不只在日志顺序:它会直接破坏令牌有效期、TLS 证书有效期和带时间戳的请求签名。
现象与影响范围
常见告警或日志包括:
JWT validation failed: token is not active
x509: certificate has expired or is not yet valid
RequestTimeTooSkewed: the difference between the request time and the current time is too large
先确认是否具有“按节点聚集”的特征。若同一用户请求落到 A 节点成功、落到 B 节点失败,且 B 的日志时间比监控、数据库或其他节点明显靠前或滞后,优先排查时钟,而不是先扩大 JWT 容忍窗口。
排查步骤
1. 同时采集本机时间、时区和同步状态
在故障节点执行:
date --iso-8601=ns
timedatectl status
chronyc tracking
chronyc sources -v
重点阅读以下字段:
System clock synchronized: yes表示 systemd 已观察到同步状态;no需要继续定位。Leap status: Normal表示 chrony 认为时间源可用;Not synchronised不能只靠重启应用解决。Last offset是最近一次测得偏差,System time是当前估计偏差。生产环境通常应控制在毫秒到几十毫秒级,具体阈值须与业务签名和监控精度匹配。chronyc sources -v中以^*标记的来源为当前选中的时间源;只有^?、^x或来源数量不足时,应检查网络、DNS、NTP 防火墙策略和上游服务。
不要把 date 与手机时间对比作为唯一证据。应在同一时刻从至少两台健康节点和一个可信时间源交叉比对,并保留命令输出与 UTC 时间。
2. 判断是持续漂移、启动未同步还是虚拟化问题
journalctl -u chronyd --since '2 hours ago' --no-pager
systemctl status chronyd
grep -E '^(server|pool|makestep|rtcsync)' /etc/chrony.conf /etc/chrony.d/*.conf 2>/dev/null
可按证据分类:
- 服务刚启动就接收流量,且
chronyc tracking未同步:启动顺序或就绪检查有问题。 - 偏差以稳定速度持续扩大:宿主机时钟、虚拟机时间同步或硬件时钟质量异常。
- 偏差突然跳变:检查虚拟机迁移、暂停恢复、快照回滚和手工执行
date -s的审计记录。 - 只有部分时段失步:检查 UDP 123 是否被防火墙拦截、NTP 域名解析是否不稳定,以及时间源是否被错误限速。
容器内通常使用宿主机时钟。不要在普通业务容器中运行第二个 NTP 客户端;应修复节点时钟,再滚动重建受影响 Pod。
3. 量化与健康节点的差值
若允许从故障节点访问一台受管健康节点,可临时执行:
ssh ops@healthy-node 'date -u +%s.%N'
date -u +%s.%N
两条命令之间包含网络往返时间,因此只能用于确认明显偏差,不能用于毫秒级定标。精确判断以 chrony 的多源测量和监控中的 node_timex_offset_seconds 为准。
修复方案
1. 让 chrony 使用多个受控时间源
以下是 /etc/chrony.conf 的示例;时间源地址应替换为组织批准的 NTP 服务:
pool ntp1.example.internal iburst
pool ntp2.example.internal iburst
pool ntp3.example.internal iburst
# 启动初期偏差过大时允许校正,避免长时间带着错误时间提供服务。
makestep 1.0 3
rtcsync
iburst 会在启动时快速发起测量;makestep 1.0 3 仅允许前 3 次更新中、偏差超过 1 秒时跳变校时。不要在高并发业务运行中随意执行大幅跳时:倒退或前跳都会影响定时任务、缓存过期和日志排序。若偏差很大,应先摘流量,再校时并验证。
修改后执行:
sudo systemctl restart chronyd
sleep 5
chronyc tracking
chronyc sources -v
若 Leap status 仍不是 Normal,不要宣布修复;继续检查 NTP 网络路径和上游时间源健康。
2. 阻止未同步节点接收业务流量
在 systemd 服务中把时间同步作为启动前置条件:
[Unit]
Wants=network-online.target time-sync.target
After=network-online.target time-sync.target
[Service]
ExecStartPre=/usr/bin/timedatectl show --property=NTPSynchronized --value
注意:上面的 ExecStartPre 只会打印状态,不能单独作为拦截条件。更可靠的做法是在部署脚本或健康检查中显式判断返回值,例如:
timedatectl show --property=NTPSynchronized --value | grep -qx yes
命令返回非零时让发布失败,并将节点保持在负载均衡器摘除状态。Kubernetes 场景可通过节点启动脚本保证 chrony 就绪,并用 readiness probe 延迟业务接流;不要让 probe 直接修改系统时间。
验证与回归
修复完成后至少确认:
chronyc tracking显示Leap status: Normal,并有选中的^*时间源。- 连续观察 10 至 30 分钟,偏差未持续扩大。
- 故障节点上的 JWT 校验、TLS 调用和带签名的 API 请求恢复正常;用真实请求而非只看 chrony 服务状态验证。
- 对比应用日志、访问日志和监控时间线,事件顺序不再出现跨节点倒置。
预防措施
- 将 chrony 配置纳入镜像或配置管理,至少配置三个受控时间源,并定期演练单一时间源不可用。
- 采集
node_timex_offset_seconds、同步状态和 NTP 源可达性;偏差超过业务阈值时告警,并按节点维度聚合。 - 在发布、扩容和虚拟机恢复流程中加入“时间已同步”门禁,避免刚恢复的节点立刻接流量。
- JWT 的时钟容忍只应作为数秒级网络抖动的缓冲,不能替代时钟治理;过大的容忍窗口会扩大令牌可被重放的时间。
- 记录时钟跳变、虚拟机迁移和宿主机维护事件,事故复盘时将其与认证失败、证书告警关联分析。
总结
当认证或 TLS 故障只集中在少数节点时,先用 chrony 的同步状态和偏差数据建立证据,再决定是否调整应用配置。可靠的治理路径是:多时间源同步、未同步节点不接流量、偏差可观测、异常可回滚。这样既能恢复当下请求,也能避免同类问题在扩容或故障恢复时再次出现。
Discussion
评论