在Ceph中创建虚拟机流程改进之分析

作为个人学习笔记分享,有任何问题欢迎交流!

最近在Gerrit中看到一个change:https://review.openstack.org/#/c/94295/ , 它主要是对当前在Ceph中创建虚拟机的流程的改进。如果glance的backend是ceph, 则nova创建虚拟机到RBD的流程是这样的:

通过glance从ceph中下载image --> 本地 --> 复制image到rbd

这个change的目的就是:不需要下载到本地,直接在rbd中复制image,以提高虚拟机创建的速度。

以前只知道nova从glance下载image,作为虚拟机的base,却没有仔细的了解过这个过程,正好借这个机会,看一看nova创建虚拟机到rbd过程中关于image的部分。

1 nova创建VM时image的流程

经过nova-scheduler选择节点后,创建VM的请求到达了nova-compute,即nova/compute/manager.py:ComputeManager._run_instance():

def _run_instance(self, context, request_spec,
                      filter_properties, requested_networks, injected_files,
                      admin_password, is_first_time, node, instance,
                      legacy_bdm_in_spec):
 

关于镜像的元数据就保存在request_spec,

image_meta = request_spec['image']

取得元数据后就开始build_instance()了,但是下面的过程与image没太大关系,所以从简带过。

def _build_instance(self, context, request_spec, filter_properties,
            requested_networks, injected_files, admin_password,   is_first_time,
node, instance, image_meta, legacy_bdm_in_spec)
---->
def _spawn(self, context, instance, image_meta, network_info,
               block_device_info, injected_files, admin_password,
               set_access_ip=False)
---->

self.driver.spawn(context, instance, image_meta,
                              injected_files, admin_password,
                              network_info,
                              block_device_info)

这里的driver就是你用的Hypervioser, 我用的是KVM,所以这个driver.spawn=nova/virt/libvirt/driver.py:LibvirtDriver.spawn():

def spawn(self, context, instance, image_meta, injected_files,
              admin_password,network_info=None,  block_device_info=None):
       disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
                                            instance,
                                            block_device_info,
                                            image_meta)
        self._create_image(context, instance,
                           disk_info['mapping'],
                           network_info=network_info,
                           block_device_info=block_device_info,
                           files=injected_files,
                           admin_pass=admin_password)

那么这个disk_info[‘mapping‘]是什么呢?这里有一个方法,我们可以从test_libvirt_blockinfo.py里找到答案,所以结合测试用例来看代码真的很有用。在Nova/tests/virt/libvirt/test_libvirt_blockinfo.py:

LibvirtBlockInfoTest.test_get_disk_mapping_simple_swap()里可以看到:

      expect = {
            'disk': {'bus': 'virtio', 'dev': 'vda',
                     'type': 'disk', 'boot_index': '1'},
            'disk.local': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'},
            'root': {'bus': 'virtio', 'dev': 'vda',
                     'type': 'disk', 'boot_index': '1'}
            }

Expect就是期望disk_info[‘mapping‘]的样子。

下面就开始创建VM的image了:

    def _create_image(self, context, instance,
                      disk_mapping, suffix='',
                      disk_images=None, network_info=None,
                      block_device_info=None, files=None,
                      admin_pass=None, inject_files=True):
#下面这个函数就是返回VM image格式的相关类,位于 #libvirt/imagebackend.py中,这里image是rbd, 返回的就是Rbd    #类。
def image(fname, image_type=CONF.libvirt.images_type):
            return self.image_backend.image(instance,
                                     fname + suffix, image_type)
......

  if not booted_from_volume:
            root_fname = imagecache.get_cache_fname(disk_images, 'image_id')#以image id作为文件名
            size = instance['root_gb'] * units.Gi

            if size == 0 or suffix == '.rescue':
                size = None

#这里有点复杂,用到了回调函数fetch_image,这里的cache是Rbd的父类#Image类的cache(),主要功能是从模板,也就是glance的image, 为VM创建一个image.
           image('disk').cache(fetch_func=libvirt_utils.fetch_image,
                                context=context,
                                filename=root_fname,
                                size=size,
                                image_id=disk_images['image_id'],
                                user_id=instance['user_id'],
                               project_id=instance['project_id'])
