清除 K8s 节点时钟漂移:一次证书校验失败与 API Server 通信中断的排查实录

K8s 节点系统时钟漂移导致 kubelet 证书校验失败、API Server 通信中断,节点频繁 NotReady 的排查与根治记录。

问题背景

公司生产 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 小时才被完全收敛。

故障现象

  1. 节点状态异常kubectl get nodes 显示 7 个节点持续 NotReady,kubelet 日志中反复出现:

    1
    2
    
    x509: certificate has expired or is not yet valid: current time ... is before ...
    Unable to connect to the server: x509: certificate signed by unknown authority
    
  2. CSR 卡死:新节点加入时 kubelet 发起的 CSR(CertificateSigningRequest)长期处于 Pending 状态,kubectl get csr 看不到自动批准记录。

  3. Pod 调度失败:受影响节点上的 Pod 全部处于 ContainerCreatingPending,事件日志显示 FailedMountMountVolume.SetUp failed

  4. 时间偏差实测

    • 控制平面节点(3 台):date 显示 2026-09-21 07:12:33
    • 故障节点 A:2026-09-21 06:27:11(慢 45 分钟)
    • 故障节点 B:2026-09-21 07:20:05(快 8 分钟)
  5. 监控告警:Prometheus node_time_secondskube_node_status_condition{condition="Ready"} 同时触发,Grafana 面板出现明显的「时间跳跃」曲线。

排查过程

第一步:确认时钟偏差范围

在故障节点执行:

1
2
3
date +%s
# 对比控制平面
ssh control-plane "date +%s"

发现偏差普遍超过 5 分钟,部分节点甚至达到 45 分钟。进一步检查 NTP 服务状态:

1
2
3
timedatectl status
chronyc tracking
systemctl status systemd-timesyncd

结果显示:部分节点仍使用默认的 systemd-timesyncd,部分节点安装了 chrony 但配置指向已下线的内部 NTP 服务器(ntp.internal.company 已于 3 周前迁移至 chrony-pool.internal)。

第二步:定位证书校验失败链路

kubelet 默认使用宿主机系统时间进行 TLS 握手。查看 kubelet 证书:

1
2
3
openssl s_client -connect api.k8s.internal:6443 \
  -cert /var/lib/kubelet/pki/kubelet-client-current.pem \
  -key /var/lib/kubelet/pki/kubelet-client-current.pem 2>&1 | grep Verify

返回 Verify return code: 9 (certificate is not yet valid),确认是时间导致证书尚未生效。

进一步检查 kubelet 启动参数:

1
2
ps aux | grep kubelet | grep -o -- '--kubeconfig[^ ]*'
cat /var/lib/kubelet/config.yaml | grep -E 'rotateCertificates|serverTLSBootstrap'

发现节点证书轮换已开启(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 通信的影响

在故障节点手动同步时间后立即测试:

1
2
3
4
sudo timedatectl set-ntp false
sudo date -s "$(ssh control-plane date)"
sudo systemctl restart kubelet
kubectl get nodes <node-name>

节点在 30 秒内恢复 Ready,确认时钟漂移是唯一根因。

解决方案

立即止血(已执行)

  1. 对 7 个故障节点执行强制时间同步:

    1
    2
    3
    4
    
    sudo systemctl stop systemd-timesyncd chronyd || true
    sudo timedatectl set-ntp false
    sudo date -s "$(ssh control-plane 'date -u')"
    sudo systemctl restart kubelet
    
  2. 临时关闭自动时间同步,待根因修复后再恢复。

根治方案

  1. 统一 NTP 客户端为 chrony

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    sudo 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
    
  2. 禁用 systemd-timesyncd(避免冲突):

    1
    2
    
    sudo systemctl disable --now systemd-timesyncd
    sudo timedatectl set-ntp false
    
  3. 在所有节点上添加时间同步监控

    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 分钟"
    
  4. 在 VM 模板中固化 chrony 配置,新节点入池即自动同步。

根因分析

根本原因:VM 迁移后,目标宿主机的 NTP 配置未同步更新,导致部分节点仍指向已下线的内部 NTP 服务器,chronyd 服务启动失败,系统时钟逐渐漂移。

技术链路

  • VM 迁移 → 旧 NTP 配置残留 → chronyd 无法同步 → 系统时间偏差 > 证书有效窗口 → kubelet TLS 握手失败 → CSR 无法批准 → 节点 NotReady

为什么监控没有提前发现

  • 集群监控仅关注 node_time_seconds 的绝对值,未设置偏差阈值告警。
  • 部分节点使用 systemd-timesyncd,Prometheus node-exporter 默认不采集其状态。

预防措施

  1. 所有节点强制使用 chrony + 内部 NTP 池,禁用 systemd-timesyncd。
  2. VM 模板中预装 chrony 并配置健康检查脚本,开机后自动验证 NTP 同步状态。
  3. 新增 Prometheus 告警node_timex_offset_seconds > 120(2 分钟偏差即告警)。
  4. 在变更管理 Checklist 中增加「NTP 配置核对」项,VM 迁移/克隆后必须执行。
  5. 定期演练时钟漂移场景:模拟 NTP 服务器下线,验证集群自愈能力。

总结

本次故障的本质是「基础设施配置漂移」在 K8s 证书链路上的放大效应。时钟偏差看似微小,却能直接阻断 kubelet 与 API Server 的 mTLS 通信,导致节点无法加入集群。根治方案不仅需要修复当前节点,更需要从 VM 模板、监控告警、变更 Checklist 三个维度建立防御体系,避免同类问题在未来扩容或迁移中再次出现。

延伸阅读

使用 Hugo 构建
主题 StackJimmy 设计