☰
OpenStack Nova架构核心组件与调度机制详解
2026/10/5 7:14:50 网站建设 项目流程

1. Nova是什么:先搞明白它在OpenStack里的位置

如果你是第一次接触OpenStack,跑完一套环境(比如PackStack或者DevStack)之后,第一个让你觉得"这东西有点东西"的组件,大概率就是Nova。运维圈子里流传着一句话:OpenStack的虚拟化能力、实例管理能力、调度能力和配额控制能力,全部由Nova提供。换句话说,Nova是OpenStack的计算核心,你要是能把它看明白,云平台的大半边天基本就清楚了。

从历史沿革来看,Nova其实是从NASA的Nebula项目孵化出来的,前身是亚马逊EC2风格的云计算组件。后来和RackSpace的Swift对象存储项目合并,才一起组成了OpenStack这个大家庭。所以Nova的代码里至今还保留着大量跟AWS EC2 API兼容的影子,像nova client里那些ec2开头的配置项,就是这段历史的"活化石"。现在OpenStack的版本都已经迭代到了2024.2(代号类似于Caracal),Nova仍然是其中最核心、最不可替代的一个子项目。

那么Nova到底能做什么?我们用一个最简单的场景来理解:你在公有云上点一个按钮创建一台虚拟机,这个动作背后那一连串复杂的流程——找一台物理主机、检查资源够不够、给这台机器分配IP、调用底层虚拟化软件把VM拉起来、再把这个实例的元数据记录下来——这些活儿全部由Nova承接。如果你考过或做过OpenStack运维,你一定遇到过nova list、nova show、openstack server create这类命令,这些命令的背后,就是Nova的API、Scheduler、Conductor、Compute这一整套组件在协同工作。

这篇文章适合谁看?一种是刚接触OpenStack、还停留在"能装能跑"阶段的初学者,你需要把Nova的组件关系串起来,知道创建一台虚拟机时消息是怎么流转的;另一种是在用官方文档或者社区教程,但文档讲得太零散,总感觉"每个单词都认识、连起来不知道在说什么"的同学。我尽量不说废话,直接拆架构。

1.1 Nova是OpenStack的计算大脑

Nova在OpenStack里的定位非常清晰:它是一个可以管理大规模计算实例(虚拟机)的生命周期的控制器。官方文档里管它叫"OpenStack Compute",负责的事情包括但不限于:实例的创建、删除、启动、暂停、挂起、恢复、迁移、调整规格(Resize)、重建(Rebuild)、快照管理等等。

你可能会问:那其他组件是干嘛的?打一个生活化的比方。假设整个OpenStack是一家公司:Nova是公司里的项目经理,负责统筹计算资源的整体调度;Neutron是网络管理员,负责给每个新入职的员工(虚拟机)配交换机、分IP、弄防火墙规则;Cinder是仓库管理员,负责提供各种规格的硬盘(卷);Glance则是资料室,负责保存操作系统镜像模板。

这几个角色不是各自为战,而是通过API互相调用。Nova在创建一个实例的时候,会去Glance要镜像,去Neutron要网络端口,去Cinder挂卷,最后再调用计算节点上的虚拟化驱动,把这一堆资源拼装成一台能开机、能远程操作的虚拟机。可以说,Nova是OpenStack所有组件中"合作对象最多"的一个,牵一发而动全身。

也正因为如此,Nova的架构设计经历过多次大调整。从早期单体进程到现在的分布式消息队列协作,从直接访问数据库到引入nova-conductor隔离层,再到后来分离出独立的Placement资源追踪服务,每一次改动都是为了解决一个非常实际的问题:节点规模大了、并发请求多了,老架构扛不住。理解了这条演进路线,你看到新版Nova里那些"多出来的组件"就不会觉得莫名其妙。

1.2 从一个问题入手:创建虚拟机到底谁负责

