OpenStack Nova 创建虚拟机

创建虚拟机的过程是一个经典的分布式、异步、状态驱动的工作流。其核心设计哲学是:API 接收请求 -> 调度决策 -> 资源分配 -> 虚拟化层执行。整个过程涉及多个 Nova 服务以及外部组件(Glance, Neutron, Cinder, Keystone)。

1、基本流程

sequenceDiagram participant User participant nova-api participant DB as Nova DB participant MQ as Message Queue participant Scheduler participant Conductor participant Compute participant Libvirt participant Glance participant Neutron participant Cinder User->>nova-api: 1. POST /servers (创建实例) nova-api->>DB: 2. 创建初始实例记录(state: BUILDING) nova-api->>MQ: 3. cast(run_instance to scheduler) Scheduler->>MQ: 4. 获取消息 Scheduler->>DB: 5. 查询资源 Scheduler->>MQ: 6. cast(build_and_run_instance to compute.X) Compute->>MQ: 7. 获取消息 Compute->>Conductor: 8. 请求资源(镜像、网络等) Conductor->>DB: 9. 查询数据 Conductor-->>Compute: 10. 返回信息 Compute->>Glance: 11. 下载镜像 Compute->>Neutron: 12. 分配网络 Compute->>Cinder: 13. 挂接卷(如适用) Compute->>Libvirt: 14. 定义并启动域(XML) Libvirt-->>Compute: 15. 成功 Compute->>DB: 16. 更新状态(state: ACTIVE) Compute->>MQ: 17. 发送状态事件

流程可分为三大阶段:

  1. API 阶段 (nova-api):接收并验证请求,创建数据库记录,立即返回响应,将后续任务异步下发。
  2. 调度阶段 (nova-scheduler):决策虚拟机在哪个计算节点上启动。
  3. 构建阶段 (nova-compute):与各类服务交互准备资源,调用 Hypervisor 真正创建虚拟机。

2、详细流程源码分析

2.1 API请求入口

nova/api/openstack/compute/servers.py

python 复制代码
# nova/api/openstack/compute/servers.py (Caracal版本)

class ServersController(wsgi.Controller):
    @wsgi.response(202)
    @validation.schema(schema_server_create_v242, '2.42')
    def create(self, req, body):
        """处理创建虚拟机API请求"""
        context = req.environ['nova.context']
        server = body['server']
        
        # 1. 参数校验与提取
        name = server['name']
        image_ref = server.get('imageRef')
        flavor_ref = server.get('flavorRef')
        
        # 2. 创建实例对象
        instance = objects.Instance(context=context)
        instance.display_name = name
        instance.image_ref = image_ref
        
        # 3. 调用Compute API
        (instances, resv_id) = self.compute_api.create(
            context,
            instance_type=flavor_ref,
            image_href=image_ref,
            # ...其他参数
        )
        
        # 4. 返回202 Accepted响应
        return wsgi.ResponseObject({}, status=202)

2.1 核心创建逻辑

nova/compute/api.py

python 复制代码
# nova/compute/api.py (Caracal版本)

class API(base.Base):
    @check_instance_create
    @wrap_exception()
    def create(self, context, instance_type, image_href, **kwargs):
        # 1. 配额检查(使用Placement API)
        self._check_quotas(context, requested_cores, requested_ram, ...)
        
        # 2. 创建RequestSpec对象
        request_spec = objects.RequestSpec(
            context=context,
            flavor=flavor,
            image=image_meta,
            num_instances=num_instances,
            # ...其他参数
        )
        
        # 3. 安全组处理(使用Neutron API)
        security_groups = self._get_requested_security_groups(context, ...)
        
        # 4. 异步调用Conductor
        self.compute_task_api.schedule_and_build_instances(
            context,
            request_spec=request_spec,
            security_groups=security_groups,
            # ...其他参数
        )

2.3 调度决策

nova/scheduler/manager.py

python 复制代码
# nova/scheduler/manager.py (Caracal版本)

class SchedulerManager(manager.Manager):
    @messaging.expected_exceptions(exception.NoValidHost)
    def select_destinations(self, context, request_spec, ...):
        # 1. 获取所有主机状态(从Placement)
        hosts = self.host_manager.get_all_host_states(context)
        
        # 2. 应用过滤器链
        filter_properties = self._get_filter_properties(request_spec)
        hosts = self.host_manager.get_filtered_hosts(hosts, filter_properties)
        
        # 3. 权重计算
        weighed_hosts = self.host_manager.get_weighed_hosts(
            hosts, request_spec, filter_properties)
        
        # 4. 选择目标主机
        selections = []
        for i in range(request_spec.num_instances):
            if not weighed_hosts:
                raise exception.NoValidHost(reason="")
            chosen_host = weighed_hosts.pop(0).obj
            selections.append(chosen_host)
        
        return selections

2.4 计算节点执行

nova/compute/manager.py

python 复制代码
# nova/compute/manager.py (Caracal版本)

class ComputeManager(manager.Manager):
    @wrap_exception()
    @wrap_instance_fault
    def build_and_run_instance(self, context, instance, request_spec, ...):
        # 1. 准备网络(使用Neutron API)
        network_info = self.network_api.allocate_for_instance(
            context, instance, requested_networks, ...)
        
        # 2. 处理块设备(使用Cinder API)
        block_device_info = self._prep_block_device(
            context, instance, bdms, ...)
        
        # 3. 获取镜像元数据
        image_meta = self._get_image_metadata(context, instance.image_ref)
        
        # 4. 调用Driver创建虚拟机
        self.driver.spawn(
            context, instance, image_meta,
            injected_files=injected_files,
            admin_password=admin_password,
            network_info=network_info,
            block_device_info=block_device_info)
        
        # 5. 更新实例状态
        instance.vm_state = vm_states.ACTIVE
        instance.task_state = None
        instance.save()

