问题背景
公司生产 K8s 集群(v1.28)近期在节点扩容与 VM 迁移后,陆续出现节点 NotReady 告警。集群规模约 42 节点,承载 1200+ Pod,核心业务为微服务 + AI 推理。运维团队最初怀疑是网络策略或 CNI 问题,但经过多轮排查发现,故障节点均存在系统时钟与控制平面明显偏差(±8~45 分钟不等)。
时钟漂移在 K8s 中并非显性故障,而是通过 TLS 证书校验链路间接触发:kubelet 与 API Server 的 mTLS 握手依赖系统时间判断证书有效期(NotBefore/NotAfter)。一旦节点时间超出证书有效窗口,握手即失败,节点无法注册、无法拉取 Secret/ConfigMap,进而进入 NotReady 状态。
本次故障发生在周五晚间 VM 迁移后,周六凌晨开始批量报错,影响了 7 个节点(占 17%),持续约 9 小时才被完全收敛。
故障现象
-
节点状态异常:
kubectl get nodes显示 7 个节点持续 NotReady,kubelet日志中反复出现:1 2x509: certificate has expired or is not yet valid: current time ... is before ... Unable to connect to the server: x509: certificate signed by unknown authority -
CSR 卡死:新节点加入时
kubelet发起的 CSR(CertificateSigningRequest)长期处于Pending状态,kubectl get csr看不到自动批准记录。 -
Pod 调度失败:受影响节点上的 Pod 全部处于
ContainerCreating或Pending,事件日志显示FailedMount、MountVolume.SetUp failed。 -
时间偏差实测:
- 控制平面节点(3 台):
date显示2026-09-21 07:12:33 - 故障节点 A:
2026-09-21 06:27:11(慢 45 分钟) - 故障节点 B:
2026-09-21 07:20:05(快 8 分钟)
- 控制平面节点(3 台):
-
监控告警:Prometheus
node_time_seconds与kube_node_status_condition{condition="Ready"}同时触发,Grafana 面板出现明显的「时间跳跃」曲线。
排查过程
第一步:确认时钟偏差范围
在故障节点执行:
|
|
发现偏差普遍超过 5 分钟,部分节点甚至达到 45 分钟。进一步检查 NTP 服务状态:
|
|
结果显示:部分节点仍使用默认的 systemd-timesyncd,部分节点安装了 chrony 但配置指向已下线的内部 NTP 服务器(ntp.internal.company 已于 3 周前迁移至 chrony-pool.internal)。
第二步:定位证书校验失败链路
kubelet 默认使用宿主机系统时间进行 TLS 握手。查看 kubelet 证书:
|
|
返回 Verify return code: 9 (certificate is not yet valid),确认是时间导致证书尚未生效。
进一步检查 kubelet 启动参数:
|
|
发现节点证书轮换已开启(rotateCertificates: true),但 bootstrap 阶段因时间漂移无法完成首次 CSR 批准。
第三步:排查 NTP 配置漂移
对比健康节点与故障节点 /etc/chrony/chrony.conf:
- 健康节点:指向
pool chrony-pool.internal iburst - 故障节点:仍指向旧地址
server ntp.internal.company,且chronyd服务处于inactive (dead)状态。
部分节点因 systemd-timesyncd 与 chrony 冲突,timedatectl 显示 NTP synchronized: no。
第四步:验证时间漂移对 API 通信的影响
在故障节点手动同步时间后立即测试:
|
|
节点在 30 秒内恢复 Ready,确认时钟漂移是唯一根因。
解决方案
立即止血(已执行)
-
对 7 个故障节点执行强制时间同步:
1 2 3 4sudo systemctl stop systemd-timesyncd chronyd || true sudo timedatectl set-ntp false sudo date -s "$(ssh control-plane 'date -u')" sudo systemctl restart kubelet -
临时关闭自动时间同步,待根因修复后再恢复。
根治方案
-
统一 NTP 客户端为 chrony:
1 2 3 4 5 6 7 8 9 10sudo apt install chrony -y sudo tee /etc/chrony/chrony.conf <<EOF pool chrony-pool.internal iburst allow 10.0.0.0/8 driftfile /var/lib/chrony/chrony.drift makestep 1.0 3 rtcsync EOF sudo systemctl enable --now chronyd sudo chronyc makestep -
禁用 systemd-timesyncd(避免冲突):
1 2sudo systemctl disable --now systemd-timesyncd sudo timedatectl set-ntp false -
在所有节点上添加时间同步监控:
1 2 3 4 5 6 7 8 9# Prometheus node-exporter 已开启 --collector.timex # 新增告警规则 - alert: NodeClockSkew expr: abs(node_timex_offset_seconds) > 300 for: 2m labels: severity: critical annotations: summary: "节点 {{ $labels.instance }} 时钟偏差超过 5 分钟" -
在 VM 模板中固化 chrony 配置,新节点入池即自动同步。
根因分析
根本原因:VM 迁移后,目标宿主机的 NTP 配置未同步更新,导致部分节点仍指向已下线的内部 NTP 服务器,chronyd 服务启动失败,系统时钟逐渐漂移。
技术链路:
- VM 迁移 → 旧 NTP 配置残留 → chronyd 无法同步 → 系统时间偏差 > 证书有效窗口 → kubelet TLS 握手失败 → CSR 无法批准 → 节点 NotReady
为什么监控没有提前发现:
- 集群监控仅关注
node_time_seconds的绝对值,未设置偏差阈值告警。 - 部分节点使用 systemd-timesyncd,Prometheus node-exporter 默认不采集其状态。
预防措施
- 所有节点强制使用 chrony + 内部 NTP 池,禁用 systemd-timesyncd。
- VM 模板中预装 chrony 并配置健康检查脚本,开机后自动验证 NTP 同步状态。
- 新增 Prometheus 告警:
node_timex_offset_seconds > 120(2 分钟偏差即告警)。 - 在变更管理 Checklist 中增加「NTP 配置核对」项,VM 迁移/克隆后必须执行。
- 定期演练时钟漂移场景:模拟 NTP 服务器下线,验证集群自愈能力。
总结
本次故障的本质是「基础设施配置漂移」在 K8s 证书链路上的放大效应。时钟偏差看似微小,却能直接阻断 kubelet 与 API Server 的 mTLS 通信,导致节点无法加入集群。根治方案不仅需要修复当前节点,更需要从 VM 模板、监控告警、变更 Checklist 三个维度建立防御体系,避免同类问题在未来扩容或迁移中再次出现。