服务激活、接受时间样本和时钟同步需要不同的观察证据。

分别检查 timesyncd 服务状态与时钟证据

systemd-timesyncd 是一个精简 SNTP 客户端,用配置的时间源同步本地系统时钟。它可以帮助证书检查与墙钟时间戳保持有意义,但不会无条件保证某个精度范围。守护进程能否运行、是否收到可接受样本,以及系统是否报告同步,是需要分别确认的事实。只看到安装了客户端,或者某条命令成功返回,都不足以推导时钟已经准确。 坏掉的样子 守护进程并不总是平滑校正。文档明确说明,大偏差会直接跳时,小偏差则逐渐调整。因此,开机时离正确时间很远的机器,可能发生时间不连续。经过时长测量应使用单调时钟,解释墙钟时间戳的应用则应处理校时,而不是假定时间永远平滑前进。不能把“精简客户端”误写成“绝不会让应用看见时间跳跃”。 FallbackNTP 不是显式服务器不可达时就一定会启用的通用应急列表,它在没有其他已知服务器信息时才适用。全局 NTP 条目和 networkd 提供的链路时间源,优先于这个后备设置。因此,应检查实际来源选择与配置覆盖文件,不能仅因为主来源连接失败,就断定某个写在 fallback 中的服务器必定已经被尝试。 未同步也不能证明 UDP 流量被阻止。服务可能缺失、未启用、被单元条件排除、没有调整时钟的权限,或者正在拒绝不合适样本。反过来,全局同步属性也不能单独说明哪个进程提供了时间。应区分管理器看到的服务状态、客户端观察到的样本,以及内核层同步指示,避免把来自不同层面的一个布尔值当成完整故障结论。 快速验证 这个失败模式实验在一次性 Debian 12 Docker 容器中以 root 运行,PID 1 为 systemd 252.39。初始化进程通过 /usr/bin/setpriv --bounding-set=-sys_time,-sys_module /sbin/init 启动,移除相关能力以防止调整 Docker 虚拟机共享时钟。脚本在请求任何服务启动前检查该前提,并要求安装包中的容器排除条件存在: from pathlib import Path import json,subprocess def run(*args): result=subprocess.run(args,text=True,capture_output=True,timeout=10) assert result.returncode==0,(args,result.stderr) return result.stdout.strip() def properties(unit): output=run('systemctl','show',unit,'-p','ActiveState','-p','SubState','-p','MainPID','-p','ConditionResult') return dict(line.split('=',1) for line in output.splitlines()) # This failure-mode exercise requires an isolated container that cannot set time. status=dict(line.split(':',1) for line in Path('/proc/1/status').read_text().splitlines() if ':' in line) assert int(status['CapBnd'].strip(),16)&(1<<25)==0,'PID 1 must lack CAP_SYS_TIME' assert run('systemd-detect-virt','--container')=='docker' unit='systemd-timesyncd.service';definition=run('systemctl','cat',unit) assert 'ConditionCapability=CAP_SYS_TIME' in definition assert 'ConditionVirtualization=!container' in definition assert properties(unit)['ActiveState']=='inactive' run('systemctl','start',unit) state=properties(unit) assert state=={'MainPID':'0','ActiveState':'inactive','SubState':'dead','ConditionResult':'no'},state clock=dict(line.split('=',1) for line in run('timedatectl','show','-p','CanNTP','-p','NTP','-p','NTPSynchronized').splitlines()) assert all(value in ('yes','no') for value in clock.values()) print('start command returned zero; unit stayed inactive with PID 0 and failed conditions') print('clock properties:',json.dumps(clock,sort_keys=True)) print('PID 1 lacks time-setting capability; Docker condition also excludes this packaged service') print('no clock-setting command was issued and no NTP synchronization was claimed') 启动命令返回零,但单元仍为非活动,主进程编号为零,ConditionResult=no。条件不满足而跳过激活,与守护进程成功运行,是不同结果。样本安装包要求调整时间的能力,并排除容器,而当前环境两项前提都不满足。因此在假设 NTP 报文被网络丢弃之前,就已经有具体证据解释服务为何没有工作。 ...

三月 11, 2027 · 1 分钟 · XB Field Notes
未同步但仍运行的客户端选择本地来源,全程不调整系统时钟。

服务器时间校准需要可观察的依据

时间守护进程只有在实际状态满足应用要求时才真正有用。进程正在运行,仍可能没有可用来源;已经选择某个来源,也可能尚未充分测量,或者来源本身就不正确。时间服务健康应当是一条可观察的链路:来源配置、成功采样、选择状态、不确定性以及实际允许的时钟控制。进程显示绿色,只能覆盖链路最前面的一部分。 读取守护进程实际报告的内容 Chrony 的 tracking 输出分别给出参考来源、层级、最近更新时间、尚未消除的系统时间修正量、估计频率、频率偏斜不确定性、根延迟和根离散度。它们回答不同问题:offset 关注时间差,frequency 关注时钟走速,skew 反映频率估计的不确定性。根延迟与离散度则参与描述参考路径带来的时间不确定性。只挑一个很小的偏移数值,不能概括整个时钟质量。 sources 输出用于观察候选来源及实际选择。常见显示中的 ^* 表示选中的 NTP 服务器。Reach 是八进制历史位图,377 表示最近八次轮询机会都有成功响应,并不是百分比,也不保证下一次仍然可达。LastRx 有助于查看最近收到的样本已经过去多久。应把来源状态与 tracking 一起读,而不是把进程持续运行时长误认为同步证据。 上游短暂中断后,守护进程可能继续依据已有模型估计时间,但不确定性会随条件变化而扩大。需要根据应用容忍度监控更新时间和误差估计,区分短期维持与无限期可信。多个服务器名称也不必然意味着独立参考,如果它们共享上游或基础设施,仍然可能一起出错。来源选择降低部分故障风险,却不能证明所有上游声明都正确。 坏掉的样子 容器通常共享宿主的内核时钟,在容器内运行时间服务并不会自然得到一块独立可调的民用时钟。Chronyd 的 -x 模式明确关闭系统时钟控制,但仍保留估计和提供时间服务的能力。因此,客户端可以报告已经同步,系统时钟却并没有被这个进程修正。排查时必须确认实际运行模式,以及哪一层负责宿主时间。 local 指令也容易制造错误信心。它允许 chronyd 在没有外部参考的情况下表现为已同步,可以用于有明确设计的隔离网络安排。但如果只是为了消除告警而启用,就会把缺失参考这一问题隐藏起来。解释 Normal 之前,要核对参考标识以及预期来源政策,不能把某种协议状态直接解释成 UTC 已经准确。 运行检查还需要有意义的限制。Chronyc 的 waitsync 可以等待同步,并可选地限制剩余时间修正量和频率估计偏斜;相应限制为零时,额外约束会被关闭。两个限制都关闭的成功结果,比执行应用专属容忍边界的检查弱得多。即使配置了边界,它也不能替任意上游时钟证明真实性,来源信任仍是另一项要求。 快速验证 使用新的临时 Debian 12 Docker 容器,运行 apt-get update 和 apt-get install -y python3 chrony 安装依赖,将下面代码保存为 chrony-status.py,以 root 执行 python3 chrony-status.py。创建容器时保持独立网络,不使用宿主网络,并移除 SYS_TIME 能力。实测版本为 chrony 4.3 与 Python 3.11.2,代码只在回环地址通信。 ...

九月 2, 2026 · 2 分钟 · XB Field Notes