跳转到内容

K8s 集群时间同步故障:apparmor 拦截 chronyd 导致节点时间漂移

结论:K8s 节点时间停在过去(kubelet 证书失效、kubectl exec 报 Unauthorized)时,根因往往是宿主机 apparmor 拦截了容器内 chronyd。删除 /etc/apparmor.d/usr.sbin.chronyd 并重启 apparmor 与容器运行时即可恢复。本案例最终 9 台节点时统指标全部达标:软件同步单点精度 ≤40μs(指标 ≤1ms),全系统时统偏差 35μs(指标 ≤10ms)。

适用范围与环境

  • 集群:master-1(麒麟 V10 ARM64,NTP 源,chronyd 监听 123+323)+ 9 台 computing(Ubuntu x86)
  • 时间同步链路:chrony DaemonSet(hostNetwork)作为 NTP 源 → 各节点 timesyncd/chrony 同步
  • 故障节点:computing-1(172.16.11.14)
  • 现象链:节点时间停在 2023 → kubelet 证书过期 → kubectl exec 报 Unauthorized → 时间同步持续超标(-1.8ms)

排障步骤

1. 现象确认

bash
date                                  # 发现系统时间停在 2023
kubectl exec -it <pod> -- bash         # 报 Unauthorized(kubelet 证书因时间错误失效)
timedatectl status                     # NTP service: n/a

2. 手动校正时间破局

bash
date -s "2026-08-25 15:00:00"          # 先手动把时间拉回,让 kubelet 证书恢复有效
systemctl restart kubelet              # 再重启 kubelet

时间恢复后 kubectl exec 即恢复正常——这是第一步破局,但没有解决根因,时间会继续漂移。

3. 排查时间同步链路

bash
# 确认 NTP 源可达(chronyd 即 123 端口,不要被"没用 123"误导)
ss -ulnp | grep -E ":123|:323"         # master-1 上 chronyd 同时监听 123+323

# 单点精度测试(持续超标判定)
for i in 1 2 3 4 5; do ntpdate -q 172.16.11.11 2>/dev/null | grep offset; sleep 3; done
# 故障节点持续输出 -0.001817s(-1.8ms),远超 1ms 指标

曾一度怀疑 NTP 客户端 timesyncd 二进制版本不匹配(此前从其他节点拷贝恢复)、bond 多播、交换机 PTP 转发等,均不是根因。

4. 根因定位:apparmor 拦截 chronyd

bash
dmesg | grep -i apparmor | tail -20     # 关键!看到 chronyd 相关 DENIED 记录
journalctl -k --no-pager | grep -i "denied" | tail -20

apparmor 的 usr.sbin.chronyd profile 拦截了容器内 chronyd 的关键操作(写 /var/run/chrony、绑定端口等),表现为进程能启动、能部分工作,但时间同步静默失败——日志里没有明显报错,这是最迷惑人的地方。

5. 修复

bash
# 删除 chronyd 的 apparmor profile
sudo rm -f /etc/apparmor.d/usr.sbin.chronyd
sudo systemctl restart apparmor

# 重启容器运行时,使容器内 chrony 重新加载
sudo systemctl restart containerd    # Containerd 运行时
# 或 sudo systemctl restart docker  # Docker 运行时

修复后单点 offset 回落到 0.000040s(40μs)。

原因与机制

  • chrony 以容器(Pod)方式运行在节点上,Ubuntu 宿主机的 apparmor profile(usr.sbin.chronyd)默认限制 chronyd 的写路径与端口操作
  • apparmor 拦截是静默的:进程存活、部分功能正常,只有 dmesg 里有 DENIED 记录,容器日志不一定有报错
  • 时间漂移 → kubelet 证书失效 → API 鉴权失败的因果链,掩盖了真正的时间源问题

指标验证方法

指标 1:软件同步精度不低于 1ms(单点)

bash
# 每台节点连续 5 轮采样,取最差值
for i in 1 2 3 4 5; do ntpdate -q 172.16.11.11 2>/dev/null | grep offset | awk '{print $NF}'; sleep 3; done | sort | tail -1
# 输出 ≤ 0.001(1ms)即达标

指标 2:信号级仿真全系统时统精度不低于 10ms(全系统)

bash
# 每台节点各测一轮 offset
ntpdate -q 172.16.11.11 2>/dev/null | grep offset
# 全系统偏差 = max(所有节点 offset) - min(所有节点 offset),≤ 0.01 即达标

验证结果(已验证)

节点修复前 offset修复后 offset
1-9 台(含故障节点)故障节点 -1817μs ❌全部 ≤40μs ✅
  • 软件同步单点精度:9/9 台 ≤40μs,指标 ≤1ms,余量 25 倍
  • 全系统时统偏差:max−min = 40−5 = 35μs,指标 ≤10ms,余量 285 倍

风险与边界

  • 删除 apparmor profile 降低了宿主机对 chronyd 的强制访问控制,内网可信环境可接受;公网/多租户环境应改为编写更细的 apparmor profile 而非删除
  • 手动 date -s 只作破局,不能替代根因修复;时间大幅回拨会触发 kubelet 证书验证失败,务必随后重启 kubelet
  • 单点采样有波动,指标验证建议多轮采样取最差轮次

教训

容器内时间服务(chrony/NTP)异常时,第一步查宿主机安全机制

bash
dmesg | grep -i apparmor | tail -20
journalctl -k --no-pager | grep -i denied | tail -20

DENIED 即 apparmor/SELinux/seccomp 在拦截。其他节点正常、单节点异常时,优先对比宿主机层差异(包来源、apparmor 状态、内核参数),而不是在网络层反复排查。

基于 MIT 许可发布