OpenStack 存储热迁移

时间:2024-04-08 07:32:24

随着存储技术的不断发展,云计算中的存储后端种类越来越多。而在不同存储后端之间进行存储热迁移则是和普遍的需求。在 OpenStack 中,云硬盘(volume)支持在不同后端之间进行数据移。具体的可分为两类,一种是云硬盘未挂载的,另一种是已经挂载的。前一种比较简单,后面一种就是存储热迁移了。下面对这两种方式在 OpenStack 中具体实现简单介绍,此外还介绍如何实现把虚拟机从本地启动的方式热迁移到云硬盘中。

未挂载云硬盘的迁移

detached volume 指未挂载到虚拟机上的 volume,状态为available。对 detached volume 进行迁移的流程比较简单,直接将数据从原来的 volume 拷贝到新的 volume 就可以了。在迁移的过程中需要注意 volume 状态的变化、对异常的处理,保证 volume 状态的一致性。整个流程如图1所示。

OpenStack 存储热迁移

图1  detach volume迁移流程图

下面简单介绍 Cinder(即 OpenStack 中 volume 的管理组件)的实现细节。

  1. cinder-api 接收 migrate 请求
    cinder-api 收到 client 发来的 migrate 请求后,经过 paste.deploy 和 route 的映射,交给 cinder.api.contrib.admin_actions.API 类中的 migrate_volume 方法。该方法首先会检查迁移的目的 host 上的 cinder volume 服务是否正常,然后将目标 volume 的 migration_status 设为migrating。当这些预处理全部完成后,cinder api 通过 rpc 将请求转到 cinder-scheduler。
  2. cinder-scheduler 对目的 host 进行检查
    cinder-scheduler 收到 cinder-api 发过来的请求后,会对迁移的目的 host 进行检查,根据配置文件对 host 进行容量等检查,以判断该 host 能否创建新的 volume。
  3. cinder-volume 处理 migrate
    当 source host 的 cinder-volume 服务收到处理请求后,会执行以下三个操作:
    通过 rpc 调用目的 host 上的 cinder-volume 服务新建一个 volume;
    当新的 volume 创建完毕后,使用 dd 命令进行复制;
    复制完成后,对 volume 的状态进行修改并删除旧的 volume。

已挂载的云硬盘的热迁移

attached volume 指已经挂载到虚拟机,状态是 in-use 的 volume,对 attached volume 进行热迁移需要在不中断虚拟机业务的同时进行 volume 的复制和切换。OpenStack 中使用 libvirt 的 rebase方法来实现 volume 的热迁移。OpenStack API 流程处理如图2所示。

OpenStack 存储热迁移

图2 attach volume热迁移整体流程

如图2所示,attached volume 需要 cinder 和 nova 两个组件共同协作才能完成迁移。下面简单介绍 API 处理流程。

  1. cinder 向 nova 发起 swap volume 请求
    cinder-volume 服务收到 volume migration 请求后,如果发现 volume 状态为 attached,则通过 novaclient 向 nova 发起 swap volume 请求。
  2. nova api 对请求进行预处理
    nova-api 收到该请求后,通过 paste.deploy 和 route 的引导和映射,请求进入 VolumeAttachmentController 的 update方法。该方法先会进行例行检查,如对新旧 volume 的状态进行判断,确保能够执行 detach 和 attach 操作;对虚拟机的 Block Device Mapping 进行检查;中间还会调用 cinderclient 来更新新旧 volume 的状态。这部分的代码逻辑主要是对资源在数据库中的状态进行判断。然后,nova-api 通过 rpc 将请求提交到虚拟机所在主机的nova-compute 服务中,这时候就开始执行 volume 的热迁移了。
  3. nova-compute 调用 libvirt 执行具体的热迁移操作
    nova.compute.manager. ComputeManager 类中的 swap_volume 方法是真正执行的入口,主要流程如图3所示。OpenStack 存储热迁移

     

    图3  swap volume操作流程图

  4. cinder-volume 处理 volume 热迁移后的一些状态更新
    经过 nova 的 swap volume 步骤后,新旧 volume 之间的数据已经复制并同步,并且虚拟机已经使用新的 volume 了。接下来 cinder 需要处理一些数据库相关的信息更新。cinder 会交换新旧 volume 在数据库中的元信息,具体交换的内容为 provider-location 和volume-type 这两个字段,该交换过程如图4所示
    OpenStack 存储热迁移OpenStack 存储热迁移

     

    图4 volume信息交换示意图

    现在需要将旧的 volume 的状态改回 attached 并添加新的 attachment 记录,然后把新的 volume 的记录删除。

