Nova 中的块设备映射

Nova 具有可以暴露给云实例的块设备概念。实例可以拥有多种类型的块设备(我们将在本文档的后续部分详细介绍),可用的设备取决于特定的部署以及为租户和用户设置的使用限制。块设备映射是一种组织和保存实例拥有的所有块设备信息的方式。

当我们谈论块设备映射时,通常指的是以下两种情况之一

  1. 用于指定实例启动请求的块设备的 API/CLI 结构和语法

  2. Nova 内部的数据结构,用于记录和保存,最终持久化在 block_device_mapping 表中。然而,Nova 内部有几种“略有”不同的格式来表示相同的数据。所有这些都在代码中记录,或者由一组不同的类呈现,但不知道它们的存在可能会让阅读代码的人感到困惑。因此,除了镜像数据库模式的 BlockDeviceMapping [1] 对象之外,我们还有

    2.1 API 格式 - 这是从 API 客户端接收到的原始键值对集合,几乎立即转换为对象;但是,使用此格式会进行一些验证。从现在开始,我们将把此格式称为“API BDM”。

    2.2 virt 驱动程序格式 - 这是 nova.virt.block_device 中的类定义的格式。该格式由各种 virt 驱动程序中的代码使用和期望。这些类除了暴露不同的格式(模仿 Python dict 接口)之外,还提供了一个用于捆绑某些类型的块设备通用的功能的地方(例如,附加卷,必须与 Cinder 和 virt 驱动程序代码交互)。从现在开始,我们将把此格式称为“Driver BDM”。

    有关更多详细信息,请参阅 驱动程序 BDM 数据结构 参考文档。

注意

允许附加到单个服务器的磁盘设备的最大数量可以通过选项 compute.max_disk_devices_to_attach 进行配置。

API BDM 数据格式及其历史

在 Nova 的早期阶段,块设备映射的总体结构紧密模仿了 EC2 API。在 Nova 的 Havana 版本中,块设备处理代码,进而块设备映射结构,进行了改进通用性和实用性的工作。这些改进包括在 API 中暴露更多详细信息和功能。为了便于此,向 v2 API 添加了一个名为 BlockDeviceMappingV2Boot [2] 的新扩展,该扩展在实例启动 API 请求中添加了一个额外的 block_device_mapping_v2 字段。

块设备映射 v1(又名遗留)

这是原始格式,仅支持 cinder 卷(类似于 EC2 块设备仅支持 EBS 卷)。每个条目都以设备名称为键(我们稍后在本页的单独部分讨论为什么这有问题),并且只能接受

  • Cinder 卷或快照的 UUID

  • 类型字段 - 仅用于区分卷和 Cinder 卷快照

  • 可选大小字段

  • 可选 delete_on_termination 标志

虽然 Nova 内部的所有代码仅使用和存储新的数据结构,但我们仍然需要处理使用遗留格式的 API 请求。Nova API 服务在每个请求上都会处理此问题。正如我们稍后将看到的那样,由于块设备映射信息也可以存储在 Glance 的图像元数据中,因此这是我们需要处理 v1 格式的另一个地方。处理遗留转换的代码是 nova.block_device 模块的一部分。

插曲 - 设备名称的问题

将设备名称作为每个实例的主要标识符并将其暴露在 API 中,对于 Nova 来说是有问题的,主要是因为 Nova 支持的几个超visor 无法保证 guest OS 分配的设备名称是用户从 Nova 请求的名称。在 Nova 的公共 API 中暴露这样的细节显然不是理想的,但为了向后兼容性,它需要保留。它也需要用于一些(略微晦涩)的功能,例如在启动实例时重载 Glance 图像中的块设备 [3]

解决此问题的计划是允许用户不指定块设备的设备名称,Nova 将确定它(在 virt 驱动程序的帮助下),以便仍然可以通过 API 发现它并在必要时使用它,就像上述功能一样(最好仅在当时)。

指定设备名称的另一个用途是允许“从卷启动”功能,通过指定与实例的根设备名称匹配的设备名称(通常为 /dev/vda)。

目前(Liberty 中期),我们不鼓励用户为所有需要或允许块设备映射的调用指定设备名称,除非尝试覆盖实例启动时的图像块设备映射,并且未来可能仍然如此。Libvirt 设备驱动程序将直接覆盖使用其自身值传递的任何设备名称。

