OpenStack快照分析:(二)从镜像启动的云主机离在线快照分析

从镜像启动的云主机在线快照

之前介绍了‘镜像启动云主机的离线快照’,接下来介绍‘镜像启动云主机的在线快照’,在线快照的处理与离线快照的处理大体上一样,只是nova-compute在处理的时候有差异,差别代码如下(代码路径:nova/virt/libvirt/driver.py/LibvirtDriver.snapshot)

def snapshot(self, context, instance, image_id, update_task_state):
   
if (self._host.has_min_version(hv_type=host.HV_DRIVER_QEMU)
        
and source_type not in ('lvm')
        
and not CONF.ephemeral_storage_encryption.enabled
        
and not CONF.workarounds.disable_libvirt_livesnapshot):
        live_snapshot =
True
       
# Abort is an idempotent operation, so make sure any block
        # jobs which may have failed are ended. This operation also
        # confirms the running instance, as opposed to the system as a
        # whole, has a new enough version of the hypervisor (bug 1193146).
       
try:
            guest.get_block_device(disk_path).abort_job()
       
except libvirt.libvirtError as ex:
            error_code = ex.get_error_code()
           
if error_code == libvirt.VIR_ERR_CONFIG_UNSUPPORTED:
                live_snapshot =
False
           
else:
               
pass
    else
:
        live_snapshot =
False

可以发现在做live_snapshot的时候,会调用abort_job()来停止系统盘上的所有任务,即就是调用_guest._domain.blockJobAbort(self._disk, flags=flags)

 接着还是会做生成临时目录的操作,即:

with utils.tempdir(dir=snapshot_directory) as tmpdir:
   
try:
        out_path = os.path.join(tmpdir
, snapshot_name)
        LOG.info(
'out_path: %s', out_path)
       
if live_snapshot:
           
# NOTE(xqueralt): libvirt needs o+x in the tempdir
           
os.chmod(tmpdir, 0o701)
           
self._live_snapshot(context, instance, guest, disk_path, out_path, source_format,
                               
image_format, instance.image_meta)
       
else:
            root_disk.snapshot_extract(out_path
, image_format)
   
finally:
       
self._snapshot_domain(context, live_snapshot, virt_dom, state, instance)
        LOG.info(
"Snapshot extracted, beginning image upload",
                
instance=instance)

因为是在线快照,满足live_snapshot的条件,首先会给临时的快照目录701的访问权限,接着调用方法“self._live_snapshot”来做具体的快照操作,其中传递的参数为:

l  context:上下文,主要是安全及权限方面的内容

l  instance:实例的对象

l  guest:实例对象对应的虚机domain

l disk_pathsource_format分别表示系统盘的路径及格式类型

l  out_pathimage_format分别表示临时快照文件路径及格式类型

l  image_meta:是镜像的metadata信息

实际上是在调用后端存储驱动执行快照,例如执行使用ceph则是使用Rbd.snapshot_extract,内部实现为调用'qemu-img convert'拷贝系统磁盘到out_path文件中,命令如下:

qemu-img convert -O raw rbd:vms/814a8ad8-9217-4c45-91c7-c2be2016e5da_disk:id=cinder:conf=/etc/ceph/ceph.conf' /opt/stack/data/nova/instances/snapshots/tmptR6hog/e44639af86434069b38f835847083697  -f raw

在线快照实际上是调用“_live_snapshot”“_live_snapshot”内容如下:

def _live_snapshot(self, context, instance, guest, disk_path, out_path,
                  
source_format, image_format, image_meta):
   
"""Snapshot an instance without downtime."""
    # 创建一个BlockDevice对象
   
dev = guest.get_block_device(disk_path)
   
# 获取实例的xml配置
   
xml = guest.get_xml_desc(dump_inactive=True, dump_sensitive=True)

   
try:
      
 # 快照前结束所有磁盘作业
        dev.abort_job()
   
except Exception:
       