#可以在cache()中看到:
        if not self.check_image_exists() or not os.path.exists(base):
            self.create_image(fetch_func_sync, base, size,
                              *args, **kwargs)

那么现在到class Rbd下可以找到create_image():

 def create_image(self, prepare_template, base, size, *args, **kwargs):
        if self.rbd is None:
            raise RuntimeError(_('rbd python libraries not found'))

        if not os.path.exists(base):
            prepare_template(target=base, max_size=size, *args, **kwargs)##这里的prepare_temple()就是 libvirt_utils.fetch_image啦。
 

libvirt_utils.fetch_image=nova/virt/libvirt/utils.fetch_image():

def fetch_image(context, target, image_id, user_id, project_id, max_size=0):
    """Grab image."""
    images.fetch_to_raw(context, image_id, target, user_id, project_id,
                        max_size=max_size)
--->

def fetch_to_raw(context, image_href, path, user_id, project_id, max_size=0):
    path_tmp = "%s.part" % path
    fetch(context, image_href, path_tmp, user_id, project_id,
          max_size=max_size)

---->
def fetch(context, image_href, path, _user_id, _project_id, max_size=0):
(image_service, image_id) = glance.get_remote_image_service(
context, image_href)#从glance获取image
with fileutils.remove_path_on_error(path):
        #这里就是把image_id的数据download到path了。Download()位于   #nova/image/glance.py。
        image_service.download(context, image_id, dst_path=path)

回到class Rbd的create_image()中,

libvirt_utils.import_rbd_image(*args)把path的image数据写入rbd,至此,整个流程就到这里结束了。

2 Change中的改进

现在回到文章开始中提到的那个change, 看看它是怎么实现的。

首先它在fetch_to_raw中加入了一个判断。

def fetch_to_raw(context, image_href, path, user_id, project_id,  max_size=0):
#判断backend是否具有‘direct_fetch’的属性,如果有,则直接返回#direct_fetch()
    if backend and hasattr(backend, 'direct_fetch'):
        try:
            return backend.direct_fetch(context, image_href)
        except exception.ImageUnacceptable:
            LOG.debug(_('could not fetch directly, falling back to download'))

给Rbd类添加了一个属性:

    def direct_fetch(self, context, image_href):
#判断driver是否支持layering(分层
#http://ceph.com/docs/firefly/dev/rbd-layering/ ,指的是块设备
#的cow克隆,支持快速创建image)
        if not self.driver.supports_layering():
            reason = _('installed version of librbd does not support cloning')
            raise exception.ImageUnacceptable(image_id=image_href,
                                              reason=reason)

        image_meta, locations = images.get_meta(context, image_href)
        LOG.debug(_('Image locations are: %(locs)s') % {'locs': locations})

        if image_meta.get('disk_format') not in ['raw', 'iso']:
            reason = _('Image is not raw format')
            raise exception.ImageUnacceptable(image_id=image_href,
                                              reason=reason)
#克隆镜像(http://ceph.com/docs/master/rbd/librbdpy/)
        for location in locations:
            if self.driver.is_cloneable(location, image_meta):
                return self.driver.clone(location, self.rbd_name)

        reason = _('No image locations are accessible')
        raise exception.ImageUnacceptable(image_id=image_href, reason=reason)

这样就不需要想1中的那样先把image下载到local, 在写到rbd中,直接在rbd中克隆,从而提高了虚拟机的创建速度。

3总结

借这个机会既熟悉了创建VM时的image流程,又熟悉了ceph的用法,同时学习了高手们是怎么实现一个功能的,看来review的益处大大的呀。:)

在Ceph中创建虚拟机流程改进之分析,布布扣,bubuko.com

时间: 2024-12-15 01:40:12

在Ceph中创建虚拟机流程改进之分析的相关文章

Azure 动手演示之一:采用定制化方法在 Azure 中创建虚拟机