想象你执行了openstack server create --flavor m1.small --image cirros myfirstvm这条命令。在你按下回车的那一瞬间,你的CLI工具先通过Keystone拿到一个认证Token,然后把这个请求发到Nova的API Endpoint上,通常是http://控制器节点IP:8774/v2.1。

但从API接收请求到虚拟机真正确认创建成功,中间隔了好几个"角色"。Nova并不是一个单体程序一口气干完所有事,而是分成若干独立进程,每个进程可能有多个副本,各自负责一段职责,进程之间通过消息队列(默认是RabbitMQ)异步通信。这样设计的好处显而易见:某一个进程挂了可以单独重启(如果你在控制节点上执行systemctl restart openstack-nova-api,调度器和计算节点的服务不会受影响);需要扩展的时候,也可以单独增加某个组件的实例数量。

那么,这个"谁负责什么"的问题,就是我下面要拆解的核心内容——Nova的五大组件,各个角色的分工与合作。

2. Nova的"五脏六腑":核心组件逐个拆解

Nova架构经过这么多版本迭代,主要角色固定下来了,分别是:nova-api、nova-scheduler、nova-conductor、nova-compute,还有一个不得不提的nova-placement(在比较新的版本中,Placement已经独立成单独的项目)。先看一张核心组件职责速查表,再逐个展开。

组件运行位置核心职责类比
nova-api控制节点接收所有OpenStack Compute API请求,校验参数、绑定路由、响应结果公司前台接待
nova-scheduler控制节点决定新实例放在哪台计算节点上,执行过滤和权重算法人力资源分配师
nova-conductor控制节点代理数据库读写操作,避免compute直连数据库,同时处理一些协调逻辑数据库守门员
nova-compute计算节点实际管理虚拟机生命周期,通过驱动调用底层Hypervisor一线车间工人
nova-placement控制节点跟踪每个资源提供者的资源库存、用量和分配情况资产台账管理员

2.1 nova-api:对外的大门

你不会直接在每台计算节点上装nova-api,它几乎永远是运行在控制节点上的。它的首要任务,就是让外部用户有一条通道可以操作Nova。这条通道是标准的RESTful API,支持OpenStack Compute API 2.1版本(更老的2.0早已淘汰,新装环境千万别用旧客户端去对接)。

可能有人觉得API层就是个"转发的",没啥技术含量。其实大错特错。nova-api在处理每个请求时,要做的事情非常多:首先去Keystone验证Token是否有效、是否过期、用户是否有对应权限;然后解析请求参数,把JSON/XML格式的数据转换为Nova内部的object;接着判断这个请求是需要立刻返回结果,还是要发到消息队列里去异步执行。

举个例子:你执行openstack flavor list的时候,nova-api直接查数据库,马上返回结果,这是一条同步链路。但你执行openstack server create,nova-api把请求包装成一个RPC消息,扔进消息队列,然后就返回了HTTP 202(Accepted开头),实际创建过程的持续轮询是你通过nova list或者openstack server list看到的。理解这个同步/异步的区别,对排查"为什么命令半天没反应"特别有帮助——很可能请求已经出发了,只是后端还没跑完。

2.2 nova-scheduler:决定虚拟机跑在哪台机器上

Nova里最能体现"架构设计智慧"的组件之一就是scheduler。它的任务就是一句话:从一堆计算节点里选出一台最合适的,去跑这个新实例。但实现这个"最合适"的判断过程,涉及两层算法:先过滤(Filter),再打分(Weighing)。

先过滤是什么意思?就是把明显不满足条件的节点淘汰掉。例如我要求新实例必须落在availability_zone: prod这个可用域里,那不满足的物理节点直接出局;再比如我要求必须有至少4GB的可用内存,内存不够的节点也出局。经过多轮过滤后,剩余的就是"候选主机名单"。