pass
   
# 通过qemu-img获取磁盘的virtual size
    src_disk_size = libvirt_utils.get_disk_size(disk_path, format=source_format)
   
# 通过qemu-img info获取系统磁盘的backing_file
    src_back_path = libvirt_utils.get_disk_backing_file(disk_path
,
                                                   
format=source_format,
                                                   
basename=False)
    #
组装一个完整的快照文件名称
    disk_delta = out_path +
'.delta'
    """
   
接着通过create_cow_image创建一个cow文件,实际上是调用qemu-img create来完成的,命令如下:
        qemu-img create -f qcow2 -o backing_file=$src_back_path cluster_size=$cluster_size size=$src_disk_size disk_delta
    """
   
libvirt_utils.create_cow_image(src_back_path, disk_delta, src_disk_size)

    quiesced =
False
   
try:
       
self._set_quiesced(context, instance, image_meta, True)
        quiesced =
True
   
except exception.NovaException as err:
       
if self._requires_quiesce(image_meta):
           
raise
       
LOG.info('Skipping quiescing instance: %(reason)s.',
                
{'reason': err}, instance=instance)

   
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.
      
 # 有支持持久配置和支持uefi的实例不支持rebase,需要先undefine掉相关配置
        if guest.has_persistent_configuration():
            support_uefi =
self._has_uefi_support()
            guest.delete_configuration(support_uefi)

       
# disk_delta指的是在上文中创建的qcow2格式的cow文件。
       
dev.rebase(disk_delta, copy=True, reuse_ext=True, shallow=True)
       
# 判断当前是否有磁盘任务未结束,如果有则sleep 0.5秒,知道所有任务结束
       
while not dev.is_job_complete():
            time.sleep(
0.5)

        dev.abort_job()
        libvirt_utils.chown(disk_delta
, os.getuid())
   
finally:
       
# 重新恢复实例的xml文件
       
self._host.write_instance_config(xml)
       
if quiesced:
           
self._set_quiesced(context, instance, image_meta, False)

   
# Convert the delta (CoW) image with a backing file to a flat
    # image with no backing file.
   
libvirt_utils.extract_snapshot(disk_delta, 'qcow2',
                                  
out_path, image_format)

上面执行在线快照的代码中调用到了libvirt_utils.create_cow_image(src_back_path, disk_delta,  src_disk_size),这一句才是做快照的底层实现,如下:

def create_cow_image(backing_file, path, size=None):
   
"""Create COW image

    Creates a COW image with the given backing file

    :param backing_file: Existing image on which to base the COW image
    :param path: Desired location of the COW image
    """
   
base_cmd = ['qemu-img', 'create', '-f', 'qcow2']
    cow_opts = []
   
if backing_file:
        cow_opts += [
'backing_file=%s' % backing_file]
        base_details = images.qemu_img_info(backing_file)
   
else:
        base_details =
None
   
# Explicitly inherit the value of 'cluster_size' property of a qcow2
    # overlay image from its backing file. This can be useful in cases
    # when people create a base image with a non-default 'cluster_size'
    # value or cases when images were created with very old QEMU
    # versions which had a different default 'cluster_size'.
   
if base_details and base_details.cluster_size is not None:
        cow_opts += [
'cluster_size=%s' % base_details.cluster_size]
   
if size is not None:
        cow_opts += [
'size=%s' % size]
   
if cow_opts:
       
# Format as a comma separated list
       
csv_opts = ",".join(cow_opts)
        cow_opts = [
'-o', csv_opts]
    cmd = base_cmd + cow_opts + [path]
    execute(*cmd)

可以看出底层实际上还是调用qemu-img create –f qcow2 ****来实现的。

在线快照源码就分析到这里,与离线快照最大的区别就是:在线快照不会挂起实例;相同点是:都需要先在本地生成临时快照文件,再上传到glance

原文地址:https://www.cnblogs.com/qianyeliange/p/9713022.html