news 2026/10/15 0:31:00

OpenStack多版本混布导致nova-compute启动卡死的排查与修复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenStack多版本混布导致nova-compute启动卡死的排查与修复

说实话,OpenStack环境里最磨人的故障,不是那种直接狂刷ERROR的,而是进程明明活着、却什么也不干的类型。我今天要聊的,就是这个标题里的场景——openstack多版本部署导致nova-compute启动卡死。我前后在几套环境里碰到过类似情况,表现形式基本一致:计算节点上systemctl status openstack-nova-compute显示 active,进程也在,但宿主机始终加不进新虚拟机,控制节点看计算资源一直处于异常。它不像崩溃那样有一个明确的退出码,也不像配置错误那样启动几秒内就报错。就是卡住,没有任何有效日志,让人无从下手。

这类问题的坑在于:它不会主动告诉你“我因为版本不匹配所以起不来了”,而是把所有异常都吞在内部重试里,以一种极其静默的方式消耗你的排查时间。这篇文章就围绕这个故障类型,从现象、根因、排查链路到修复方案完整走一遍,结合我自己的实操经验,尽量把每一步为什么这么做、卡在哪、怎么验证都讲透。适合正在做OpenStack版本升级、混合部署、或者时不时遇到“服务活着但实际不可用”问题的运维、平台开发和架构师参考。

1. 现象复盘:一个毫无报错的“卡死”

1.1 现场情形:服务状态与表象

先说说我遇到的典型场景。一次版本升级过程中,控制节点和部分计算节点已经升到新版,但还有几个计算节点因为资源池排期问题没来得及升级,处于旧版本。就在这个“混合运行”的阶段,某一个旧计算节点的 nova-compute 服务被重启过——可能因为是内核更新、断电恢复、或者有人手动重启——然后它就再也回不来了。

从表面看,systemctl status显示active (running),进程 PID 存在,端口也在监听。但你去控制节点执行openstack compute service list,会看到该计算节点处于down状态,nova hypervisor-list里也看不到它。更诡异的是,你登录到该计算节点,直接手动执行nova-compute的前台启动命令,日志停在一个“看起来很正常”的地方就不动了,没有报错,没有退出,Ctrl+C 才能结束。

1.2 卡死与崩溃的本质区别

崩溃类故障有一个明确退出码,系统会把错误堆栈打出来,定位起来相对快。卡死则完全不同,它意味着进程的主线程还在,但被某个等待条件阻塞住了,可能是等待网络返回、等待数据库锁、等待消息队列回包。这种等待如果没设超时,或者重试机制有问题,就会无限持续下去。

在多版本部署环境下,nova-compute 卡死的特殊性在于:它往往不是操作系统层面的问题,而是OpenStack各组件之间版本协商失败后,代码进入了“重试—失败—再重试”的循环。这个循环之所以看起来像卡死,是因为重试间隔会指数退避,时间越拉越长,最长可能达到几分钟甚至几十分钟。你观察它的表象时,恰好处于一个漫长的退避等待窗口里,自然看不到任何日志输出。

2. 为什么多版本混布会精准卡住nova-compute:根因拆解

2.1 nova-compute启动期的关键依赖链

要理解卡死点,得先搞清楚 nova-compute 启动时都干了什么。简化后的启动链路大概是这样的:

  1. 解析/etc/nova/nova.conf与 rootwrap 配置,加载 oslo.config 配置对象;
  2. 初始化 oslo.messaging,创建与 RabbitMQ 的长连接,声明自己的 topic 队列;
  3. 连接数据库,查询 cell mapping、service 记录,拿到本节点的 UUID 和身份信息;
  4. 初始化 virt driver,比如 libvirt driver,连上本机 qemu;
  5. 向 placement 服务注册 resource provider,并上报各类 inventory 资源;
  6. 启动周期任务与 servicegroup 心跳,向 conductor 注册服务能力。

多版本混布环境下,以上任何一步都可能因为版本不匹配而卡住。但根据我的排查经验,真正高发且具有“静默”特征的主要集中在三个环节。

2.2 三个最容易形成“静默阻塞”的环节

第一个是 RPC 队列声明冲突。旧版 nova-compute 使用的 oslo.messaging 版本,与新版控制节点上声明的 exchange、queue 元数据不一致时,RabbitMQ 会拒绝 queue 声明请求。oslo.messaging 内部对这类异常的处理逻辑是重试,而不是直接抛错。如果恰好遇到队列被其他版本组件以不同参数声明过,旧进程会陷入反复声明、反复被拒绝的循环。

