尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Kubernetes集群NTP时间同步实战:chrony部署与故障排查

Kubernetes集群NTP时间同步实战:chrony部署与故障排查 1. 项目背景被忽略的时间同步正在拖垮你的集群先讲个真实经历。去年我接手过一套生产环境Kubernetes集群现象很离谱Pod调度明明正常但应用日志里的时间戳经常乱跳Prometheus告警偶尔误报更诡异的是某个服务偶尔出现证书校验失败。排查到后面才发现三台master节点的时钟偏差已经超过4秒而etcd集群对时间漂移极其敏感Raft协议下的选举和心跳全被这4秒搅乱了。这套集群从搭建到现在从来没配过NTP。很多团队在搭建Kubernetes时会把精力放在网络插件、存储方案、高可用架构上时间同步这种基础服务反而被当成“装个系统就有”的东西。但生产级集群对时间一致性的要求远比单机环境严格得多。证书校验、日志关联、分布式事务、etcd选举、HPA伸缩、CronJob调度几乎每个核心组件都隐式依赖节点间的时间一致性。这篇文章我把自己在Kubernetes集群里落地NTP的完整思路写出来包括协议原理、部署方案、参数选型和真实故障排查记录。不管你是刚接触Kubernetes还是已经在维护生产环境这套方案都能直接拿过去用。2. NTP同步原理与Kubernetes的时间敏感点2.1 NTP报文交互与时间偏移计算NTPNetwork Time Protocol能工作核心在于精确计算本地时钟与时间服务器之间的偏移量offset。整个交互过程看起来简单但每个时间戳都有严格的位置定义。客户端在发送请求前记录本地时间T1originate timestamp把T1写入报文发出。服务器收到请求后记录收到时刻T2receive timestamp并在处理完后把回复报文的发送时刻T3transmit timestamp写入报文。客户端收到回复的瞬间记录T4destination timestamp。有了这四个时间戳网络延迟和时钟偏移就可以算出来延迟 (T4 - T1) - (T3 - T2) 偏移 ((T2 - T1) (T3 - T4)) / 2这个算法的巧妙之处在于它假设网络往返延迟是对称的。虽然现实中不对称的情况很常见但NTP通过多次采样、过滤和统计筛选能把误差控制在毫秒级别。生产环境里我们通常用chrony而非ntpd原因后面会详细说。Kubernetes集群中节点与节点之间、Pod与Node之间没有明确的时钟同步需求真正需要的是所有节点无限接近于“同一个真实时间”。这就像一支乐队每个乐手只要都盯着指挥的手势NTP服务器节奏就不会乱而不是互相看对方的乐器。2.2 哪些组件对时间不一致特别敏感Kubernetes生态里时间同步不是“有就行”而是“必须准”。etcd是第一受害者。etcd用Raft协议保证一致性领导人选举依赖心跳超时。默认心跳间隔100ms选举超时1秒。如果某个节点时钟比leader快了几百毫秒它发出的心跳会被认为来自“未来”或者它自己因为看不到“当前时间”内的心跳而发起不必要的新选举。集群规模越大节点越多时间漂移导致的选举抖动越容易被放大。Kubelet证书轮转是第二受害者。Kubernetes默认给kubelet签发的证书有效期是一年到期前自动轮转。证书校验依赖当前时间是否在有效期内。如果节点时间比真实时间快了几天证书还没到轮转窗口就已经“过期”kubelet连接APIServer会被拒绝节点直接NotReady。我在生产环境遇到过节点时钟快了7天导致kubelet证书全部失效的情况最后只能手动重置节点。日志和监控是第三受害者。多节点日志采集后汇总到ELK或LokiNode A和Node B的日志时间戳差几秒排障时看到的日志顺序就是乱的。Prometheus告警规则里如果用了time()函数或基于时间窗口的聚合时间不同步也会导致误报和漏报。应用层依赖同样不能忽视。CronJob调度器虽然运行在controller-manager里但它依赖节点时间确认执行窗口。分布式数据库、消息队列的客户端时间戳校验甚至前端页面和后端API之间的请求签名也都依赖一致的时间基准。2.3 为什么不能只靠云平台的自动同步云平台上很多虚拟机默认开启了时间同步机制比如AWS的chrony配置、阿里云的systemd-timesyncd但这不意味着你可以完全不管。一方面部分云主机默认只同步宿主机时间而宿主机自身的NTP配置未必可靠。另一方面容器运行时的时钟隔离做得并不彻底。容器共享宿主机内核的CLOCK_REALTIME你没法在容器层面单独跑一个NTP服务去改时间。所以集群节点层的时间同步永远只能落在宿主机层面。我曾遇到过ESXi宿主机没有配置NTP导致上面跑的几十台虚机全部跟着宿主机一起漂移的情况。虚机里的chrony怎么调都没用因为它的参考源是宿主机时钟而宿主机自己就是不准的。所以不管是物理机、虚拟机还是容器时间同步必须是端到端的链条。3. 时钟同步方案选型chrony才是生产环境的正确打开方式3.1 chrony vs ntpd vs systemd-timesyncd很多老管理员习惯在CentOS上装ntpd新一些的系统自带systemd-timesyncd。三者的定位不同适用场景也不同。ntpd是传统方案采用逐步调整的策略每次修正幅度很小适合需要长时间稳定运行、时钟本身精度较高的场景。但它有一个明显的短板如果本地时钟偏差太大比如超过1000秒ntpd会拒绝调整需要人工介入。systemd-timesyncd是systemd自带的轻量级SNTP客户端只做时间获取不做服务端。它的配置简单到几乎不用管但同样有问题没有复杂的滤波算法精度有限而且无法对外提供时间服务。chrony的设计目标就是解决ntpd的痛点。它支持快速同步即使时钟漂移很大也能在几秒内收敛支持对时钟频率的持续补偿还自带服务端能力。实测中chrony在普通物理机上的同步精度可以稳定在毫秒级在云主机上表现也优于ntpd。特性ntpdsystemd-timesyncdchrony同步精度毫秒级10~100毫秒毫秒级启动时大偏差收敛慢可能拒绝调整快快时钟频率补偿有无有作为时间服务器支持不支持支持配置复杂度中低中生产级Kubernetes集群我统一用chrony。3.2 chrony核心配置逐项拆解以最常见的/etc/chrony.conf为例生产环境配置我会这么写# 时间服务器池 pool 2.centos.pool.ntp.org iburst server ntp.aliyun.com iburst server ntp.tencent.com iburst # 允许本机作为时间服务器提供服务 allow 10.0.0.0/8 allow 192.168.0.0/16 # 即使在网络暂时中断时也用本地时钟作为参考 local stratum 10 # 时钟偏差过大时仍允许调整 makestep 1 3 # 记录时钟漂移率 driftfile /var/lib/chrony/drift # 启用RTC跟踪 rtcsync # 允许chronyc命令管理 cmdallow 127.0.0.1逐项解释关键参数pool和server的区别pool会从域名解析出的多个IP里自动选择若干服务器有负载均衡和故障转移的效果server则是固定服务器。建议国内环境优先用国内NTP服务器公网pool服务质量不稳定。iburst参数第一次同步时快速发起8个请求包而不是等正常的同步间隔。这样chrony启动后几秒内就能完成初次同步对开机时间敏感的节点很有用。allow网段如果集群节点想通过NTP服务器互相同步必须配置这个。比如三个master中指定一台为内部时间源其他节点从这台同步就需要在这台上配置allow。local stratum 10指定本机时钟作为参考的级别。当所有上游服务器都不可达时本机还能继续为下游提供时间服务。stratum 10表示“可信度较低但比没有强”。makestep 1 3前三次同步时如果偏差超过1秒直接跳变而不是逐步调整。这个参数在生产环境非常重要节点启动后能快速追平时间不用等几分钟的平滑调整。3.3 系统层与Docker层的时间配置补充chrony配置好只是第一步操作系统内核、Docker/containerd也需要配合。时钟跳变对Linux内核的影响往往被忽视。如果系统重启后硬件时钟与系统时钟差异过大内核日志里会出现hpet相关告警某些驱动会受影响。因此最好把硬件时间也同步一下hwclock --systohc容器运行时层面Docker默认继承宿主机的PID namespace和UTS namespace但时间不是namespace隔离的。也就是说容器里跑date看到的时间和宿主机一致。这是一个既好又坏的特性——好在排查问题不用进容器对时间坏在宿主机一旦漂移所有容器跟着遭殃。4. 生产级Kubernetes集群的NTP部署实操4.1 集群拓扑中的NTP架构设计Kubernetes集群的NTP拓扑不能简单搞成“所有节点都去连公网服务器”。公网源的质量和稳定性不可控而且每个节点都访问公网NTP源既浪费出口带宽又增加了不可靠因素。推荐的分层架构是这样一层内网的NTP服务器可以是集群外的独立服务器也可以是master节点中的一台向上同步自多个公网NTP源向下对内网提供服务。二层所有Kubernetes节点包括master和worker向这个内网NTP服务器同步。三层无。不要搞多级层叠每多一级转发时间误差就累积一次。两个级别的架构足够。如果集群规模不大比如100台以内直接在master节点上跑chrony服务端开放UDP 123端口给内网其他节点指向它就够了。如果集群更大建议用独立的NTP服务器或者负载均衡VIP避免master节点的chrony成为单点。4.2 各节点部署chrony的完整命令我这里以Ubuntu 20.04 LTS为例CentOS/Rocky的操作本质相同就是包管理器不同。第一步安装并配置chronyapt update apt install -y chrony cat /etc/chrony/chrony.conf EOF # 上游时间服务器 server ntp.aliyun.com iburst server ntp.tencent.com iburst server ntp.ntsc.ac.cn iburst # 如果作为内网时间服务器放开这个 allow 10.0.0.0/8 # 本地时钟作为最后防线 local stratum 10 # 快速收敛大偏差 makestep 1 3 # 重启后仍保留漂移信息 driftfile /var/lib/chrony/drift rtcsync EOF systemctl enable --now chrony systemctl restart chrony第二步验证时间同步状态chronyc tracking输出里重点关注两个指标Leap status必须是Normal表示没有闰秒调整等待。System time显示本机与参考源的偏差正常应小于1毫秒。Stratum本机所处层级能上网时一般是2或3。再执行一下chronyc sources -v查看时间源的状态。输出中的^*表示当前选中的同步源^是备用源。如果出现^?说明该源不可用需要检查网络连通性或防火墙规则。第三步测试实际同步# 查看当前系统时间 date # 查看硬件时间 hwclock -r两个时间应该基本一致说明rtcsync生效系统会定期把系统时间同步到硬件时钟。4.3 云主机与裸金属环境的不同处理方式云主机的NTP部署比裸金属稍复杂。国内主流公有云的默认镜像里都预装并启用了cloud-init的时间同步配置。如果你不改cloud-init可能会在每次启动时重置你的chrony配置。处理方式有两种一是在cloud-init中禁用时间管理模块二是直接用cloud-init的模板自定义chrony配置。我推荐第二种因为重启后配置还能保持。在/etc/cloud/cloud.cfg.d/99-custom-ntp.cfg里写ntp: enabled: true ntp_client: chrony config: confpath: /etc/chrony/chrony.conf packages: - chrony service_name: chrony template: | server ntp.aliyun.com iburst server ntp.tencent.com iburst allow 10.0.0.0/8 local stratum 10 makestep 1 3 driftfile /var/lib/chrony/drift rtcsync另外要注意云厂商安全组/防火墙必须放行UDP 123端口。这个坑我踩过不止一次节点之间网络通但NTP的UDP包被安全组静默丢弃chronyc sources里一直显示^?排查半天才发现是安全组规则漏了。裸金属环境相对简单只需要保证物理机的BMC/IPMI带外管理口也配置好NTP即可。很多服务器BMC的时钟一开始就不准而系统安装时如果开启了从硬件同步时间的配置这个误差会一直传递到系统里。4.4 容器与Pod层面要不要做时间同步有不少人问过我Pod容器里面要不要也设置时间同步。答案很明确不需要也做不到。Linux的time namespace虽然存在但目前容器运行时默认没有开启。就算开启也不建议在生产环境这么搞因为时间跳变对应用的影响不可预估。容器里跑dpkg-reconfigure tzdata修改的是/etc/localtime时区文件只是改变了时间的展示形式并没有改变实际时间戳。所以只要宿主机时间准确容器时间就一定准确。唯一需要额外处理的是构建镜像时设置正确的时区# Dockerfile中 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ echo Asia/Shanghai /etc/timezone这样容器内日志的时间展示才不会偏移8个小时。5. 集群时间同步的验证与监控5.1 全节点状态收集脚本Kubernetes集群节点时间是否一致不能靠一台一台登录排查效率太低。写个脚本批量收集才符合生产环境的管理方式。#!/bin/bash MASTERS(10.0.0.1 10.0.0.2 10.0.0.3) WORKERS(10.0.1.11 10.0.1.12 10.0.1.13 10.0.1.14) for node in ${MASTERS[]} ${WORKERS[]}; do echo $node ssh $node timedatectl | grep synchronized\|Time zone; chronyc tracking | grep System time\|Stratum\|Last offset; chronyc sources -v | grep \^\* done重点关注System time这一栏的前缀符号负值说明本机时间比参考源慢正值说明快。所有节点的偏差绝对值都在10毫秒以内是理想状态放宽一点也不能超过100毫秒。5.2 使用Prometheus监控时钟偏移生产级集群必须有对应的监控指标。node_exporter自带的node_timex_*指标可以上报时钟相关信息其中最关键的是node_timex_offset_seconds时钟偏移量。node_timex_sync_status同步状态值为1表示已同步。在Prometheus里配一个告警规则当时钟偏移超过阈值时触发告警groups: - name: ntp-alerts rules: - alert: NodeClockOffsetHigh expr: abs(node_timex_offset_seconds) 0.1 for: 5m labels: severity: warning annotations: summary: 节点 {{ $labels.instance }} 时钟偏移过大阈值我设的是0.1秒。如果业务对时间一致性特别敏感比如分布式存储建议压到0.05秒。5.3 常见问题速查表问题可能原因解决方法chronyc sources显示所有源都是^?UDP 123端口被防火墙拦截检查systemctl status firewalld放行UDP 123时钟一直在跳变无法稳定上游NTP服务器质量差更换更稳定的NTP源增加server条目chrony启动后几秒内时间跳变太多makestep参数未配置在chrony.conf中添加makestep 1 3虚机时间偏差大但chrony正常宿主机自身时间不准先修复宿主机NTP再重启虚机chrony容器内时间和宿主机不一致手动修改过容器内时间重启容器不要手动改容器时间6. 真实故障记录一次时钟漂移引发的etcd选举风暴这套集群有3台master5台worker。某次升级过程中我重启了其中一台master节点结果集群开始间歇性不可用kube-apiserver频繁报错kubectl偶尔执行失败。一开始我以为是升级操作引起的组件异常检查了所有静态Pod状态都正常。后来发现APIServer和etcd的日志大量出现“leader changed”和“failed to send out heartbeat”的告警。用chronyc tracking查看所有master节点发现刚重启的那台机器时间比另外两台快了约800毫秒。原因很典型这台机器之前发生过内核panic硬件时钟没有同步重启后系统时钟从硬件时钟读取直接带了好几天的偏差。虽然chrony配置了makestep但当时的上游NTP服务器是公网的重启后网络还没就绪chrony没法立即拉到正确时间。解决过程是这样的# 强制立即同步时间忽略makestep次数限制 chronyc makestep # 重启chrony systemctl restart chrony # 持久化系统时间到硬件时钟 hwclock --systohc强制同步后与etcd的时间偏差降到几毫秒集群在几分钟内恢复稳定。这个案例给我两个教训一是集群节点的系统盘最好做硬件时钟自动同步不要依赖公网NTP在系统启动后慢慢修正二是重启节点前先检查chronyc tracking的输出确认时间同步正常再执行重启操作。还有一次客户反馈某个服务的日志时间戳出现了整整8个小时的跳变。排查后确认不是时间同步问题而是容器镜像里的/etc/localtime没有设置容器默认使用UTC与宿主机东八区时间差了8小时。修改Dockerfile设置正确的时区后解决。这类问题在Kubernetes中太常见了写镜像时顺手加一行时区配置能省去大量后续沟通成本。7. 关于内置时间同步与运维习惯的几点经验NTP这件事部署并不难难在维护习惯。有几个点是我这几年反复强调的开机自动同步要开。很多系统日志里看到的时间偏差根源在服务器长时间未重启chrony也没发生任何告警但晶振漂移导致系统时间逐步偏离。时钟晶振的漂移率是随温度变化的chrony的driftfile能记录并修正这部分误差前提是chrony一直在运行。Kubernetes节点重启前必须确认时间。根据个人经验重启前时间偏差超过1秒的节点重启后有大概率带偏差启动。如果业务允许先把chronyc makestep执行一下再操作。不要迷信云平台的自动同步。公有云控制台里“自动时间同步”选项默认开启但不代表底层配置就一定是准的。每年抽查两次集群节点的时钟状态比出问题再处理要省事得多。容器镜像的时区同样需要管理。统一在基础镜像里设置好/etc/localtime和/etc/timezone避免每个服务各自处理容易遗漏。最后再说一个小技巧在Kubernetes集群里不必刻意避免使用date命令或TZ环境变量。很多排障和日志分析场景你需要的只是一个可靠的时间基准而NTP做的事情就是把这个基准稳定地放到每一个节点上。把这个基础打牢后续很多看似玄学的“时间问题”根本不会发生。
返回列表