再打分又是怎么回事?因为符合条件的不止一台机器,总得挑一个最优解。打分器(Weigher)会给候选机器打一个分数,比如CPU剩余多的权重高、内存剩余多的权重高。Nova默认把ram_weight_multiplier设为1.0,也就是内存多的机器更可能被选中。这两步走完后,scheduler把选中的主机变成一条消息,发回消息队列,通知该主机上的nova-compute干活。

有很多新人在配置scheduler的时候有个误区:以为只要调度器选好主机,那么实例就一定会跑在那边。实际上负责任地告诉你,如果nova-compute在最后一步执行的时候因为某种原因失败了,实例有可能会被重新放置,或者直接变ERROR状态。这个最后一步的失败,调度器是帮不上忙的,需要结合计算节点日志往上查。

2.3 nova-conductor:数据库操作的"代理层"

Nova架构早期,nova-compute是直接读写数据库的。听起来好像也没什么毛病,但真出了问题就后怕了:计算节点通常分布在各机房,如果你只把数据库的账号密码分发到几十台计算节点上,那么任意一台机器被攻破,整个数据库的数据都保不住。这是一个巨大的安全隐患。

所以从Grizzly版本开始,Nova引入了nova-conductor,作为所有数据库操作的中间代理。计算节点上的nova-compute再想查数据库,不再直连MySQL/MariaDB,而是把自己的请求封装成RPC消息发给conductor,由conductor去执行数据库操作,再把结果返回。这样一来,数据库账号只需要存在于控制节点上,计算节点彻底跟数据库隔离了。

除了安全考虑,conductor还承担了一部分重量级的逻辑操作,比如在实例迁移(migration)的时候,许多协调性工作是在conductor里完成的。所以,如果某一天你发现创建实例卡在某一步、日志里大量出现nova-conductor超时或者DBError,别急着一顿乱查,先看conductor是不是活着、数据库连接池是不是被耗尽了。这个守门员一旦罢工,全流程直接瘫痪。

2.4 nova-compute:真正干活的角色

终于轮到最一线的工人了。nova-compute跑在每一台计算节点上,它是唯一一个直接操作Hypervisor(比如KVM、QEMU、Xen、VMware ESXi)的组件。它通过驱动架构屏蔽底层差异:对KVM来说,它的驱动是libvirt,通过libvirt的API去创建domain、管理生命周期;对Xen来说,用的是XenAPI;对VMware来说,用的是VMwareVCDriver。

你可以理解为一个工厂里,不管车间里用的是哪种型号的机床,工人手里都有一套标准化的操作面板。nova-compute就是那个操作面板,driver是这个面板底下接的各个型号机床的适配器。在进行nova-compute的配置时,最关键的一句配置是compute_driver=libvirt.LibvirtDriver,如果你换成了别的驱动,整个计算节点管理的虚拟化方式就变天了。

特别提醒一下,nova-compute确实是"干活的人",但它并不是所有事情都能自己拍板。它经常需要向conductor查询数据库(比如查flavor的规格、查镜像的信息),也需要把自己收集到的资源变化情况上报给Placement服务。所以你会发现,即使计算节点和数据库不直连,它的消息队列连接也绝对不能断——一旦RabbitMQ的某个队列堆积起来,实例创建的即时反馈会变得非常糟糕。

3. 一场虚拟机创建之旅:Nova内部的消息流

讲完组件,我们把整个创建流程串一遍。这里我用的是最常见、也是新装环境默认的RabbitMQ消息队列模式,其他消息中间件(如Kafka、ZeroMQ)原理类似。作为运维,你能把这套消息流描述清楚,面试和排障都够用了。

3.1 从API到数据库:请求进入Nova的大门

第一步,客户端(比如openstack命令行工具)向Keystone发起认证请求,拿到一个Scoped Token。然后带着Token去请求nova-api:POST /v2.1/servers,Body里带着flavorRef、imageRef、networks等参数。如果你用的是较新版本的OpenStack,这里其实还会带上用户直接指定的availability_zone和scheduler_hints。

