• 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

  • 相关阅读:
    MongoDB 常用命令
    jpa一对多
    jpa的常用注解
    jpa常用方法
    springdata查询的多种用法
    springdata笔记
    java 动态代理 demo 实现原理 参数理解(三)
    Solr
    一些重要的知识点-数据库优化-angularjs--JSON-fastDFS
    支付功能
  • 原文地址:https://www.cnblogs.com/qianyeliange/p/9713022.html
Copyright © 2020-2023  润新知