Ceph RBD 支持

OpenStack 社区目前对 volume 的迁移主要考虑了 LVM 等 iSCSI 协议的后端。对 Ceph RBD 后端的支持比较有限,目前还不支持从 LVM 迁移到 RBD,这时需要进行进一步开发。目前我们进行了相关的测试,使得可以热迁移到 Ceph RBD。

虚拟机系统盘热迁移

虚拟机如果一开始就是从云硬盘启动的,那么我们可以方便地将虚拟机在各个存储后端直接迁移。然而虚拟机如果是从本地镜像启动的,我们想要迁移就比较麻烦了。一种简单的方式是迁移整个虚拟机到其他存储后端的 nova 计算节点上。这里我们介绍另一种方法,直接将虚拟机的本地启动镜像迁移到云硬盘中,同时这个云硬盘还能使用上述的热迁移的方法,进一步迁移到其他存储上。

Libvirt 的 rebase 功能

在讲如何实现迁移到云硬盘功能之前,我们来回顾下 volume 热迁移时提到的 libvirt rebase 方法。Libvirt rebase 主要是用到了 QEMU live block copy 的功能,这个具体在 libvirt 中如何实现我们暂且不讨论。我们来看下 nova 在 swap volume 时如何调用 libvirt rebase 的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

def _swap_volume(self, guest, disk_path, new_path, resize_to):

    """Swap existing disk with a new block device."""

    dev = guest.get_block_device(disk_path)

 

    # Save a copy of the domain's persistent XML file

    xml = guest.get_xml_desc(dump_inactive=True, dump_sensitive=True)

 

    # Abort is an idempotent operation, so make sure any block

    # jobs which may have failed are ended.

    try:

        dev.abort_job()

    except Exception:

        pass

 

    reraise = False

    try:

    # NOTE (rmk): blockRebase cannot be executed on persistent

    # domains, so we need to temporarily undefine it.

    # If any part of this block fails, the domain is

    # re-defined regardless.

    if guest.has_persistent_configuration():

    guest.delete_configuration()

 

    # Start copy with VIR_DOMAIN_REBASE_REUSE_EXT flag to

    # allow writing to existing external volume file

    dev.rebase(new_path, copy=True, reuse_ext=True)

 

    while not dev.is_job_complete():

        time.sleep(0.5)

 

    dev.abort_job(pivot=True)

    # NOTE(alex_xu): domain.blockJobAbort isn't sync call. This

    # is bug in libvirt. So we need waiting for the pivot is

    # finished. libvirt bug #1119173

    while not dev.is_job_complete():

        time.sleep(0.5)

 

    if resize_to:

        dev.resize(resize_to * units.Gi / units.Ki)

    except Exception:

        dev.abort_job()

        reraise = True

    finally:

    # NOTE(mdbooth): We don't know if we're in exception context or

    # not. reraise=False will not fail if we're not in exception

    # context.

        with excutils.save_and_reraise_exception(reraise=reraise):

            self._host.write_instance_config(xml)

注意到 _swap_volume 函数并没有指定旧的磁盘是否必须是 cinder 提供的 volume,如果被迁移的磁盘是本地的系统盘呢?其实也是可以的。

实现迁移系统盘到云硬盘中

考虑到 _swap_volume 函数也能支持将本地的系统盘迁移到 volume 上,我们可以实现新的 nova API 来调用此函数。

基本步骤如下:

  1. 调用 cinder 创建新的 volume
  2. 调用 libvirt driver 来进行热迁移
  3. 对 nova 中虚拟机的信息进行更新,将虚拟机的状态改为从云硬盘启动

 

 

转载自:http://blog.umcloud.com/openstack-block-live-migrate/