nova-api收到请求后,做的第一件事是验证Token:它会调用Keystone提供的验证接口,确认这个Token对应的用户、项目(租户)是否合法。验证通过后,nova-api把请求里的参数组装成RequestSpec对象,再调用api层的create()方法,把数据库记录(如实例表instances)的状态先写成BUILDING,同时把一条RPC消息扔进消息队列,这条消息的目的地是conductor,因为很多数据库写入逻辑需要conductor代理完成。

这里有个十分重要的启动参数:在配置文件中,scheduler_availability_zone和default_schedule_zone这些值会影响后续调度。nova-api会把请求里没有显式指定的默认值补全。也就是说,如果你在request里没指定可用域,那么默认你认为的"要去哪个可用域"就取决于配置文件,这个配置往往就是你踩的第一个坑。

3.2 调度器如何"挑肥拣瘦":Filter + Weight机制

消息从nova-api进入RabbitMQ后,会进入一条调度队列(通常叫conductor或者scheduler相关队列),nova-scheduler和nova-conductor都是消息队列的消费者。调度的主流程可以这么描述:

  1. conductor收到请求,调用scheduler_client的select_destinations()方法,把RequestSpec传给scheduler。
  2. scheduler开始过滤,它会向Placement服务查询所有计算节点(资源提供者)的当前资源状态,再结合自己的Filter链逐一淘汰。
  3. scheduler进行打分,剩余的节点按权重排序,选出得分最高的那个(如果有多个,默认从高分队列里选一个)。
  4. scheduler返回目标主机列表,conductor收到后,再将结果作为RPC消息发给对应主机上的nova-compute。

很多人觉得调度器没啥好优化的,默认配置跑得很好。但真要大规模部署的时候,Filter顺序和Weigher参数是需要反复调的。比如:你有两套宿主机,一套是普通机械盘、另一套是NVME SSD,如果按默认的调度策略,新实例可能随机落在两套上,导致性能差异不均。这时候就要利用AggregateInstanceExtraSpecsFilter配合主机聚合(Host Aggregates)来做区分:给SSD那组打一个ssd=true的metadata,flavor的extra_specs里也声明ssd=true,调度器就会把该flavor的实例全部赶到SSD组去。

关于Filter和Weigher的具体清单和调优思路,下一节我会单独展开。

3.3 从调度到创建:nova-compute的执行链路

nova-compute收到RPC消息后,不是马上创建虚拟机,而是先做一连串预检和回源操作:

  • 查询数据库:作为消费者,它会调用conductor的RPC接口,把flavor信息、镜像信息、实例的uuid等元数据查出来;
  • 获取网络信息:它调用Neutron的API,为这个实例创建或绑定一张虚拟网卡(Port);
  • 获取卷信息:如果需要挂Cinder卷,它会调用Cinder的API去attach卷;
  • 准备镜像:把Glance里镜像通过glanceAPI拉取到本地缓存(如果是本地存储的话),或者从共享存储的镜像文件中直接拷贝;
  • 生成XML:nova-compute基于这些信息,通过driver生成要给libvirt执行的XML定义文档(如果底层是QEMU/KVM,这个文档里包括CPU型号、内存、磁盘、网卡,以及各种超线程/NUMA/CPU pinning参数等);
  • 执行创建:driver调用libvirt的virDomainDefineXML,把VM定义到Hypervisor上,再调用virDomainCreate,启动它。

这里每一个环节出错的概率都不低。最常见的是网络步骤卡住——比如Neutron的DHCP Agent没起来,或者子网网段跟物理环境冲突,实例就会一直卡在BUILDING状态,最终变成ERROR。这时候你去查看/var/log/neutron和/var/log/nova/nova-compute.log,能发现非常明确的报错线索。

4. Nova的调度策略详解:Filters与Weights的协同