第二个是 placement API 接口版本不匹配。nova-compute 启动时会上报 resource provider 的 inventory,这个动作依赖 placement 的 REST API。新版 placement 对请求体中的字段要求更严格,比如要求inventories里必须携带保留字段、要求 PUT 请求必须判空等。旧版 nova-compute 发送的请求格式不满足新版要求,placement 会返回 400 或 409。nova-compute 的 HTTP 客户端在收到这类响应时,会进入连接池重试逻辑。问题在于 reconnection 的退避时间很长,而且不会向日志写入一条明确的“placement 返回 400”记录。

第三个是数据库 schema 迁移不一致。控制节点升级时,通常会在共享数据库中执行nova-manage db sync,将 schema 升到新版本。此时旧计算节点上的 nova-compute 代码去查新表结构时,会遇到新版本才有的列或约束。特别是查询时如果涉及到新加的 NOT NULL 列,数据库直接报错,而 oslo_db 的会话重试机制会在后台反复建立新连接,表现就是进程卡在建连接池和重试的路上。

2.3 最容易中招的三种多版本场景

根据我遇到和帮朋友排查过的案例,以下三种场景最容易触发 nova-compute 卡死:

  • 滚动升级中间态:一部分计算节点已经升级到新版本,另一部分还是旧版本,且头尾相差两个release以上。这种情况下,旧计算节点与新版conductor之间的RPC接口兼容性就已出现风险。
  • 控制节点先升级、计算节点长期滞后:这是最容易忽略的。控制节点和数据库升级后,旧计算节点里的nova代码在启动时大量依赖新版schema和新版placement接口,卡死概率最高。
  • 版本回退后schema残留:升级后发现新版不稳定,把计算节点回退到旧版本,但数据库已经跑过新版迁移脚本。旧代码启动时遇到不认识的字段或表,就可能陷入数据库重试泥潭。

3. 复现排查链路:从日志尾部到线程栈再到边界服务

3.1 第一步:定位最后一条有效日志

排查卡死问题,第一件事不是看配置,而是看日志最后到底停在哪。这一步的目标是缩小阻塞范围,判断是卡在网络初始化、数据库连接、还是virt driver加载。

tail -200 /var/log/nova/nova-compute.log

实战中我看到的典型“最后一条日志”有几种:

2024-XX-XX 10:23:45.123 INFO nova.compute.manager [req-xxx] Loading compute driver "libvirt.LibvirtDriver"

或

2024-XX-XX 10:23:50.456 INFO nova.compute.manager [req-xxx] Connecting to neutron

或干脆没有任何新日志,只有很早期的Starting nova-compute这一行。

日志停在不同位置,对应的嫌疑点不同。停在“Loading compute driver”之前,大概率卡在RPC或数据库初始化阶段;停在“Connecting to neutron”附近,则多半是neutron接口版本协商出了问题。这一步不需要做任何修改,只是帮自己划定一个“从哪开始挖”的范围。

3.2 第二步:抓取线程堆栈确认阻塞点

日志不是全部真相。很多时候进程卡住的位置藏在线程栈里,尤其Python服务,靠日志判断可能完全错方向。这里我强烈推荐py-spy,它能直接dump出Python进程的线程栈,不用重启服务不打断进程。

pip install py-spy py-spy dump --pid $(pgrep -f nova-compute | head -1)

输出会列出所有线程当前的调用栈。重点关注处于waiting、sleeping、locked状态的线程,尤其是主线程和周期任务线程。

我遇到过几个非常有代表性的栈:

  • 栈底部停在oslo_messaging._drivers.impl_rabbit._consume,说明卡在RabbitMQ消费/重连逻辑;
  • 栈底部停在requests.sessions.Session.send配合urllib3.util.retry.Retry,说明卡在API请求重试;
  • 栈底部停在oslo_db.sqlalchemy.enginefacade._transaction_scope配合QueuePool,说明卡在数据库连接池等待。

看到这些栈基本就能把问题定性了:不是“系统假死”,而是在等待某个外部依赖;再结合第一步日志位置,就能锁定到底是哪个外部依赖。

3.3 第三步:横向检查conductor、placement与消息队列

确认阻塞方向后,需要到边界服务侧做交叉验证。比如怀疑卡在RabbitMQ,就去查队列状态和连接来源;怀疑卡在placement,就去看placement API的访问日志;怀疑卡在conductor,就去查conductor是否有处理该请求的记录。

