计算节点上nova-compute服务启动异常,可能是OpenStack运维里遇到频率最高的故障之一。这个服务一旦起不来,云平台的计算面就直接断了一半:虚拟机调度排队、冷热迁移全部卡住,线上用户的工单瞬间堆满。而且它的毛病往往不像网络故障那样一目了然,日志里藏着的根因五花八门——配置文件、消息队列、数据库、磁盘、时钟,甚至libvirt的连接都能成为压垮它的最后一根稻草。
这篇文章不是拿一套“标准答案”应付你,而是把我在实际运维中处理nova-compute启动异常时真正用到的排查链路和方法写出来。你会看到怎么一步步定位问题,不同故障类型各自的表现特征和处理方式,以及一个完整的真实排障案例。无论你是刚接触OpenStack的新手,还是已经管着几十个计算节点的老手,这套思路都可以直接拿去用。
1. nova-compute启动失败的第一现场:日志定位与状态确认
遇到计算节点服务异常,最忌讳的是上来就改配置、重启一堆服务。冷静下来先确认三件事:服务当前处于什么状态、日志最后说了什么、这个节点上最近发生过什么(是否重启过、是否改动过配置、是否有人动过目录权限)。
1.1 三步锁定服务状态
在基于systemd的发行版上(CentOS/RHEL为主,Ubuntu的upstart时代已经很少见了),三条命令最有用:
bash复制systemctl status nova-compute
systemctl list-units | grep nova
journalctl -u nova-compute -n 200 --no-pager
systemctl status 的输出会明确指出服务处于 active (running)、active (exited) 还是 failed 状态。如果是failed,通常会有 Process: xxx ExecStart=... 一行提示退出码。journalctl -n 200 看的是最近的系统日志,但要注意journal有时会被systemd的限额截断,如果关键报错不在最后200行,可以加上 --since "10 minutes ago" 缩小范围,或者把行数放大到1000。
很多新手会直接打开 /var/log/nova/nova-compute.log,但这里有个细节:这个文件记录的是nova-compute进程内部的运行轨迹,而服务能不能在系统层面起来(配置文件权限、Python包导入失败、端口被占用),在journal里才看得到。所以两个日志要配合看,不能只盯一个。
1.2 区分“起不来”的两种形态
- 进程根本没拉起来:systemd显示failed,日志里往往是
import error、config parse error、OSError这类,问题出在环境和配置层面。 - 进程起来了但反复退出:呈现典型的 crash loop,日志里反复出现TRACE后进程退出,再被systemd拉起。这类往往是nova自身逻辑抛异常,更依赖
nova-compute.log里的TRACE段来确定根因。
区分清楚这两种形态,排错方向完全不同。前者往下检查依赖和配置语法,后者往上去看nova内部逻辑与外部服务(消息队列、数据库、libvirt)的连通性。
我自己的习惯是,第一轮先不深入看任何细节,而是给服务连续采集三轮状态:
bash复制for i in 1 2 3; do systemctl status nova-compute --no-pager | head -20; sleep 10; done
如果每次显示的时间都在变化,基本可以断定是crash loop;如果完全没变化,多半是进程僵住或启动即退出。这个信息决定你接下来是去翻配置还是去查环境。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 配置层故障:语法、权限与nova.conf的“隐性陷阱”
在排除“完全没有进程”的系统级问题之后,配置层是故障高发区。nova.conf的配置项极其庞大,但80%的启动失败都和下面几个点有关。
2.1 配置文件本身:可读性、语法与权限
先说最直接的。nova-compute进程以nova用户运行,如果nova.conf被误改成了root所有、权限600,nova用户读不到,启动必然失败。journal里会看到 PermissionError。检查方式很简单:
bash复制ls -l /etc/nova/nova.conf
正常情况应该是 root:nova 或 nova:nova,权限至少要让nova组可读。这个坑在有人用 cp 备份后覆盖原文件时特别容易踩——备份文件保留的是原用户,但覆盖后属主可能变成操作者的账号。
配置文件语法方面,OpenStack的ini格式虽然简单,真正的坑在两级section嵌套。比如 [libvirt] virt_type = kvm 不能写成 [DEFAULT] virt_type = kvm,一旦写错,libvirt驱动初始化时读到的就是默认值或空值。一个常用的快速检查方法是用Python自带的configparser加载一次:
bash复制python3 -c "import configparser; c=configparser.ConfigParser(); c.read('/etc/nova/nova.conf'); print(c.sections())"
能正常打印sections说明语法层面没大问题。但要注意:configparser不检查配置项名称是否合法。nova对未知配置项一般会警告而不是报错,真正会让你起不来的是必填项缺失或值格式错误。
2.2 启动期必读的几个配置项
nova-compute启动时会主动读取并校验一批配置,缺了或者写错,直接退出。我排障时必查这几项,它们也是社区帖子里出现频率最高的:
[DEFAULT] my_ip:计算节点管理IP。如果配成网关IP或一个不存在的IP,启动时nova会尝试探测该地址,报错比较典型的是Unable to determine server address。[vnc] vncserver_listen/[vnc] vncserver_proxyclient_address:前者不能配成管理网段之外的地址,否则nova-compute启动时尝试绑定该地址会直接失败,报Address already in use或Cannot assign requested address。很多人在多网卡节点上栽在这里。[DEFAULT] state_path/instances_path:nova需要在这两个路径下创建instances目录存放虚拟机磁盘。如果对应文件系统不存在、只读、或没有写权限,启动直接失败。日志里的典型报错是PermissionError: [Errno 13] Permission denied: '/var/lib/nova/instances'或FileNotFoundError。[libvirt] virt_type:取值必须和当前机器架构匹配。x86_64机器上配virt_type = qemu不算错,但如果你把virt_type = kvm配在无法访问/dev/kvm的环境里,libvirt连接阶段就会报错。如果你在尝试qemu部署多架构虚拟机(比如x86宿主机上跑ARM模拟实例),这个参数和CPU型号配置会变得极其敏感——我自己的测试环境里就遇到过virt_type与cpu_mode组合不当导致nova-compute无法初始化libvirt连接的情况。
快速定位配置类错误的方式,手动以nova用户前台运行nova-compute,让完整错误直接打到终端:
bash复制su -s /bin/sh nova -c "nova-compute --config-file /etc/nova/nova.conf --debug"
这个命令在前台运行,如果退出很快,错误就在眼前,比在journal和日志文件里来回翻要直观得多。注意先停掉systemd服务再手动跑,避免端口和锁冲突。
3. 环境依赖故障:消息队列、数据库与libvirt的联动失败
配置看起来没问题,nova-compute仍然起不来,就要往下游依赖排查。nova-compute虽然跑在计算节点上,但它依赖控制节点的消息队列和数据库,任何一个连不上,启动阶段就会抛异常。
3.1 消息队列连不上导致注册失败
nova-compute启动时要向消息队列注册并接收调度指令。日志里常见的报错是:
log复制AMQP server on 10.0.0.5:5672 is unreachable: timed out
这段一般来自oslo_messaging,处理起来分四步:
- 确认计算节点到消息队列节点的网络连通性:
telnet 10.0.0.5 5672或nc -zv 10.0.0.5 5672。 - 核对账号密码:检查
/etc/nova/nova.conf里的[oslo_messaging_rabbit] rabbit_userid和rabbit_password是否与控制端一致。 - 在控制节点执行
rabbitmqctl list_queues,确认RabbitMQ服务本身正常、queue没有被错误清掉或堆积异常。 - 如果使用了vhost,确认vhost存在。不少部署脚本会把vhost写成
/nova,实际却只在/下建队列。
还有一个容易被忽略的:消息队列节点时钟和计算节点时钟偏差过大,AMQP的heartbeat机制会判定连接失效,服务端主动断开连接。这个放到后面资源型故障里细说。
3.2 数据库连接失败导致初始化终止
nova-compute启动时会初始化数据库连接。报错形态通常是:
log复制oslo_db.sqlalchemy.exc.RetryRequest: DBError: connection is closed
或者:
log复制OperationalError: (2003, "Can't connect to MySQL server on '10.0.0.4'")
控制节点数据库如果是MariaDB/MySQL,要检查nova库是否已经初始化、nova用户是否被正确授权、[database] connection 里的IP是否是控制节点实际IP。新版用 nova-status check 可以快速验证nova相关的数据库层状态。
一个经验:很多数据库连接失败其实是防火墙导致的。计算节点到数据库端口(默认3306)的连通性平时没人主动测,等nova-compute启动失败时才想起来。所以排查时先确认 nc -zv 10.0.0.4 3306,别急着改数据库授权。
3.3 libvirt连接异常
libvirt问题在计算节点上非常典型。nova-compute通过python的libvirt模块连接本机的libvirtd,报错常见有:
log复制libvirtError: internal error: no supported architecture for os type 'hvm'
libvirtError: Cannot connect to hypervisor URI 'qemu:///system'
逐步处理:
systemctl status libvirtd是否正常,libvirtd挂了nova-compute必然起不来。/var/run/libvirt/libvirt-sock是否存在且nova用户可访问。- 虚拟化是否真的可用:
ls /dev/kvm。机器上根本没有KVM但virt_type配成kvm,连接时就会报架构不支持。在多架构虚拟机实验场景里,qemu模拟的ARM机器上/dev/kvm不存在,必须配virt_type = qemu才能过这一关。 - 内核是否加载kvm模块:
modprobe kvm_intel看是否报错。
这里有个实用技巧:在计算节点上先用命令行验证libvirt本身没问题,再谈nova。直接执行:
bash复制virsh -c qemu:///system list
如果这个命令能正常返回,说明libvirt层基本健康,问题大概率在nova配置与libvirt的对接参数上,而不是libvirtd本身。
常见依赖故障的快速对照表:
| 依赖组件 | 典型报错特征 | 第一步排查 |
|---|---|---|
| RabbitMQ | AMQP server ... unreachable: timed out | nc -zv 验证端口,核对rabbit账号密码 |
| MySQL | Can't connect to MySQL server | nc -zv 3306,核对连接串和授权 |
| libvirt | Cannot connect to hypervisor URI | systemctl status libvirtd,virsh list |
4. 资源型故障:磁盘、内存与时间同步导致的“神秘失败”
这一节是很多文档不会重点写、但生产环境里踩得最多的坑。资源类问题往往没有一个明确报错直指根因,而是服务启动后运行几秒就被环境“逼死”,最迷惑人的是报错天天变样子。
4.1 磁盘写满与inode耗尽
nova-compute要往instances目录写镜像、往日志目录写日志,任何一块盘满了,服务都会异常。但表现形态有很多种:
/分区写满:Python运行时缓存、临时文件都写不进去,启动直接报No space left on device。但注意,有时候df -h显示还有几个GB,实际上是inode耗尽了——文件数太多、每个都很小。检查要两个命令一起看:
bash复制df -h
df -i
/var/log写满:日志一写就失败,nova-compute进程往往直接退出。生产环境的常见诱因是日志轮转没配好,或者某个服务在疯狂打印日志。如果你看到日志文件几个月前的都还在、体积好几个GB,说明logrotate没有正常工作。/var/lib/nova/instances所在分区写满:一般是删除虚拟机时残留的磁盘文件没清理干净。用du -sh /var/lib/nova/instances/*排查,大目录往往对应僵尸实例。
关于僵尸实例,不能直接手动删目录。正确方式是通过数据库把实例状态置为ERROR或DELETED后,用 nova-manage project purge 或在控制端强制清理,否则nova-compute恢复后发现自己管理的实例信息和实际磁盘目录对不上,后续调度、迁移都会出现奇怪的错误。
4.2 内存不足与OOM Killer
nova-compute本身是常驻进程,内存占用一般稳定,但一个节点上同时运行大量虚拟机时,宿主机内存耗尽,内核会启动OOM Killer。如果被杀的进程恰好是nova-compute,服务就会退出。查看是否发生过OOM:
bash复制journalctl -k | grep -i oom
或者:
bash复制dmesg -T | grep -i "killed process"
如果发现nova-compute内存占用异常高,可以用systemd单位加 MemoryMax= 约束限制峰值,或用 systemd-run 观察具体占用。但更常见的情况是节点上虚拟机太多、内存超分策略设置不合理,导致内核回收时误杀。这个时候应该调整的是宿主机内存超分策略,而不是只盯着nova-compute这个进程。
4.3 时间同步偏移引发“幽灵故障”
这个坑极其隐蔽。计算节点时钟如果偏移超过消息队列的heartbeat阈值,rabbit会判定连接已死;如果偏移超过Keystone token有效期容差,后续虚拟机调度、镜像上传都会出现签名验证失败。但nova-compute启动时报的错却往往和这些表象对不上,看起来像网络故障或认证问题。
处理方法是在所有节点(尤其计算节点)配置chrony:
bash复制chronyc tracking
chronyc sources -v
如果发现时钟偏移超过几百毫秒,需要先纠正时钟再重新拉起服务,否则就算服务起来了,后续也会有各种偶发故障。
还有一个经验:虚拟机CPU抢占、宿主机负载过高都会影响chronyd的时钟跟踪精度,高负载计算节点上的时间漂移比空闲节点严重得多,所以负载高的节点更需要关注chrony的同步状态。
5. 一次真实排障的完整复盘:从日志倒推根因的全过程
前面讲了原理,这里还原一次我实际处理过的比较典型的案例,完整演示排查链路,这比直接给结论要有用得多。
背景:一套用OpenStack Kolla容器化部署的环境,某个计算节点所在物理机发生了一次断电,来电后宿主机上所有容器自动拉起,其他服务都恢复了,但 nova_compute 容器一直处于restarting状态。
我刚开始按老套路,先看容器日志:
bash复制docker logs nova_compute --tail 100
日志里反复出现同一段:
log复制ERROR oslo_messaging._drivers.impl_rabbit [req-...] AMQP server on 10.0.0.5:5672 is unreachable: timed out
第一反应是消息队列问题。我去控制节点验证rabbit的5672端口,ss -lntp | grep 5672 显示正常,rabbitmqctl list_queues 也正常。再从计算节点执行 nc -zv 10.0.0.5 5672,同样是通的。这就奇怪了——端口通、服务正常,却一直超时。
然后我注意到时间戳:容器日志里记录的时间比控制节点慢了大约20分钟。马上查chrony,发现该物理机断电重启后chronyd虽然启动了,但一直没完成初始同步,本地时钟严重滞后。RabbitMQ的心跳机制检测到两端时钟差超过阈值,直接掐断连接,导致nova-compute在启动阶段反复尝试连接却一直失败。
我执行了:
bash复制chronyc makestep # 立刻台阶式校准时钟,不是慢慢平滑调整
systemctl restart chronyd
校准后,再重启nova_compute容器,连接消息队列马上正常了。
但我没有立刻宣布修复完成,而是做了一轮更彻底的检查,因为这类异常重启最容易引发连锁问题。接着发现 /var/lib/docker/volumes/nova_compute/_data/lib/nova/instances 目录下有些残留的实例磁盘目录,是断电时还没来得及清理的。我通过nova API在控制端确认这些实例已处于DELETED状态,然后用 nova-manage project purge 清理掉,避免后续心算出现不一致。
这次排障得到的经验是:异常重启之后排障,第一件事永远是看时钟,第二件事才是看服务和网络。时间偏移导致的连接超时,光靠网络排查永远查不出结果。另外,容器化部署和裸机部署的排障路径差异很大:裸机先看systemd,容器化先看容器日志和卷配置,但底层原理完全一致,日志和网络验证的方法可以平移。
6. 恢复验证与加固:让计算节点稳定运行的一些实操心得
服务起来了,不等于事情结束。太多人nova-compute一恢复就干别的去了,结果第二天节点又挂,因为导致故障的根因根本没处理掉。恢复验证至少要分两层。
6.1 服务层面与调度层面的双重验证
先确认服务注册状态。在控制节点执行:
bash复制openstack compute service list
看计算节点对应的nova-compute条目是不是 up。注意看更新时间——如果一直不刷新,说明它虽然活着但没正常上报心跳,要回计算节点再看系统日志。
再验证调度功能,不要急着把生产工作负载迁移上去。可以先创建一个测试虚拟机或做一次冷迁移:
bash复制openstack server create --image test-image --flavor m1.tiny --availability-zone nova:compute-node-01 test-recovery
确认虚拟机能够稳定运行至少几分钟,再进入后续操作。
6.2 配置systemd自愈策略
生产环境我会给nova-compute做两道保障。第一道是systemd自动重启,在 /etc/systemd/system/nova-compute.service.d/ 下新建配置文件:
ini复制[Service]
Restart=on-failure
RestartSec=10
这个配置能在服务崩溃退出后自动拉起。第二道是定时健康检查脚本,通过cron每分钟探测一次服务状态和关键端口,发现异常就拉起并记录报警。不要把全部希望寄托在systemd上,因为有些故障形态下systemd看到的服务状态是active,但nova的调度已经不通了。
6.3 日志、监控与变更管理
真正减少故障影响的关键在日常监控。建议至少盯住这些指标:磁盘使用率(尤其是 /var/lib/nova/instances 所在分区)、inode使用率、内存余量、系统时间偏移、nova-compute进程存活状态。任何一项触发告警都值得立即处理,而不是等下一次故障爆发。
日志方面,确认nova-compute.log的轮转策略。文件长期不清理会占用大量磁盘,而且会掩盖启动阶段的早期错误——旧日志里的信息被冲掉了。配置logrotate时注意给nova用户保留权限,并且设置 copytruncate。因为nova不会自动重开文件句柄,logrotate直接rename的话nova会一直往旧文件里写,新日志文件反而永远是空的。
最后一点心得:很多启动异常不是运行事故,而是变更事故。升级nova版本时,如果跨了大版本(比如从Train升级到Wallaby),要特别注意nova.conf里的配置项可能被废弃或改默认值。我踩过一次坑:升级后nova-compute启动时日志安静地打了几行WARNING,然后直接退出,查了半天才发现是一个老配置项在版本更新后被移除了,nova直接把它当成致命错误。应对做法是在升级前用新版二进制跑一遍配置校验工具(不同版本命令名不一样,但都有配置检查能力),并且在测试环境先验证一轮再上生产。按我现在的习惯,任何变更操作都留一条退路:备份nova.conf和关键目录,记录变更前的系统状态,变更后观察至少一个完整的调度周期,确认虚拟机创建、冷迁移、宿主机间网络都正常。这样才能把nova-compute启动异常这类问题的影响面控制在最小。