调度是Nova核心中的核心,值得单独用一整节来深挖。Nova默认使用的调度器是FilterScheduler,配置项是scheduler_driver = filter_scheduler.FilterScheduler。它会把所有候选主机先过一遍Filter链,剩下的再用Weigher排序。如果这两个阶段都没调明白,你的云平台可能在某次大促销/大扩容时直接"翻车"。

4.1 常用Filter与排序器速查

阶段名称作用
FilterAvailabilityZoneFilter只保留匹配请求可用域的主机
FilterComputeFilter过滤掉已禁用、不在线、不处于服务状态的主机
FilterComputeCapabilitiesFilter根据flavor的extra_specs匹配主机能力
FilterRAMFilter过滤掉可用内存不足的主机(防止超分过高)
FilterNUMATopologyFilter指定NUMA拓扑需求时,过滤掉不匹配物理拓扑的主机
FilterAggregateInstanceExtraSpecsFilter配合主机聚合的metadata做精确能力匹配
WeigherRAMWeigher剩余内存越多,权重越高,默认multiplier重1.0
WeigherMetricsWeigher根据采集的metrics(CPU负载等)打分,需另配插件
WeigherServerGroupAntiAffinityWeigher尽量把同组实例散开到不同主机

你安装完OpenStack后,nova.conf里的[filter_scheduler]段一般长这样:

[filter_scheduler] enabled_filters = AvailabilityZoneFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,CoreFilter,RAMFilter available_filters = nova.scheduler.filters.all_filters

这个配置的意思就是:只启用这些过滤器,其它过滤器不启用。工程上常需要按情况调整顺序和启用项。例如你启用了CoreFilter,它默认检查物理主机的超配比例,如果cpu_allocation_ratio=16,你允许一台32核的机器超配到512个vCPU。一旦某台机器已经塞了超过500个vCPU,新实例就不会再放上去。

经验之谈,刚开始搭建环境时,很多人想通过调大cpu_allocation_ratio来增加创建的虚拟机数量,这本身没问题,但一定要同时调好ram_allocation_ratio和磁盘的overcommit参数。如果不设置好,调度器看起来"有资源",真到创建的时候Hypervisor要么内存不够、要么磁盘写满,一堆实例处于ERROR状态,排查起来非常麻烦。

4.2 多可用域与主机聚合的调度

可用域(Availability Zone)和主机聚合(Host Aggregates)是两个看起来相似、语义上却完全不同的概念。可用域是用户可见的(API参数里可以显式指定),主要用于高可用场景,比如让客户把业务分散到多个故障域;主机聚合则通常是管理员自己定义的资源池,带特殊的metadata,比如上文的ssd=true。

实际架构里,我们经常把主机聚合跟可用域混着用。比如在双机房的场景中,我们可以为A机房的所有计算节点创建聚合az-a,并为B机房创建聚合az-b。然后在调度时通过AvailabilityZoneFilter去保证新实例落在指定机房,再通过AggregateInstanceExtraSpecsFilter去保证只有满足特定spec(比如shared_storage=true)的主机才会被纳入候选。

调度器级的多级过滤设计,让Nova可以在一个云平台里同时服务多种差异巨大的业务需求——对CPU绑定敏感的HPC任务,对真实内存大小敏感的数据库任务,对性能均衡要求高的Web集群。几乎所有场景都能通过Filter和Weigher的组合切分清楚。但反过来,如果你不规划好,集群里所有机器都会变成"一锅烩"——没法精细分片,之后在运维层面补也是亡羊补牢。

5. Nova架构演进:Placement服务与Cells v2

很多新人在看新版本Nova的架构图时会愣住:怎么多了一个叫Placement的东西?其实在Nova刚诞生的时候,资源追踪都是靠数据库里一套叫compute_nodes的表格来记录的。nova-compute启动时会把自己的总资源量和已用量写入这张表,调度器来查询。这套机制在规模小的时候还行,但到了几百个计算节点后,资源冲突、并发更新的问题就开始出现了。

5.1 为什么新增Placement服务