检查RabbitMQ队列和消费者:

rabbitmqctl list_queues name consumers messages

对比正常节点和卡死节点的队列数量。多版本场景下,常见异常是旧计算节点所需的队列没有消费者,或者队列元数据与正常节点不一致。还可以直接看RabbitMQ的连接列表,确认这个计算节点的连接是否处于blocked状态。

检查placement侧:

journalctl -u apache2 -u httpd | grep -i resource-provider

或直接手动模拟一次上报:

curl -i -X PUT \ -H "Content-Type: application/json" \ -H "X-Auth-Token: <token>" \ -d '{"name":"test","uuid":"<uuid>","parent_provider_uuid":null}' \ https://<placement-endpoint>/placement/resource_providers

如果返回400或409,而正常节点返回201,基本可以确认是API版本协商出问题。

3.4 第四步:数据库schema与版本一致性校验

最后一步,也是最能一锤定音的,就是核对所有相关组件版本和数据库迁移版本。

# 查看各节点nova版本 nova-manage version pip show nova 2>/dev/null || rpm -q openstack-nova-compute # 查看数据库schema版本 nova-manage db version nova-manage api_db version

我遇到过一次非常经典的例子:控制节点nova-manage db version显示database migration is up to date,但某个计算节点上pip show nova显示的是低于该schema版本对应的代码。新旧代码混跑,新库旧码,角色错位一目了然。这一步做完,基本就能下结论了。

4. 修复实操:让nova-compute恢复正常启动的完整方案

4.1 场景A:计算节点版本滞后,直接对齐版本

如果你确认是计算节点版本滞后导致的问题,最省事的方案就是把它升级到与控制节点一致的版本。注意,这里要统一的不只是nova主包,还有它的依赖库。

以某次我用源码安装方式排障的过程为例:

# 在计算节点上停服务 systemctl stop openstack-nova-compute libvirtd # 从控制节点导出完全相同的nova版本信息 pip freeze | grep -E "^(nova|oslo|neutron|placement)" > /tmp/req.txt # 在计算节点上安装对齐版本 pip install -r /tmp/req.txt --upgrade systemctl start libvirtd openstack-nova-compute

源码安装环境通常更多依赖库版本,比如oslo.messaging、oslo.db、openstacksdk这些都要一起对齐。如果是RPM部署,则:

yum install openstack-nova-compute-<版本号> --enablerepo=xxx

升级后别急着看服务状态,先观察日志尾部是否出现了新的初始化进展。我这里给个建议:用 journald 和日志文件双通道同时跟踪,因为systemd服务在前台日志可能被吞掉一部分。

journalctl -u openstack-nova-compute -f & tail -f /var/log/nova/nova-compute.log

只要日志开始出现“Loading compute driver”“Connecting to neutron”之后的下一步,说明阻塞解除。

4.2 场景B:无法立即统一版本时的临时拉起方案

现实项目中经常有“现在不能全部升级,但服务得先跑起来”的尴尬。如果没法立刻统一版本,可以尝试用配置参数和故障隔离手段把服务先拉起来。

首先,把RPC超时时间调大,避免启动期的正常慢速协商被误判为超时中断:

[DEFAULT] rpc_response_timeout = 300 [oslo_messaging_rabbit] rabbit_retry_interval = 1 rabbit_retry_backoff = 2 rabbit_max_retries = 0

其次,清理掉该节点在RabbitMQ里残留的队列。多版本场景下,旧队列可能被新控制节点用不同参数声明过,导致队列规范冲突。建议先禁用compute服务,再手动清除队列:

openstack compute service set --disable <host> <nova-compute> rabbitmqctl list_queues | grep "compute.<host>" rabbitmqctl delete_queue compute.<host>.<uuid> systemctl restart openstack-nova-compute openstack compute service set --enable <host> <nova-compute>

注意:删除队列前必须确认该队列没有其他节点正在消费,否则会影响该节点上的存量实例心跳上报。保守做法是先把服务禁用再删队列,恢复后再启用。

如果卡在placement API版本协商,还可以在 nova.conf 里指定placement端点时,使用带/placement显式路径的 internal 接口。部分场景下旧版nova-compute通过service catalog拿到的placement URL格式不对,需要手动指定:

[placement] auth_url = https://<keystone>/v3 region_name = RegionOne valid_interfaces = internal timeout = 60

这套临时方案的目标不是消除版本差异,而是绕过导致卡死的等待点。它有一定的风险,比如存量实例迁移、疏散、节点维护等操作期间可能遇到不一致行为,所以只能作为短期应急手段,长期仍要回到版本统一这条路。