也许大家看过很多关于 Windows Azure (Microsoft Azure) 介绍或如何使用的文章,但即使看过很多图片或 PPT,也难以真正理解这一全球领先的公有云平台. 鉴于此,我制作了一系列如何使用 Azure 的视频录像,主要是实时录制我在屏幕上对 Azure 的操作.希望能对大家深入的了解 Windows Azure (Microsoft Azure) 有所帮助. 本演示看上去非常简单,目的是如何在 Azure 上创建虚拟机,但事实上,它是如何在 Azure 上创建整个企业部署的

Nova创建虚拟机的底层代码分析

作为个人学习笔记分享.有不论什么问题欢迎交流! 在openstack中创建虚拟机的底层实现是nova使用了libvirt,代码在nova/virt/libvirt/driver.py. #image_meta:镜像的相关内容,#injected_files:要注入到VM的文件 #network_info:网络相关信息.block_device_info:磁盘相关信息 def spawn(self, context, instance, image_meta, injected_files, ad

OpenStack创建虚拟机流程

云主机创建流程图: dashboard发创建云主机的请求是先到novaclient,再由novaclient以http的形式发送到nova-api端,我们这里直接从nova端讲起,通过wsgi映射匹配,API映射匹配可以看我的另一篇博客:OpenStack Restful API框架介绍 创建云主机会首先调用到nova/api/openstack/compute/servers.py文件中的create()函数: @wsgi.response(202) @extensions.expected_

Azure ARM Portal 创建虚拟机流程

Azure ARM Portal已经上线很长一段时间了,这篇文章非常基础,写这一系列文章也是因为近期在接触客户中,发现很多客户非常需要这一些最基础的帮助.因此我也花了一点时间重头整理了下Azure上的基础使用文档,首先就从创建虚拟机开始 1.登陆portal.azure.cn,国际版登录portal.azure.com,点击左侧菜单,选择资源组,然后创建一个新的资源组. 2.为资源组命名,选择地理位置. 3.完成资源组创建后,继续创建虚拟网络,如下 4.为虚拟网络选择资源组 5.完成网络创建后开

无法在父文件夹中创建虚拟机文件夹

在VirtualBox中创建虚拟机时,出现以下错误 解决方法:在”管理“——>"全局设定" ——> "常规"中更改“默认虚拟电脑位置“的路径即可解决.

二、Windows Server 2012R2 Hyper-v在Hyper-v中创建虚拟机

1.服务器(右键)->新建->虚拟机 2.下一步 3.键入虚拟机的名字,并制定虚拟机存储位置 4.安装Win7需要选择第一代虚拟机 5.给虚拟机分配内存 6.连接暂时不做分配 7.根据需求选择虚拟硬盘大小及位置 8.选择以后安装系统 9.完成 10.

PowerShell 在hyper-v中创建虚拟机

# This script configures the Hyper-V machines used for the 50331 Course. # PowerShell 3.0 and Windows Server 2012 or Windows 8 Pro are required to perform this setup # The C:\ Drive should have at least 200GB of free space available. # All the files

SCVMM2008R2学习(七),利模板创建虚拟机--下

在上一篇博文中我从虚拟机创建了模板,在本篇博文中我将利用模板来加速创建虚拟机看看和虚拟机克隆有什么区别. 如下图,选择"新建虚拟机" 选择新虚拟机的源,这里我们选"使用现有的虚拟机,模板或虚拟硬盘",选择"浏览" 这里我们选择"模板"即2003template,选择确定 返回后选择下一步 虚拟机名称(在SCVMM管理器中显示的名称),这里为随便起个叫做2003ABC,选择下一步 这里的配置文件根据自己的实际情况进行修改,这里我

在Hyper-V虚拟机中创建差异磁盘克隆系统

Hyper-V虚拟机差异磁盘克隆系统 1.  首先去掉模板的SID值,这里用2003来举例,在2003的安装盘中拷贝这个两个文件到桌面或者C盘 2.  执行其中的sysprep.exe文件,如图 等关机之后在把VHD磁盘拷贝到另外位置,这个拷贝的文件就是你差异磁盘的文件 3.  在Hyper-V服务器上面选择新建磁盘,在新建磁盘类型中选择差异 4.  在指定名称和位置这里写上你名称和位置 5.  给查差异磁盘指定父盘,父盘就是刚才关机之后拷贝的那个文件 然后选择下一步,点击完成,差异磁盘就算创建