Placement这个项目最早的原型出现在N版(Newton),到O版(Ocata)和P版(Pike)不断强化,最后在 Queens 版本中把它从Nova代码库里彻底独立成了一个单独的项目,单独打包成openstack-placement-api之类的包。

它的核心职责就是保存"资源提供者(Resource Provider)"的信息。一个资源提供者可以是一台物理机、也可以是一个资源池。它记录的内容包括:这个提供者拥有多少资源(比如vCPU总量、内存总量、磁盘总量),已经用掉了多少,以及各类资源当前被哪个"消费者"(比如哪个实例)占用。调度器在做Filter和Weighing时,首先会调Placement的API来获取实时的、全量的资源视图。

这种方式比老旧的数据库表靠谱多了。首先,Placement的API设计是RESTful的,任何组件都能调用;其次,它把资源追踪这块逻辑跟计算调度彻底解耦,Nova可以随时查询,而不用关心这些数据到底存在哪。你在部署中如果能单独重启placement-api服务而不影响nova-api,这在老架构里是不可想象的。

运维提示:升级到新版本后,别忘了先跑一遍placement-manage db sync初始化数据库,否则placement-api会一直报401或者500。另外,平常可以用openstack resource provider list、openstack resource provider inventory list <rp_uuid>这类命令来查看资源账本,排查调度不准确的问题。

5.2 Nova-compute直连的局限与Cells v2

除了Placement,Nova还经历了另一次大手术——Cell V2。老时代Nova就是一个大数据库、大消息队列,所有nova-compute节点都连着同一个bus。这在早期几十台节点的规模下没问题,可一旦扩展到上几千台物理机,单一数据库的写入压力、双边消息队列的队列堆积、故障内容同步的一致性问题都会集中爆发。

于是社区推出了Cells架构(第一代是Cell V1,比较复杂,后来废弃了),一直到N版引入了Cell V2,并且在P/T版中把它做成唯一的部署模式。Cell V2把Nova的架构分成两层:全局层(global)和单元层(cell)。全局层包括API、Scheduler、Placement、整个环境的共享数据库;单元层则包含一个独立的消息队列、一个独立的数据库、一个或多个Conductor和Compute节点。

每个Cell有自己的Cell数据库,存的是这个Cell内部的实例信息和计算节点信息;全局数据库只存放跨越所有Cell的元数据,比如Cell的映射关系、实例和Cell的对应关系。这是典型的"分库分表"思路。创建这台VM时,全局API和Scheduler先把实例映射到某个Cell(现在通常是cell0以外的一个或几个cell),然后把创建请求路由到该Cell内部的消息队列,由该Cell的conductor和compute去执行。

你可能会觉得这会增加排查复杂度。确实如此。排障的时候,你先要查这个请求被调度到了哪个cell(直接看placement的allocation就能看到),然后去那个cell的消息队列和数据库里查实例的状态,不能只看全局数据库。我记得有次生产环境有个实例创建老是卡住,全局库里明明查到实例状态是BUILDING,但具体卡在是哪个环节,必须登录对应的cell-conductor日志才看得到。这个观念不转变,玩转新版Nova是空谈。

6. Nova与周边组件的关系:一张联动网络

只盯着Nova内部组件看是不够的,因为OpenStack是一个整体。Nova是"中心调度者",它时时刻刻在和Keystone、Glance、Neutron、Cinder交互。下面我把交互关系梳理清楚。

6.1 Nova与Keystone、Glance的交互

Keystone是身份认证服务。所有OpenStack CLI和API请求都必须先通过Keystone获取Token。Nova收到请求后,用这个Token去Keystone校验用户信息和授权范围。如果你给某个用户只授予了member角色,那么他创建实例没问题;如果只有reader角色,那么创建请求会被拒绝。