4.3 场景C:版本回退产生的schema残留清理

如果你经历了“升级—回退”的过程,那么问题往往不只是代码版本不匹配,数据库里还残留了新版schema。旧代码启动时可能因为查询了新列、新表而反复触发数据库异常。

处理这个场景的原则是:先确认该计算节点是否还有实例需要保留。如果有,先迁移或疏散实例;如果没有,可以直接清掉service表记录,让nova-compute当成全新节点加入。

# 查看计算节点对应的service id openstack compute service list --host <host> # 删除该计算节点的service记录 openstack compute service delete <service-id>

之后清理数据库里与该节点相关的残留resource provider数据:

# 在控制节点上,进入nova_api库 mysql -u root -p nova_api DELETE FROM resource_providers WHERE uuid = '<node-uuid>'; DELETE FROM inventories WHERE resource_provider_id = <id>;

再重启nova-compute,它会把该节点视为一个新的计算节点,重新创建resource provider并上报。这样做的代价是原有节点上以resource provider维度保存的一些资源记录会消失,但正常情况下不影响存量实例计数,因为实例属于cell数据库,不在nova_api库。

如果数据库整体迁移版本和代码版本存在系统性偏差,就必须做更严苛的处理:先用备份恢复数据库到迁移前状态,再按正确顺序重新执行迁移。

# 控制节点操作,务必先在维护窗口执行 mysqldump nova_api > /tmp/nova_api_backup.sql mysqldump nova_cell0 > /tmp/nova_cell0_backup.sql

恢复后重新执行:

nova-manage api_db sync nova-manage db sync nova-manage db online_data_migrations

这里我踩过一个坑:只记得恢复nova_api,忘记了nova_cell0和cell数据库。结果控制节点正常,计算节点依旧起不来。所以备份恢复时,所有nova相关的库都要一起处理,cell库尤其关键。

5. 防复发:多版本升级场景下如何安全部署不踩坑

5.1 升级顺序与组件依赖版本矩阵

nova-compute卡死这件事,本质上不是“计算节点自己”的问题,而是整个OpenStack控制面和计算面版本同步性的问题。所以防复发的手段,也应该从架构和流程层面处理,而不是只修那一台机器。

多版本升级时,必须坚持“先控制面,后计算面”的滚动顺序。控制节点先把nova-api、nova-conductor、nova-scheduler、placement全部升级完,数据库迁移跑完,确认线上无异常再开始动计算节点。计算节点最好按资源池分批升级,比如每次只升一个集群的几个节点,观察一段时间再继续。

这里给一张我常用的升级版本检查表,每次升级前逐项比对:

检查项命令/方法通过标准
nova代码版本nova-manage version所有节点显示一致
数据库迁移状态nova-manage db version与控制节点一致
依赖库版本pip freeze/rpm -qaoslo.messaging等关键依赖一致
配置差异diff /etc/nova/nova.conf除host、ip外无差异
placement接口可用性手动curl上报返回正常HTTP码
neutron扩展协商nova-compute日志正常打印已加载扩展列表

5.2 配置版本与配置文件一起管

除了二进制包版本,配置文件的版本差异同样会造成启动阻塞。我见过最隐蔽的情况是:计算节点上的nova.conf里还有一个旧版本的transport_url,指向一个早已被回收的RabbitMQ集群;控制节点新版里已经切到新集群。旧计算节点启动时连不上旧MQ,消息队列连接一卡就是几分钟。

所以强烈建议把配置文件和部署脚本一起纳入版本管理。用Ansible等配置管理工具统一下发nova.conf,不要在节点上手动改配置。在group_vars里显式声明版本变量:

nova_version: "2023.1" rpc_transport_url: "rabbit://nova:****@mq01:5672,mq02:5672" placement_endpoint: "https://controller/placement"

这样每次升级,配置变更跟随代码变更一起发布,降低因为节点间配置漂移引发的启动问题。

5.3 关键超时与重试参数的设置

多版本环境里,不设超时或者超时设置得极短,都会放大卡死问题。超时太长会掩盖真实故障,超时太短则会让正常的协商过程被误杀。我自己在混合版本期间用的参数如下:

[DEFAULT] rpc_response_timeout = 300 rpc_conn_pool_size = 30 [oslo_messaging_rabbit] rabbit_retry_interval = 1 rabbit_retry_backoff = 2 rabbit_max_retries = 0 [placement] timeout = 60 [database] retry_interval = 5 max_retries = 10 connection_recycle_time = 600