2.5 Hypervisor交互

nova/virt/libvirt/driver.py

python 复制代码
# nova/virt/libvirt/driver.py (Caracal版本)

class LibvirtDriver(driver.ComputeDriver):
    def spawn(self, context, instance, image_meta, **kwargs):
        # 1. 准备磁盘(支持多种后端)
        disk_info = self._create_image(context, instance, image_meta)
        
        # 2. 生成安全启动配置
        secure_boot = self._get_secure_boot_config(instance)
        
        # 3. 生成Libvirt XML
        # /var/lib/nova/instances/<instance-uuid>/libvirt.xml 
        xml = self._get_guest_xml(
            context, instance, disk_info,
            network_info=kwargs['network_info'],
            block_device_info=kwargs['block_device_info'],
            secure_boot=secure_boot)
        
        # 4. 定义并启动虚拟机
        guest = self._host.get_guest()
        guest.create(xml, flags=libvirt.VIR_DOMAIN_START_PAUSED)
        
        # 5. 注入元数据
        self._inject_data(instance, network_info=kwargs['network_info'])
        
        # 6. 恢复虚拟机运行
        guest.resume()

3、常见问题

3.1 查看实例错误状态

第一步永远是查看实例的详细状态

bash 复制代码
openstack server show b2c3d4e5-f6g7-8901-bcde-f12345678901 -c status -c fault
text 复制代码
+--------+----------------------------------------------------------------------------------------------------+
| Field  | Value                                                                                              |
+--------+----------------------------------------------------------------------------------------------------+
| fault  | {                                                                                                  |
|        |   "message": "No valid host was found. There are not enough hosts available.",                     |
|        |   "code": 500,                                                                                     |
|        |   "details": "Exceeded maximum number of retries. Exception: No valid host was found. ...",        |
|        |   "created": "2025-09-15T10:23:12Z"                                                                |
|        | }                                                                                                  |
| status | ERROR                                                                                              |
+--------+----------------------------------------------------------------------------------------------------+

fault 字段通常会给出比较直接的错误原因。

3.2 根据状态定位问题阶段

  • 长时间处于 SCHEDULING 状态

    • 问题nova-scheduler 无法找到合适的主机。
    • 排查
      1. 资源不足 :检查目标主机是否有足够的 CPU、内存、磁盘。使用 openstack hypervisor stats showopenstack hypervisor show <hypervisor-id>
      2. 过滤器导致 :检查 nova-scheduler 日志,看主机是如何被过滤掉的。常见于强制策略(如 DifferentHostFilter 找不到另一个实例)。
      3. Placement 数据不同步 :运行 nova-manage placement sync 同步数据。
  • 长时间处于 SPAWNING 状态或最终变为 ERROR

    • 问题nova-compute 在目标节点上构建失败。
    • 排查
      1. 查看计算节点日志 :这是最重要的步骤!日志路径通常为 /var/log/nova/nova-compute.log
      2. 镜像下载失败 :Glance 镜像 URL 不可达、镜像格式不支持(如 raw, qcow2)、磁盘空间不足(检查 /var/lib/nova/instances/_base)。
      3. 网络问题 :Neutron 无法分配端口(如 IP 耗尽、安全组规则错误)。查看 Neutron 相关日志 (/var/log/neutron/*.log)。
      4. Hypervisor 问题 :Libvirt 权限问题(检查 libvirtd 进程和 /var/lib/libvirt/ 权限)、QEMU 进程启动失败(检查 ps aux | grep qemu)、SElinux 或 AppArmor 策略限制。
      5. 卷挂载失败 :Cinder Volume 状态不是 available 或连接器(connector)信息有误。
  • 立即变为 ERROR

    • 问题:通常发生在 API 或调度阶段,是前置检查失败。
    • 排查
      1. 查看 API 节点日志/var/log/nova/nova-api.log
      2. 配额不足 :检查项目配额 openstack quota show <project>
      3. 参数无效:指定的 Flavor、Image、Network 不存在或不可见。
      4. Keystone 认证失败:Token 过期或权限不足。

3.3 核心排查工具与命令

  1. 日志!tail -f /var/log/nova/nova-*.log。使用 grep <instance-uuid> 过滤特定实例的日志,这是最强大的工具。
  2. 虚拟化层检查
    • 登录到计算节点,检查 Libvirt 域:virsh list --all(看实例是否存在)。
    • 如果存在但有问题,查看其定义:virsh dumpxml <instance-name>
    • 查看虚拟机控制台日志:virsh console <instance-name>(需在镜像内启用 console)。
  3. 网络命名空间 :Neutron 使用 Linux Network Namespace。在计算节点上,使用 ip netns list 找到实例相关的命名空间(如 qrouter-qdhcp-),然后 ip netns exec <ns-name> bash 进入命名空间内部调试网络(ping, ip addr)。
相关推荐
哈里谢顿6 天前
Nova parse_args 函数详解
openstack
哈里谢顿8 天前
OpenStack 中的 nova-conductor 与 ironic-conductor 及其分布式锁机制详解
openstack
哈里谢顿12 天前
OpenStack oslo-config 详解
openstack
感哥17 天前
OpenStack Cinder 创建卷
openstack
感哥18 天前
OpenStack Cinder 架构
openstack
感哥18 天前
OpenStack Nova Scheduler 计算节点选择机制
openstack
感哥21 天前
OpenStack Glance(镜像)
openstack
感哥21 天前
OpenStack Keystone详解
openstack
安全菜鸟1 个月前
传统方式部署OpenStack具体教程
openstack