Glance则负责镜像管理。Nova在创建实例时,会把镜像一个接一个地下载到计算节点(如果用的是本地存储),或者通过共享存储路径直接引用镜像。Glance的API地址由Nova的配置文件[glance]段指定,如api_servers = http://controller:9292。如果镜像损坏或者权限不足,Nova会直接报ImageNotFound或Forbidden。

这里有一个运维小技巧:如果你发现创建虚拟机特别慢,且日志里频繁出现"Image is not in active state",可能不是网络问题,而是Glance镜像没有上传完成。另外,在Nova的[glance]配置里,可以设置allowed_direct_url_schemes来让计算节点直接从共享存储拷贝镜像文件,而不是每次都走HTTP下载,效率提升明显。

6.2 Nova与Neutron、Cinder的交互

网络这块绑定得很紧密。Nova实例的每一张网卡,本质上都是Neutron里的一个Port。在创建实例的时候,Nova会调用Neutron的API先创建Port,拿到这个Port的MAC地址、IP地址和所属网络信息,然后把它塞给libvirt的domain定义中,相当于把虚拟机的"网线"插好。一旦Neutron的plugin/agent(比如Open vSwitch或Linux Bridge)挂了,你创建的VM表面上"已经启动",但网络不通,这个坑特别容易让人误以为是Nova的问题。

卷存储也一样。Nova创建实例时如果不指定--block-device-mapping,默认是本地盘(ephemeral);如果指定了Cinder卷启动,Nova会调用Cinder的API将卷attach到计算节点上。这个attach过程用到了os-brick库,它会根据底层存储类型(iSCSI、FC、NFS、Ceph RBD等)来执行对应的连接操作。RBD在Ceph环境下,一般不需要额外装驱动,但如果是iSCSI,你得确保计算节点上有正确的multipath和iscsi-initiator-utils工具。很多新手在Ceph挂卷失败时,日志里只看到Failed to connect to volume,实际上问题往往出在计算节点缺少依赖包或缺少到存储网络的互通路由。

7. 常见架构问题排查与运维实操经验

架构理解了,能不能用到排障上才是检验标准。我挑几个实际运维中反复出现的Nova架构问题,配合排查思路展开。

7.1 经典排障思路:从API层往里逐层推进

排查Nova问题时,我建议遵循一条铁律:从用户请求链路的最前端开始,逐层往后查,不要跳层。

比如,一个实例创建失败,报错ERROR或一直BUILDING。我的排查顺序通常是:

# 1. 先看全局日志,锁定大致阶段 tail -f /var/log/nova/nova-api.log tail -f /var/log/nova/nova-scheduler.log # 2. 查数据库里的实例状态,确认调度结果 mysql -u nova -p -h controller nova -e "select uuid, vm_state, task_state, host, node from instances where uuid='<instance_uuid>'\G;"

如果实例的host字段是空的,说明调度没完成,问题出在scheduler或Placement;如果host字段已经填了某台计算节点,但实例状态还是BUILDING,那问题大概率在nova-compute与底层hypervisor之间。

第二步,去计算节点上看nova-compute的日志:

sudo tail -n 500 /var/log/nova/nova-compute.log | grep -E "ERROR|Error|Exception"

看到Nova报LibvirtError或者oslo_messaging超时,再对症下药。比如virDomainDefineXML频繁超时,常见原因是被当成宿主的这台机器上libvirtd服务卡死,或者libvirt连接达到上限。遇到这种,立即在计算节点执行systemctl status libvirtd看看是不是活着,必要时重启libvirtd,但注意这会短暂中断该节点的虚拟机热迁移能力。

7.2 架构演进中的运维避坑记录

我在多套OpenStack环境上做运维的几年里,踩过非常多跟Nova架构直接相关的坑,挑三个最常见的分享。

坑一:Placement同步不一致

明明节点上资源已经释放了,但Placement里还是显示已分配。这种情况通常是因为腾讯/虚拟机删除失败,或者nova-compute上报资源发起了RPC但没有成功。旧做法是去数据库里硬改allocations表——我极其不建议用这种方式,因为Placement有缓存和一致性校验。正确做法是用openstack resource provider allocation delete先清理异常allocation,再在计算节点上跑nova-manage resource重刷资源。等新版中nova-manage placement audit这类工具更完善之后,尽量用工具,别手写SQL。

