九台服务器的告警几乎在同一时间涌进运维群那一刻我的血压和屏幕上的红色一起飙升。我们团队维护的是一套自建的KVM虚拟化集群三台物理宿主机上跑着九个业务虚拟机OA、数据库、代码仓库、内网远程协助全挤在这一层薄薄的虚拟化世界里。周一早上九点业务方说系统全挂了我第一反应是宿主机出问题了或者是存储阵列炸了。可等我连上虚拟化平台管理界面看到的却是更诡异的一幕物理机全活着虚拟机却集体处于半死不活状态——有的能ping通但应用报错有的在反复重启有的干脆连SSH都进不去。服务器是虚拟的惊魂却是实实在在的。这篇文章就把这次事故的完整排查过程、最终根因和事后复盘写清楚希望能给同样维护虚拟化集群的朋友提个醒。1. 事故现场9台服务器集体罢工是什么样的1.1 周一早上的告警风暴九点过几分值班群里的告警开始连片往外蹦。HTTP服务不可用、数据库连接失败、SSH登录超时、远程协助服务离线……一条接一条几乎覆盖了我们集群里跑的所有业务。我们总共有三台物理宿主机每台上面跑三台虚拟机一共九台。这九台里有内部OA有MySQL主从有GitLab代码仓库有一台自己搭的RustDesk远程协助服务器还有一台文件共享网关。放在平时一两台VM出问题是很正常的重启一下或者回滚个更新就完事。但九台一起出问题而且症状各不相同这事就没那么简单了。业务方的电话很快打了过来问是不是又在割接网络领导也过来催恢复时间。我们几个运维围在工位前第一反应是先把故障面搞清楚哪些服务完全不可用哪些还能撑一阵哪些是连带反应。我让一个同事逐个去ping那九台VM的IP结果很有意思——有的通有的不通通的那些里面部分能登录部分登录到一半就卡死。这种半身不遂的表现非常不像纯粹的物理宕机或网络中断更像是什么东西在应用层和虚拟化层之间捣鬼。1.2 第一反应物理层还是网络层按照运维的常规思路先查物理硬件再查网络链路最后才碰虚拟化层。我打开三台物理机的带外管理界面也就是IPMI/BMC三台宿主全部在线电源、温度、风扇没有异常CPU和内存使用率也没有爆表。这时候心里稍微踏实了一点至少不是机房断电或者硬件集体烧了。接着看网络。核心交换机上各端口状态都是UP流量有波动但不算异常错误包和丢包统计也都是正常水平。再从一台正常物理机上traceroute到九台VM的网关路径干干净净的没有环路也没有黑洞。物理层面的嫌疑基本排除之后我登上了虚拟化平台的管理界面。就在这时真正让人冒冷汗的画面出现了。1.3 登录虚拟化平台一片血红的虚拟机列表虚拟化平台是开源的Proxmox VEPVE基于KVM我们用了很多年。登录进去集群状态那一栏直接显示红色部分节点心跳异常。再点开虚拟机列表九台VM的状态简直是群魔乱舞有的显示运行中但控制台黑屏有的显示已停止可进程还在宿主机上有的显示迁移中还有几台显示高可用服务恢复中。我刷新了一下页面这些状态还在变化像是有一群看不见的手正在把虚拟机在节点之间推来搡去。看到这个画面我心里反而定了——问题出在虚拟化集群的控制面而不是虚拟机的业务本身。控制面认为某些节点不健康于是开始执行隔离、迁移、重启的动作但因为判断依据是错的动作就变成了互相拉扯。接下来要做的是搞清楚控制面到底因为什么误判了节点健康状态。2. 排查过程一场从虚拟到现实的捉迷藏2.1 物理机全活着虚拟化层面却在内讧我先在宿主机上执行了virsh list --all确认九台VM的进程其实都还在。但奇怪的是部分VM的磁盘锁文件处于异常状态有的报Cannot acquire lock有的报Another QEMU process is running。这说明虚拟机底层的数据没丢只是被集群的锁机制给卡住了。虚拟化集群为了保证同一台虚拟机不会在两个节点上同时运行会在共享存储上写锁文件如果节点间的心跳判断出了偏差集群就认为某个节点已经失联于是尝试在别的节点启动同一台VM结果锁还没释放两边就撞车了。这种时候最忌讳的就是手动去virsh destroy或者virsh start硬重启。因为集群的HA管理器可能正在执行自己的重试逻辑你手动操作会跟它打架轻则启动失败重则把磁盘元数据弄坏。我们的策略是先观察收集日志搞清楚HA到底在干什么再决定要不要插手。2.2 集群事件日志里的心跳超时与隔离风暴我打开集群日志果然看到最近几分钟内有大量和心跳相关的记录Corosync lost token、Fencing node、HA service recover and migrate。这些术语看着吓人其实原理不难理解Corosync是PVE集群节点之间维持心心跳的组件每个节点会定期向其他节点报平安如果在一段时间内收不到对方的回应就判定对方故障触发fence动作也就是把故障节点隔离出去同时把上面运行的虚拟机迁移到其他健康的节点。这次的问题在于九台虚拟机全部处于被迁移的状态而且迁移来迁移去都落不了地。一是因为锁文件冲突二是集群里三个节点的心跳状态本身就互相矛盾。日志里甚至能看到某个节点刚被判定失联几秒钟后又恢复了心跳然后另一个节点又被判定失联。这种反复横跳在集群运维里非常典型——节点之间并不是真的失联而是它们互相看对方的依据出了问题。2.3 时间偏差藏在监控盲区里的小线索顺着心跳判断依据这个方向我怀疑到了节点间时间不一致的可能性。于是我在三台宿主机上分别跑了timedatectl和chronyc tracking这一跑把自己吓了一跳宿主机A的系统时间比标准时间快了6分多钟宿主机B慢了将近1分钟宿主机C倒是准的。也就是说三台物理机之间的最大时间偏差已经接近7分钟早就超过了集群默认的容忍范围。再进虚拟机里看发现部分VM的时间也偏得离谱而且不是一个方向地偏是有快有慢。有个同事说了一句原来不是服务器挂了是时间疯了。这句话说得很通俗但方向上完全正确。到这一步我已经基本确定这场集体罢工的核心就是时间同步链路出了问题。接下来要搞清楚的是时间偏差到底怎么引发这一连串故障的以及为什么我们的监控完全没有发现。3. 根因复盘一个时间源故障怎么就能让9台VM集体停摆3.1 虚拟化环境的时间同步链条有多脆弱先说结论我们内网有一台自建的NTP时间服务器所有宿主机和大部分VM的时间都指向它。上周五那台服务器的磁盘出了坏道服务进程已经悄悄死掉了但没有触发任何告警。更糟的是前段时间安全加固防火墙把到外部公共NTP的访问给拦了等于说内网再没有第二层时间来源。于是从上周五晚上开始宿主机和VM就一直在靠各自的硬件时钟硬撑。硬件时钟的漂移速度取决于设备晶振质量和使用环境通常一天慢几秒到几十秒是很常见的。我们这三台宿主机误差如比大是因为有的机器本身负载高、温度高有的还启用了虚拟机的主机时间同步功能错误的时间就像多米诺骨牌一样从宿主机传到了虚拟机里。等到周一早上宿主机A已经比标准时间快了6分钟多而它上面那些启用了跟随宿主机时间的VM自然也跟着偏出去了。至于其他VM有自己单独跑NTP客户端的反而误差小一些。这就解释了为什么九台VM的时间偏差有快有慢。3.2 时钟漂移引发认证雪崩与数据库分裂时间偏差达到6分钟以上对我们这种内部环境是致命的。第一个直接受影响的就是我们内网的统一认证体系。公司所有办公系统都接了Kerberos/LDAP域认证Kerberos协议对客户端和服务端之间的时间偏差有严格的容忍上限默认是5分钟。一旦偏差超过这个值客户端向认证中心申请的票据TGT就会被判定为无效表现就是用户明明输对了密码系统却一直报用户名或密码错误或凭据已过期。机房里的Linux服务器也大量依赖keytab认证访问跟着一起登不上。所有依赖域账号登录的OA、文件共享、运维跳板机瞬间全部失守。第二个受创的是数据库。我们内部业务用的是MySQL主从架构主库和备库之间通过半同步复制来确认数据写入。半同步复制有一个超时判断机制备库认为主库无回应和双方之间的心跳超时很大程度上和时间戳相关。时间偏差一大备库频繁误判主库失联触发自动切换可备库自己时间也不准切换过去之后新的主从关系又立刻出现异常于是角色在两个节点之间来回倒应用层的连接池也跟着全部失效。日志里全是failed to switchover和seconds_behind_master暴涨的记录。第三个受害者是各种基于TLS证书和JWT令牌的组件。GitLab、RustDesk Server、还有我们自己的Web网关它们在做HTTPS握手和令牌校验的时候会对比证书的签发时间、令牌的有效期和系统当前时间。系统时间快了6分钟很多令牌直接被判定过期或尚未生效服务端返回500错误或直接拒绝连接。RustDesk Server那台VM更惨它本身时间偏了导致所有客户端的TLS证书校验都失败内网远程协助工具全线掉线我们连远程排查的通道都少了一条。最后虚拟化平台自身的HA、存储锁、备份快照调度这些系统对时间同样非常敏感。时间一乱各种心跳超时锁超时就跟着出来了。3.3 为什么硬校时是事故中的第二个坑真相大白以后有个同事已经急着要执行ntpdate -u 192.168.x.x去强制同步时间了。我赶紧拦住了他。这里必须说一句在生产环境里最怕的就是硬校时也就是直接跳变系统时钟。系统时间在运行过程中突然往前跳或者往回拨几分钟会带来一大堆连锁反应运行中的进程记录的日志时间戳会混乱依赖单调时钟的算法可能出现ID回拨TCP的长连接定时器会瞬间触发超时数据库的binlog时间戳和复制心跳也会被搅乱。更夸张的情况是时间往回拨那简直是在给分布式系统上刑。这次事故里我们实际上已经踩到了这个坑的边缘。周末值班的同事发现宿主机时间偏了之后曾经执行过ntpdate -s强制同步但当时NTP源本身已经挂了同步失败等于白忙一场。如果当时NTP源是好的直接从一个错误的时间硬跳到正确时间业务侧多半会在那一瞬间出现大量连接中断等于把故障面又扩大了一层。所以正确的恢复思路不是越快校正越好而是在尽量小的影响范围内分步骤、有控制地让时间归位。这一点直接决定了后续应急操作的顺序。4. 应急处置从惊魂到恢复的分步实战4.1 止血先隔离再保关键业务恢复时间之前我们先把动作分了两步第一步止血第二步治本。止血要做的事情是把还在反复迁移、反复重启的VM从HA管理里暂时摘出去停止这种无意义的抖动。在Proxmox VE里可以针对具体虚拟机执行ha-manager set vmid --state disabled或者直接在管理界面把高可用服务暂停。注意这个操作不是把VM关掉只是让HA管理器不再干预它们的状态。摘除HA之后原来卡住的锁文件需要清理但一定要确认对应QEMU进程已经不存在再用virsh undefine之类的方式小心处理元数据锁。我们在处理的时候留了个心眼每摘一台VM之前都先去宿主上确认没有重复进程避免误伤。随后按业务优先级排序数据库那两台VM优先生效。MySQL主从环境里我们已经看到角色在反复切换先选一个时间相对准确的节点作为主库把另一个节点上的复制线程强制停止避免两边继续互相覆盖。业务侧则通过负载均衡临时把流量切到可用的只读节点或者干脆摘掉故障写入口先保住能读和能登录这两个底线。那台RustDesk Server因为证书时间校验的问题完全起不来也先停着大家直接用带外管理连物理机操作。4.2 修复时间同步链路先修源再逐级校准止血动作做完才开始治本。第一步是修好内网那台NTP服务器。我们过去检查发现服务进程早已退出磁盘挂载也异常重启服务之前先把文件系统修复了一下。服务拉起来后配置好从外部公共时间源同步同时把防火墙里UDP 123端口的NTP出口规则重新放开。等这台时间服务器自己先校准准确再让其他机器来找它同步顺序绝对不能反过来。接下来是宿主机。我们选择逐台校准而不是三台一起操作。具体做法是先停掉chronyd服务执行ntpdate -u 内网NTP服务器IP强制同步一次然后立即启动chronyd让它持续跟踪。之所以分台操作是因为如果三台宿主机在同一秒完成时间跳变那几秒钟内整个集群的网络会话、监控采集、数据库连接都会同时产生波动影响反而集中逐台来至少能让一部分服务保持稳定。每台校准完成后立刻执行chronyc tracking确认偏移量降到100毫秒以内再继续下一台。虚拟机层的逻辑类似但因为有些VM的时间是跟随宿主机的我们同步完宿主机以后再在VM里重启chronyd或ntp服务。这里有个细节KVM平台默认不建议开主机时间同步因为虚拟机的RTC在迁移和关机重启时会产生跳变正确的做法是让虚拟机自己跑NTP客户端。在这次事故里部分VM恰恰是开了跟随宿主机时间才被错误时间带偏的所以我们在恢复后把这些选项重新梳理了一遍。4.3 确认HA集群虚拟机和数据库恢复正常的清单时间校准完成后并没有立刻恢复HA管理而是先做了一次全面确认。我用下面这个清单逐项检查确认没问题后才把之前摘出去的VM重新纳入HA托管在每台宿主机上执行pvecm status确认三节点都在线没有节点处于disconnected状态执行corosync-cfgtool -s确认心跳令牌传递正常没有大量重传执行chronyc tracking和timedatectl确认所有节点偏移量都在正常范围执行virsh list --all确认VM不会反复启动或迁移进入MySQL主库执行show slave status\G确认Seconds_Behind_Master归零复制链路重新建立用域账号实际登录OA和文件共享系统确认Kerberos认证恢复登录RustDesk Server确认TLS连接恢复远程协助软件能重新接入。整个确认过程花了大约40分钟比真正的时间校准还久。业务恢复初期还是有一些零散报错主要是之前积压的失败任务和连接超时导致的清理了一波进程到中午之前全部恢复正常。那三个小时里最消耗人的其实不是技术操作而是看不到头的不确定性。5. 事后复盘虚拟化集群时间管理的几条保命建议5.1 时间源要冗余权限要收敛这次事故的第一个教训就是时间源不能只有单一节点。内网NTP服务器挂掉之后整个时间同步链条就断了而且没有任何告警。现在我们的方案是至少保留两个互为备份的时间源一台自建NTP服务器作为主源另一台直接指向外部公共NTP池作为备用源。所有宿主机和VM的chrony配置里都同时写上主备两个源chronyd本身会在主源失效后自动切换。同时对NTP服务器的管理权限做了收敛只有两名资深运维能上去改配置避免再出现周末好心同学强制校时的操作。5.2 监控里必须加时间偏移指标这次事故最讽刺的是我们监控了CPU、内存、磁盘、网络唯独没有监控时间。时间偏移这种指标看起来不起眼但它在虚拟化、数据库、分布式存储这类场景里往往是最先出问题的信号。现在我们在所有宿主机和关键VM上都配置了时间偏移监控用Prometheus的node_exporter采集node_time_seconds与本地标准时间比对设置5秒即告警。Zabbix用户也可以通过监控项采集NTP状态道理一样。别只监控宿主机VM也要纳入因为很多错误时间会从虚拟化平台复制到VM里。5.3 应急预案里要有时间故障专属章节以前我们的应急手册里只有网络故障、存储故障、虚拟机故障这些章节没有专门写时间同步异常的处理流程。这次之后我们把时间类故障单独列了一章明确写了几个关键原则严禁直接date -s跳变时间禁止在多台机器上同时大规模硬同步必须先恢复时间源再逐级校准NTP源失效超过一定时长必须走变更流程评估对Kerberos和数据库的影响后再动手。这份预案在两个月后的一次演练中真派上了用场整个处理过程比这次从容很多。5.4 虚拟化平台的时间同步策略要明确最后一条建议针对虚拟化平台本身每台VM的时间同步策略是跟随宿主机还是自己跑NTP必须事前想清楚。我们现在的统一原则是宿主机负责主机层面的时间同步VM内部一律自己配置NTP客户端除非极个别场景否则不开启主机时间同步。因为在虚拟化环境里宿主机和VM之间的时间关系本来就容易被忽略而一旦宿主机时间出错错误会沿着虚拟化链路迅速传播最后变成一批VM集体时间错乱的场面。明确这个策略至少能保证一个问题只出现在一个层面而不是上下两层一起炸。6. 时间相关故障排查速查表6.1 常见症状与排查方向把这次的经验整理成一个速查表方便遇到类似问题的时候快速定位。表格里列的症状每个都对应着我们这次实际遇到的场景故障现象可能的根因方向优先排查多台VM同时能ping通但应用无法登录Kerberos/LDAP认证时间偏差超限对比VM和KDC的系统时间数据库主从反复切换、复制中断主备库心跳超时、半同步复制超时检查主备库时间偏差HA集群节点心跳失联、虚拟机反复迁移控制面误判节点健康检查所有节点系统时间偏差HTTPS/TLS连接瞬间大量失败证书时间校验失败查看服务端系统时间和证书有效期定时任务、备份任务乱序执行系统时间跳变或漂移对比标准时间和任务触发时间虚拟化平台报锁文件冲突集群锁超时检查节点间时间偏差和心跳状态排查的第一步永远是timedatectl和chronyc tracking开道这两条命令几十秒就能出结果。如果发现时间偏移超过一秒立刻把它当成头等嫌疑不要等它自己恢复。6.2 常用排查命令速查下面这些命令是我在生产环境里实测下来最有用的都顺手记在这里# 查看系统时间和时区 timedatectl # 查看NTP同步状态和偏移量chrony chronyc tracking chronyc sources -v # 查看NTP同步状态老系统用ntpd ntpq -p # 查看集群心跳状态Proxmox VE pvecm status corosync-cfgtool -s # 查看虚拟机列表和状态KVM virsh list --all # 手动读取标准时间源不直接修改时间先确认差了多少 ntpdate -q NTP服务器IP # 强制同步时间仅在确认影响可控、业务低峰且得到授权后使用 systemctl stop chronyd ntpdate -u NTP服务器IP systemctl start chronyd chronyc tracking有一点要再强调一遍上面最后那个ntpdate -u强同步操作在生产环境里属于有风险动作执行前确认影响面执行时尽量逐台操作执行后观察连接和日志的波动。如果偏差不大更好的做法是让chronyd自动渐进校正或者执行chronyc makestep在允许的范围内一次性校正而不是用date -s去手动改系统时间。写在最后的一点体会我个人的体会是这次事故最值钱的教训不是把时间修好了而是再也不敢让时间源处于无人监管状态。虚拟化让服务器的生死变成了软件层面的状态切换也让故障变得像障眼法一样难以看穿。九台虚拟机集体罢工物理服务器却一切都好这种割裂的现场感只有亲手经历过才懂。后来我们做的改进其实都很简单时间监控加上了、NTP源做了冗余、应急预案补了章节、同步策略统一了。现在我每次进机房巡检都会顺手跑一句chronyc tracking看偏移量就像出门摸钥匙一样自然。这个习惯是那次惊魂给我留下的最好纪念。
阅读完成 · 觉得有帮助?