块设备映射 v2

引入新格式是为了解决上述讨论的原始块设备映射格式中的问题,并允许更多灵活性和添加以前无法实现的特性。

新的块设备映射是包含以下字段的字典列表(除了已经存在的字段)

  • source_type - 这可以是以下值之一

    • image

    • volume

    • snapshot

    • blank

  • destination_type - 这可以是以下值之一

    • local

    • volume

  • guest_format - 告诉 Nova 如何/是否在附加设备之前格式化设备,应仅与空白本地图像一起使用。如果值为 swap,则表示交换磁盘。

  • device_name - 请参阅上一节以获取更深入的说明 - 目前最好留空(未指定),除非用户想覆盖图像元数据中指定的现有设备。对于 Libvirt 而言,即使在以覆盖现有图像元数据的目的传递,实例的最终设备名称集仍然可能被驱动程序更改。

  • disk_bus 和 device_type - 一些超visor(当前仅限 libvirt)可能支持的低级细节。一些示例 disk_bus 值可以是:ideusbvirtioscsi,而 device_type 可以是 diskcdromfloppylun。这并非详尽无遗的列表,因为它取决于虚拟化驱动程序,并且随着添加更多支持可能会发生变化。将这些留空是最常见的事情。

  • boot_index - 定义超visor 在尝试从存储启动 guest 时尝试设备的顺序。每个能够用作启动设备的设备都应从 0 开始按升序分配唯一的启动索引。一些超visor 可能不支持从多个设备启动,因此只会考虑启动索引为 0 的设备。一些超visor 将支持从多个设备启动,但前提是它们是不同的类型 - 例如磁盘和 CD-ROM。将负值或 None 设置为指示不应将设备用于启动。最简单的用法是将它设置为 0 以用于启动设备,并将其保留为 None 以用于任何其他设备。

  • volume_type - 添加到服务器创建 API 的微版本 2.67 中,以支持在启动实例时指定卷类型。当快照卷支持的服务器时,block_device_mapping_v2 图像元数据将包含 BDM 记录中的 volume_type,因此,如果用户然后从该快照创建另一个服务器,nova 从该快照创建的卷将使用相同的 volume_type。如果用户希望更改图像元数据中的 volume_type,可以通过图像 API 来执行此操作。

有效的源/目标组合

source_typedestination_type 的组合将定义该条目所指的块设备类型。支持以下组合

  • image -> local - 这仅当前用于引用实例正在启动的 Glance 图像的条目(它也应标记为启动设备)。值得注意的是,指定此内容的 API 请求还必须提供与启动请求的 image_ref 参数相同的 Glance uuid(这是为了向后兼容性,将来可能会更改)。此功能可能会扩展到指定附加到实例启动后的其他 Glance 图像(类似于内核/ramdisk 图像),但当前任何驱动程序都不支持此功能。

  • volume -> volume - 这只是要附加到实例的 Cinder 卷。它可以标记为启动设备。

  • snapshot -> volume - 这与传递 type=snap 的效果完全相同。它将从 Cinder 卷快照创建一个卷,并将该卷附加到实例。可以标记为可启动。

  • image -> volume - 顾名思义,这将从 Glance 图像下载到 cinder 卷并将其附加到实例。也可以标记为可启动。这实际上只是在启动实例之前从图像创建卷的快捷方式。

  • blank -> volume - 创建一个空白的 Cinder 卷并将其附加。这将还需要设置卷大小。

  • blank -> local - 根据 guest_format 字段(参见下文),这将是超visor 本地存储上的一个临时空白磁盘,或者一个交换磁盘(实例只能有一个)。

Nova 不允许在单个请求中混合 BDMv1 和 BDMv2,并且将执行基本验证以确保请求的块设备映射有效,然后再接受启动请求。

常见问题解答

  1. 是否可以配置 nova 以自动使用 cinder 为所有根磁盘提供卷支持?

    否,Nova 中没有自动机制可以将非-从卷启动 请求转换为将图像转换为根卷。已经讨论过一段时间的几个想法,这些想法都包含在 基于卷的风味 规范中。但是,如果您希望强制用户始终创建基于卷的服务器,则可以通过将 max_local_block_devices 设置为 0 来配置 API 服务。这将导致任何非从卷启动的服务器创建请求失败并返回 400 响应。