rabbit_max_retries = 0表示无限重试,配合指数退避可以让偶发网络问题时服务自动恢复。但要注意,无限重试在多版本场景下也可能掩盖真正不可恢复的版本冲突。所以这个参数更适合已经确认“版本没大问题,只是网络抖动”的时候用。

5.4 事后验证与常态化巡检

修复完成后,不能只盯一次启动成功就完事。nova-compute启动成功和完全可用之间还有一段距离,需要在控制节点侧做几个验证:

# 检查计算服务是否已UP openstack compute service list # 确认hypervisor资源已经上报 openstack hypervisor list openstack hypervisor show <host> # 尝试在该节点创建一台测试虚拟机 openstack server create --flavor m1.small --image cirros --availability-zone nova:<host> test-vm

除此之外,建议把版本一致性检查做成定时任务。比如每周执行一次脚本,自动比对所有计算节点的nova-manage version和控制节点的差异,差异大于一个小版本就告警。多版本混布最大的风险不是“有差异”,而是差异存在了很长时间而没人发现,最后一次偶发重启直接把问题引爆。

多版本部署本身不是错,滚动升级也不可避免会有一段混合期。真正危险的是没有意识到混合期各组件的兼容边界,没有给comute节点一个“如果协商失败该怎么办”的兜底策略。这篇内容里的排查方法、修复命令和参数建议,都是我实际用过且验证有效的,希望能帮你少走几趟弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 0:30:48

傅里叶梅林变换MATLAB代码:旋转缩放配准的工程实现与避坑指南

简介&#xff1a;这份MATLAB参考代码围绕傅里叶梅林变换展开&#xff0c;面向图像处理、计算机视觉方向的学习者与研究者&#xff0c;用于解决图像旋转不变性分析与配准问题。资源包共6个文件&#xff0c;以5个bmp测试图像和1个m脚本为主&#xff0c;压缩包约152KB&#xff0c;…

作者头像 李华
网站建设 2026/10/15 0:13:43

PLC+触摸屏+审计追踪:制药级控制系统技术拆解

PLC触摸屏审计追踪&#xff1a;制药级控制系统技术拆解同样叫"PLC 控制"&#xff0c;有的设备几千块的控制箱&#xff0c;有的却是能过 FDA 审计的完整系统——价格差十倍&#xff0c;能力差一个时代。控制系统是设备的"大脑"&#xff0c;也是 GMP 数据完整…

作者头像 李华
网站建设 2026/10/15 0:12:49

技术面试图论高频考点:从存储选型到Dijkstra实战避坑指南

1. 图结构在面试中的真实定位聊到图这个数据结构&#xff0c;很多人的第一反应是"难"。难在哪&#xff1f;不是概念难&#xff0c;是面试官一旦往图的方向出题&#xff0c;往往意味着他想考察的东西比数组、链表、栈队列要深得多。数组和链表考的是基本功&#xff0c…

作者头像 李华
网站建设 2026/10/15 0:07:28

064、广播数据长度超出承载上限时的分包处理

现场&#xff1a;一条永远发不出去的广播 几年前做一个分布式采集系统&#xff0c;主控通过广播通道向所有节点下发同步命令和参数。协议栈的单帧净荷上限是 31 字节&#xff0c;这是链路层硬约束&#xff0c;改不了。前期功能很简单&#xff0c;同步命令就几个字节&#xff0c…

作者头像 李华
网站建设 2026/10/14 23:59:52

SpringBoot+Vue3社区养老服务平台全栈项目实战解析

1. 项目背景与整体技术选型近两年社区养老信息化一直是个热门的落地方向&#xff0c;特别是“人口老龄化社区服务与管理平台”这种系统&#xff0c;典型的SpringBoot 2 Vue 3组合&#xff0c;配合MyBatis-Plus和MySQL 8.0&#xff0c;基本就是Java Web全栈项目的标准答案。我前…

作者头像 李华
网站建设 2026/10/14 23:59:48

从核算到价值:财务转型五阶段路径与实操指南

做财务转型项目这些年&#xff0c;我最常听到的一句话是&#xff1a;“我们财务天天加班&#xff0c;老板还嫌我们不创造价值。”这句话听起来扎心&#xff0c;但确实点到了传统财务部的命门。核算型财务的典型状态是&#xff1a;月底结账、出报表、跑税务、审报销、核往来&…

作者头像 李华