坑二:RabbitMQ队列堆积导致"假死"

一场大型创建任务(比如几百台VM同时起),RabbitMQ里的队列消息量会瞬间飙升。在很多默认配置里,nova-api到nova-scheduler之间的RPC调用都是同步等待的,一旦消息处理不过来,CLI命令会一直卡在那里。这时候你在控制节点执行rabbitmqctl list_queues能看到某些队列的消息数上万。最粗暴的解法是增加scheduler/conductor的并发数,但前提是底层数据库扛得住。另一个需要注意的点是,在配置文件里给oslo_messaging_rabbit段设置rabbit_max_retries和rabbit_retry_interval,避免客户端长时间死等。

坑三:跨Cell迁移导致实例找不到

Cell V2架构下,如果你用旧方式直接改数据库把实例从一个Cell迁到另一个Cell,实例在全局会查不到映射,导致openstack server show找不到这个实例。遇到这台情况,不要瞎折腾。在所有Cell中做全量搜索,去导出的数据库备份里查找instance_mappings和cell_mappings表,确认实例原本归属于哪个Cell,再想办法降级回该Cell处理。任何跨Cell的高级操作(迁移、跨AZ疏散等),一定先看官方文档确认当前版本是否支持,不要自己脑补。

7.3 日常巡检Nova架构的几个关键点

最后整理一下我日常巡检Nova时一定会看的点,做个简单的checklist供大家抄作业:

  • 控制节点上的nova-api、nova-scheduler、nova-conductor服务状态是否健康(用systecmctl和curl到8774端口探活)。
  • 所有计算节点的nova-compute与libvirtd是否在线,nova计算机节点的状态是否up(通过openstack compute service list查看)。
  • RabbitMQ和MySQL的连接数是否正常。特别是RabbitMQ的unicast队列数量、MySQL每秒QPS,如果在高峰期异常飙高,要考虑扩容或者调整超时。
  • Placement的资源账面是否跟物理资源一致,定期用openstack resource provider list对照实际宿主机数量。
  • 实例的vm_state和task_state有没有异常。大量实例卡在migrating或resizing,说明协调层可能有积压,优先看conductor日志。

8. 最后聊点Nova架构的"感觉"

写到这里,Nova架构的核心内容基本都覆盖了。回头看这个过程,我最大的体会是,Nova这个组件其实像极了一个微服务化的调度系统。它把一整个"创建虚拟机"的流程拆成一个事件流水线,每个服务只负责自己那块,消息队列负责传输,数据库负责最终状态确认。这种架构一开始理解起来有点绕,尤其是习惯了单体应用、函数直接调用的人,会困惑为什么一个创建请求还要经过"API -> Scheduler -> Compute -> Conductor -> DB"这么漫长的路程。

但等你真正做过一次几千节点的超大集群,你就能体会到这套设计背后的良苦用心:每个组件可以独立扩展,单个组件的故障不会拖垮全局,计算节点分布式部署却不需要暴露数据库账号。调度器、API、Compute之间的松耦合,让Nova既有足够的弹性去应对大规模资源池,又有清晰的边界去划分每个运维人员的排查范围。

最后再分享一个小技巧:当你遇到Nova问题百思不得其解的时候,把脑子里"这是不是业务/镜像/网络问题"的预设先放一边,回到架构里去分析"这条消息现在应该在哪一环节,哪个服务可能已经挂了"。

这套"顺着消息流查架构"的思路,比背任何API文档都有用。Nova版本更新得再快,底层这套角色分工和消息流转的骨架,都没有变过。你先把这个骨架烂熟于心,再看任何一篇官方架构文档、任何一个社区排障帖,速度都会快很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询