logo logo
关于我们

技术分享

技术分享 openstack快照分析

openstack快照分析

2018-06-05

1.openstack快照

1.1.OpenStack快照类别

注意:不要混淆为卷创建快照和为以卷作为后端存储的虚拟机创建快照
虚拟机快照(快照存放于glance):nova image-create <server> <name>
  • cold snapshot:创建snapshot时,需暂停虚拟机。

  • live snapshot with no downtime:创建snapshot时,无需暂停虚拟机。(目前未实现,存在的问题见此链接:http://blog.chinaunix.net/uid-29718549-id-4346700.html

volume backed以外类型的虚拟机,在nova-api中调用API.snapshot接口创建快照(最终实现在createImage接口)
volume backed的虚拟机,在nova-api中调用API.snapshot_volume_backed接口创建虚拟机快照
卷快照(LVM快照存放于本地): nova volume-snapshot-create <volume-id>
在nova-api中调用API.volume_snapshot_create接口创建虚拟机快照

1.2.openstack创建快照命令

nova image-create

usage: nova image-create [--show] [--poll] <server> <name>

Create a new image by taking a snapshot of a running server.

Positional arguments

  • <server>

  • Name or ID of server.

  • <name>

  • Name of snapshot.

Optional arguments

  • --show

  • Print image info.

  • --poll

  • Report the snapshot progress and poll until image creation is complete.

    nova volume-snapshot-create

    usage: nova volume-snapshot-create [--force <True|False>]
                                       [--display-name <display-name>]
                                       [--display-description <display-description>]
                                       <volume-id>

    Add a new snapshot.

    Positional arguments

    • <volume-id>

    • ID of the volume to snapshot

    Optional arguments

    • --force <True|False>

    • Optional flag to indicate whether to snapshot a volume even if its attached to a server. (Default=False)

    • --display-name <display-name>

    • Optional snapshot name. (Default=None)

    • --display-description <display-description>

    • Optional snapshot description. (Default=None)

1.3.流程

cold snapshot 流程:
# Save the state and stop a running guest, then detach pci devices
      $ virsh   managedsave   vm (managedsave流程见此链接:http://blog.chinaunix.net/uid-29718549-id-4772180.html)
# Create a qemu internal snapshot
      $ qemu-img    snapshot   -c   snap1   vm_snapshot
# Extract the internal snapshot, convert it to qcow2 and export it a file, then upload to glance
      $ qemu-img    convert   -f   qcow2   vm   -O   qcow2   vm_snapshot
# Start the guest again
      $ virsh   start   vm

live snapshot 流程:

# Abort any failed/finished block operations:

     $ virsh blockjob vm vda --abort

# Undefine a running domain. (Note: Undefining a running domain does not _kill_ the domain, it just converts it from persistent to transient.)

     $ virsh undefine vm

# create a destination image with the original backing file and matching size of the instance root disk.

     $ qemu-img create -f qcow2 vm_copy --backing_file=backing_file --size=root_disk_size

#Invoke 'virsh blockcopy' (This will take time, depending on the size of disk image vm1):

     $ virsh blockcopy --domain vm vda vm_copy --wait --verbose

#Abort any failed/finished block operations:

    $ virsh blockjob vm vda --abort

#Define the guest again (to make it persistent):

    $ virsh define vm

#From the obtained new copy, convert the QCOW2 with a backing file to a qcow2 image with no backing file, then upload to glance:

    $ qemu-img convert -f qcow2 -O raw vm_copy vm_convert

2.与传统快照的区别

对openstack而言,虚拟机的快照其实就是镜像。这是因为Openstack是采用创建镜像的方式创建快照(qemu-img convert -f qcow2 -O qcow2 <disk_path>  <out_path>),而不是通过正统的virsh或者qemu的live snapshot创建快照函数( virsh  snapshot-create-as --live ....)来实现的。虚拟机快照做完后以镜像形式存于glance。openstack的快照与传统快照有以下区别
  • libvirt 主流快照实现: 采用virDomainSnapshotCreateXML()函数(CLI为virsh snapshot-create)。新建的快照与虚拟机有关联:若为内置快照,快照信息和虚拟机存在同一个qcow2镜像中;若为外置快照,新建一个qcow2文件,原虚拟机的disk将变为一个read only的模板镜像,新qcow2镜像仅记录与2.模板镜像的差异数据。这种快照包含快照链信息,可保留disk和ram信息,可回滚至快照点

  • openstack快照实现:openstack并未采用virDomainSnapshotCreateXML()来实现快照,而是单纯的对虚拟机镜像做转换和拷贝,生成一个与虚拟机无关联的镜像,最后上传至glance中。这种快照不包含快照链信息,只保留disk信息,无法回滚至快照点,只能采用该快照镜像创建一个新的虚拟机。

制与缺点

  • 没有快照链信息,不支持revert恢复虚拟机到某一个快照点

  • 只对系统盘进行快照,

  • 不支持内存快照,不支持同时对虚拟机和磁盘做快照

  • 需要用户进行一致性操作

  • 不支持含元数据导出,

  • 不支持含元数据导入

  • 只支持虚拟机全量数据快照(与快照的实现方式有关,因为是通过image进行保存的)

  • 过程较长(需要先通过存储快照,然后抽取并上传至glance)

  • 快照以Image方式保存,而非以cinder卷方式保存,无法充分利用存储本身能力加快快照的创建和使用

  • 当前限制openstack的虚拟机快照只快照root盘,不快照内存/CPU状态以及挂载磁盘。挂载磁盘需要事先卸载磁盘(数据盘),然后进行快照,然后再挂载磁盘

  • 没有快照链信息

3.OpenStack快照源代码流程分析

3.1 nova/compute/api.py

  1. # NOTE(melwitt): We don't check instance lock for snapshot because lock is

  2.     # intended to prevent accidental change/delete of instances

  3.     @wrap_check_policy

  4.     @check_instance_cell

  5.     @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,

  6.                                     vm_states.PAUSED, vm_states.SUSPENDED])

  7.     def snapshot(self, context, instance, name, extra_properties=None):

  8.         """Snapshot the given instance.


  9.         :param instance: nova.db.sqlalchemy.models.Instance

  10.         :param name: name of the snapshot

  11.         :param extra_properties: dict of extra image properties to include

  12.                                  when creating the image.

  13.         :returns: A dict containing image metadata

  14.         """

  15.        #_create_image函数中调用glance api创建image entry,为后将snapshot上传为镜像做准备虽然镜像和snapshot在可以上传到glance作为镜像启动虚拟机,

  16.        #但是为了区分二者的不同,glance将镜像和snapshot标记为不同的类型:type=image 和 type=snapshot

  17.         image_meta = self._create_image(context, instance, name,

  18.                                         'snapshot',

  19.                                         extra_properties=extra_properties)


  20.         # NOTE(comstud): Any changes to this method should also be made

  21.         # to the snapshot_instance() method in nova/cells/messaging.py

  22.         # 将任务状态(task state) 设置为:image_snapshot_pending

  23.         instance.task_state = task_states.IMAGE_SNAPSHOT_PENDING

  24.         instance.save(expected_task_state=[None])

  25.         #通过rpc调用nova/compute/rpcapi.py的snapshot_instance函数

  26.         self.compute_rpcapi.snapshot_instance(context, instance,

  27.                                               image_meta['id

3.2 nova/compute/rpcapi.py

  1. #梳理下流程: 

  2. (1)用户发起create snapshot的请求; 

  3. (2)nova-api服务接收到这个请求并进行前期处理,即3.1中代码的处理流程;

  4. (3)真正的snapshot操作是需要在nova-compute节点上执行的,所以nova-api需要向nova-compute发送message

  5. #由于OpenStack环境中会有很多个nova-compute,所以需要通过server=_compute_host(None, instance)来获取虚拟机所在的host,并向其发送message。

  6. def snapshot_instance(self, ctxt, instance, image_id):

  7.         version = '3.0'

  8.         cctxt = self.client.prepare(server=_compute_host(None, instance),

  9.                 version=version)

  10.         cctxt.cast(ctxt, 'snapshot_instance',

  11.                    instance=instance,

  12.                    image_id=image_id)

3.3 nova/virt/libvirt/driver.py

  1.  def snapshot(self, context, instance, image_id, update_task_state):

  2.         """Create snapshot from a running VM instance.


  3.         This command only works with qemu 0.14+

  4.         """

  5.         try:

  6.             virt_dom = self._get_domain(instance)

  7.         except exception.InstanceNotFound:

  8.             raise exception.InstanceNotRunning(instance_id=instance['uuid'])


  9.         base_image_ref = instance['image_ref']


  10.         base = compute_utils.get_image_metadata(

  11.             context, self._image_api, base_image_ref, instance)


  12.         snapshot = self._image_api.get(context, image_id)


  13.         disk_path = libvirt_utils.find_disk(virt_dom)

  14.         source_format = libvirt_utils.get_disk_type(disk_path)


  15.         image_format = CONF.libvirt.snapshot_image_format or source_format


  16.         # NOTE(bfilippov): save lvm and rbd as raw

  17.         if image_format == 'lvm' or image_format == 'rbd':

  18.             image_format = 'raw'


  19.         metadata = self._create_snapshot_metadata(base,

  20.                                                   instance,

  21.                                                   image_format,

  22.                                                   snapshot['name'])


  23.         snapshot_name = uuid.uuid4().hex


  24.         state = LIBVIRT_POWER_STATE[virt_dom.info()[0]]


  25.         # NOTE(rmk): Live snapshots require QEMU 1.and Libvirt 1.0.0.

  26.         # These restrictions can be relaxed as other configurations

  27.         # can be validated.

  28.         # NOTE(dgenin): Instances with LVM encrypted ephemeral storage require

  29.         # cold snapshots. Currently, checking for encryption is

  30.         # redundant because LVM supports only cold snapshots.

  31.         # It is necessary in case this situation changes in the

  32.         # future.

  33.         #这里需要注意,解释了为啥现在是cold snapshot而不是live snapshot:

  34.         # 有人提过live snapshot的bug,社区认为live snapshot目前不稳定,所以默认条件下采用cold snapshot,并且是通过硬编码来实现的

  35.         # 看下面这个判断条件,成立的时候将live_snapshot = true,其中MIN_LIBVIRT_LIVESNAPSHOT_VERSION=1.3.0, 其实现在libvirt的最新版本

  36.         # 才到1.2.11, 所以这个live_snapshot的条件不满足,就变成了cold_snapshot

  37.         if (self._host.has_min_version(MIN_LIBVIRT_LIVESNAPSHOT_VERSION,

  38.                                        MIN_QEMU_LIVESNAPSHOT_VERSION,

  39.                                        REQ_HYPERVISOR_LIVESNAPSHOT)

  40.              and source_format not in ('lvm', 'rbd')

  41.              and not CONF.ephemeral_storage_encryption.enabled):

  42.             live_snapshot = True

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

  44.             # jobs which may have failed are ended. This operation also

  45.             # confirms the running instance, as opposed to the system as a

  46.             # whole, has a new enough version of the hypervisor (bug 1193146).

  47.             try:

  48.                 virt_dom.blockJobAbort(disk_path, 0)#live第一步

  49.             except libvirt.libvirtError as ex:

  50.                 error_code = ex.get_error_code()

  51.                 if error_code == libvirt.VIR_ERR_CONFIG_UNSUPPORTED:

  52.                     live_snapshot = False

  53.                 else:

  54.                     pass

  55.         else:

  56.             live_snapshot = False


  57.         # NOTE(rmk): We cannot perform live snapshots when a managedSave

  58.         # file is present, so we will use the cold/legacy method

  59.         # for instances which are shutdown.

  60.         if state == power_state.SHUTDOWN:

  61.             live_snapshot = False


  62.         # NOTE(dkang): managedSave does not work for LXC

  63.         #注意这里,如果live_snashot目前是false,在做snapshot之前先要执行:

  64.         #(1)_detach_pci_devices, 卸载虚拟机挂载的pci设备,比如数据盘

  65.         #(2)self._detach_sriov_ports, 卸载虚拟机挂载的SRIOV设备,比如支持SRIOV的网卡设备

  66.         if CONF.libvirt.virt_type != 'lxc' and not live_snapshot:

  67.             if state == power_state.RUNNING or state == power_state.PAUSED:

  68.                 self._detach_pci_devices(virt_dom,

  69.                     pci_manager.get_instance_pci_devs(instance))

  70.                 self._detach_sriov_ports(instance, virt_dom)

  71.                 virt_dom.managedSave(0)#cold第一步

  72.         #判断虚拟机的后端存储是什么,不同的后端存储做snapshot是不同的,本地文件系统的话,默认qcow2

  73.         snapshot_backend = self.image_backend.snapshot(instance,

  74.                 disk_path,

  75.                 image_type=source_format)


  76.         if live_snapshot:

  77.             LOG.info(_LI("Beginning live snapshot process"),

  78.                      instance=instance)

  79.         else:

  80.             LOG.info(_LI("Beginning cold snapshot process"),

  81.                      instance=instance)

  82.         #更新任务的状态为:image_pending_upload, 大家都知道做完snapshot要上传

  83.         update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)

  84.         #目前做快照的过程是:

  85.         #(1)先在../data/nova/instance/snapshots目录下生成临时目录,比如nova/instances/snapshots/tmptHr585

  86.         #(2)然后将快照生成到这个目录,具体参见snapshot_backend.snapshot_extract(out_path, image_format)这个函数

  87.         #(3)生成完成后,通过glance api上传,具体参见 self._image_api.update

  88.         snapshot_directory = CONF.libvirt.snapshots_directory

  89.         fileutils.ensure_tree(snapshot_directory)

  90.         with utils.tempdir(dir=snapshot_directory) as tmpdir:

  91.             try:

  92.                 out_path = os.path.join(tmpdir, snapshot_name)

  93.                 if live_snapshot:

  94.                     # NOTE(xqueralt): libvirt needs o+in the temp directory

  95.                     os.chmod(tmpdir, 0o701)

  96.                     self._live_snapshot(context, instance, virt_dom, disk_path,

  97.                                         out_path, image_format, base)#热迁移

  98.                 else:

  99.                     #这个函数实际执行了一条命令: qemu-img convert -f qcow2 -O qcow2  disk_path out_path,算是生成了快照

  100.                     snapshot_backend.snapshot_extract(out_path, image_format)

  101.             finally:

  102.                 new_dom = None

  103.                 # NOTE(dkang): because previous managedSave is not called

  104.                 # for LXC, _create_domain must not be called.

  105.                 if CONF.libvirt.virt_type != 'lxc' and not live_snapshot:

  106.                     if state == power_state.RUNNING:

  107.                         new_dom = self._create_domain(domain=virt_dom)   ##恢复做快照之前虚拟机的状态

  108.                     elif state == power_state.PAUSED:

  109.                         new_dom = self._create_domain(domain=virt_dom,

  110.                                 launch_flags=libvirt.VIR_DOMAIN_START_PAUSED)

  111.                     if new_dom is not None:

  112.                         self._attach_pci_devices(new_dom,

  113.                             pci_manager.get_instance_pci_devs(instance))

  114.                         self._attach_sriov_ports(context, instance, new_dom)

  115.                 LOG.info(_LI("Snapshot extracted, beginning image upload"),

  116.                          instance=instance)


  117.             # Upload that image to the image service


  118.             update_task_state(task_state=task_states.IMAGE_UPLOADING,

  119.                      expected_state=task_states.IMAGE_PENDING_UPLOAD)

  120.             with libvirt_utils.file_open(out_path) as image_file:       ###将生成的快照上传到glance

  121.                 self._image_api.update(context,

  122.                                        image_id,

  123.                                        metadata,

  124.                                        image_file)

  125.                 LOG.info(_LI("Snapshot image upload complete"),

  126.                          instance=instance)




云祺备份软件,云祺容灾备份系统,虚拟机备份,数据库备份,文件备份,实时备份,勒索软件,美国,图书馆
  • 标签:
  • 云计算

您可能感兴趣的新闻 换一批

现在下载,可享30天免费试用

立即下载

请添加好友为您提供支持
jia7jia_7

请拨打电话
为您提供支持

400-9955-698