分享

云硬盘启动与镜像启动源码分析及差异

desehawk 发表于 2016-11-25 17:57:49 [显示全部楼层] 只看大图 回帖奖励 阅读模式 关闭右栏 0 11287
问题导读

1.从云硬盘启动实例,源码是如何实现的?
2.如何从镜像启动实例?
3.二者有何不同?





云硬盘启动虚拟机

先使用cinder创建云硬盘,然后在nova中创建示例的时候,会先在_prep_block_device中挂载cinder中创建的卷,然后创建虚拟机

流程如下:
[mw_shl_code=bash,true](1)从cinder创建系统盘的流程  
cinder.volume.rpcapi.VolumeAPI.create_volume  
->cinder.volume.manager.VolumeManager.create_volume  
->cinder.volume.flows.manager.create_volume.get_flow  
->cinder.volume.flows.manager.create_volume.CreateVolumeFromSpecTask.execute  
->cinder.volume.flows.manager.create_volume.CreateVolumeFromSpecTask._create_from_image (_create_raw_volume、_create_from_snapshot)  
->cinder.volume.derivers.glusterfs.create_volume (这里是具体驱动的create_volume, VS底层使用glusterfs)  
->cinder.volume.derivers.glusterfs._do_create_volume (这里很重要,可能是我们的切入点)  
->cinder.volume.derivers.remotefs._create_sparsed_file(class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver)会调到父类的方法)  
    ->cinder.volume.flows.manager.create_volume.CreateVolumeFromSpecTask._copy_image_to_volume  
->cinder.volume.derivers.remotefs.copy_image_to_volume(class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver)会调到父类的方法)  
->cinder.image.image_utils.fetch_to_raw  
->cinder.image.image_utils.fetch_to_volume_format  
    ->cinder.image.image_utils.fetch (从fetch到fetch再到download和2.6中的流程很像了)  
->cinder.image.glance.download(从glance下载镜像到卷)  
->cinder.volume.flows.manager.create_volume.CreateVolumeFromSpecTask._handle_bootable_volume_glance_meta (设置为可启动)  
创建镜像完毕  
  
(2)nova中从云硬盘启动虚拟机的流程  
从云硬盘启动还是从虚拟机启动,在如下地方有差别:  
A 在Nova.virt.libvirt.driver._create_image中有差别  
nova.api.openstack.compute.servers.Controller.create  
->nova.compute.api.API.create  
->nova.compute.api.API._create_instance  
->nova.comductor.ComputeTaskAPI.build_instances  
->nova.conductor. rpcapi.ComputeTaskAPI.build_instances  
->nova.conductor. manager.ComputeTaskAPI.build_instances  
->nova.compute.rpcapi.ComputeManager.build_and_run_instance  
->nova.compute.manager.ComputeManager.build_and_run_instance   
->nova.compute.manager.ComputeManager._locked_do_build_and_run_instance  
->nova.compute.manager.ComputeManager._do_build_and_run_instance  
->nova.compute.manager.ComputeManager._build_and_run_instance  
  ->nova.compute.manager.ComputeManager._build_networks_for_instance //准备网络资源  
  ->nova.compute.manager.ComputeManager._prep_block_device //准备块设备  
    ->nova.compute.manager.ComputeManager.attach_block_devices   
      ->nova.virt.block_device.DriverImageBlockDevice.attach:volume_api.attach  
        ->Nova.volume.API.attach  
          ->nova.volume.cinder.API.attach  
            -> client = cinderclient(context) ; client.volumes.attach(size, **kwargs)//挂载cinder创建好的卷(云硬盘)  
->nova.virt.libvirt.driver.LibvirtDriver.spawn   
->Nova.virt.libvirt.driver._create_image //创建系统盘  
这里会检查是从云硬盘启动,还是不从云硬盘启动:  
booted_from_volume = self._is_booted_from_volume(  
            instance, disk_mapping)  
...  
if not booted_from_volume:  
->Nova.virt.libvirt.driver._try_fetch_image_cache  
->Nova.virt.libvirt.imagebackend.Image.cache  
->Nova.virt.libvirt.imagebackend.Qcow2.create_image  
   ->Nova.virt.libvirt.imagebackend.Qcow2.create_image:prepare_template即fetch_image  
     ->Nova.virt.libvirt.utils.fetch_image  
       ->Nova.virt.images.fetch_to_raw  
         ->Nova.virt.images.fetch            #下载系统镜像到/var/lib/nova/instances/_base/目录  
           ->nova.image.glance.GlanceImageService.download  
             ->nova.image.glance.GlanceClientWrapper.call  
  ->Nova.virt.images.convert_image    #若镜像不是raw格式,且nova.conf中force_raw_images=True,则将backing file强制转换为raw格式  
  ->Nova.virt.libvirt.imagebackend.Qcow2.create_image:copy_qcow2_image  
  ->nova.virt.libvirt.utils.create_cow_image  [/mw_shl_code]


镜像启动

nova还有一种启动方式:“从镜像启动(创建一个新卷)”

这个流程中,nova会在_prep_block_device中的attach_block_device去调用cinder的create创建一个卷

然后会在_prep_block_device中的attach_block_device去调用cinder的attach挂载这个卷

然后才去spawn,孵化虚拟机,在spawn中会调用_create_image来创建镜像,这里创建镜像不是创建卷,卷是在_prep_block_device创建好的,除非是“从镜像启动”才会在

_create_image创建卷。

下面看下nova中“从镜像启动(创建一个新卷)”创建虚拟机的流程:

[mw_shl_code=bash,true]nova.api.openstack.compute.servers.Controller.create  
->nova.compute.api.API.create  
->nova.compute.api.API._create_instance  
->nova.comductor.ComputeTaskAPI.build_instances  
->nova.conductor. rpcapi.ComputeTaskAPI.build_instances  
->nova.conductor. manager.ComputeTaskAPI.build_instances  
->nova.compute.rpcapi.ComputeManager.build_and_run_instance  
->nova.compute.manager.ComputeManager._do_build_and_run_instance  
->nova.compute.manager.ComputeManager._build_and_run_instance //直到这里,流程仍与前两种创建虚拟机的情况相同,看看最终是哪里有差别  
->nova.compute.manager.ComputeManager._build_resources  
  ->nova.compute.manager.ComputeManager._build_networks_for_instance //准备网络资源  
  ->nova.compute.manager.ComputeManager._prep_block_device //准备块设备  
   ->nova.compute.manager.ComputeManager.attach_block_devices   
    ->nova.virt.block_device.attach_block_devices   
      ->nova.virt.block_device.DriverImageBlockDevice.attach  
        ->nova.virt.driver.create  
         ->nova.virt.libvrit.driver.create 创建一个新卷  
      ->nova.virt.block_device.DriverVolumeBlockDevice.attach (DriverVolumeBlockDevice是DriverImageBlockDevice的父类)  
        ->nova.virt.driver.attach  
          ->nova.virt.libvirt.driver.attach 挂载这个卷   
->nova.virt.libvirt.driver.LibvirtDriver.spawn   
//再回过头来梳理下spawn,是不是这时候块设备都有了呢?之前的流程是不是有点问题?  
//是不是块设备在_prep_block_device都建好了,后面只是向里面填不同的内容?  
//那么spawn中的_create_image 又做了什么呢?上面的_prep_block_device和spawn中的_create_image分别做什么?看起来都像建卷?  
//事实上,是一个建卷(_prep_block_device只是块设备,里面没有东西)而一个是在卷上组织镜像(_create_image之前卷已经存在,这里只是组织镜像)  
//另外考虑分支情况:(1)如果卷存在_prep_block_device流程怎么走?(2)如果从云硬盘启动_create_image流程怎么走  
//也就是说把分支情况也要考虑好  
接上面:  
->nova.virt.libvirt.driver.LibvirtDriver.spawn   
->Nova.virt.libvirt.driver._create_image //组织镜像  
这里会检查是从云硬盘启动,还是不从云硬盘启动:  
booted_from_volume = self._is_booted_from_volume(  
            instance, disk_mapping)  
...  
if not booted_from_volume:  
->Nova.virt.libvirt.driver._try_fetch_image_cache  
->Nova.virt.libvirt.imagebackend.Image.cache  
->Nova.virt.libvirt.imagebackend.Qcow2.create_image  
   ->Nova.virt.libvirt.imagebackend.Qcow2.create_image:prepare_template即fetch_image  
     ->Nova.virt.libvirt.utils.fetch_image  
       ->Nova.virt.images.fetch_to_raw  
         ->Nova.virt.images.fetch            #下载系统镜像到/var/lib/nova/instances/_base/目录  
           ->nova.image.glance.GlanceImageService.download  
             ->nova.image.glance.GlanceClientWrapper.call  
  ->Nova.virt.images.convert_image    #若镜像不是raw格式,且nova.conf中force_raw_images=True,则将backing file强制转换为raw格式  
  ->Nova.virt.libvirt.imagebackend.Qcow2.create_image:copy_qcow2_image  
  ->nova.virt.libvirt.utils.create_cow_image  [/mw_shl_code]

差异

在前面跟踪分析了:“从镜像启动”、“从镜像启动(创建一个新卷)”过程中,卷的创建是在_build_resources->_prep_block_device中创建的,而不是在

spawm->_create_image中创建的,而_create_image只是来在卷上创建镜像,或者说来组织镜像。

在这里看一下“从云硬盘启动”和“从镜像启动”、“从镜像启动(创建一个卷)”,在_prep_block_device中的差异.

(1)“从云硬盘启动”可以先不关注,肯定是在cinder这边创建的卷,不过是怎么_prep_block_device的?

(2)“从镜像启动(创建一个新卷)”确定是在cinder中创建的,nova调用了cinderclient的创建卷的命令,但其_prep_block_device做了什么?

(3)“从镜像启动”这里的卷(_base + disk)是在nova中创建的还是调cinder创建的?在_prep_block_device中做了什么?

(4)除了创建卷,还做了什么?不管在哪里建卷,都要_prep_block_device,是准备哪些东西?

再深入看一下_prep_block_device的流程:

[mw_shl_code=bash,true]->nova.compute.manager.ComputeManager._prep_block_device //准备块设备  
  ->nova.compute.manager.ComputeManager.attach_block_devices   
      ->nova.virt.block_device.attach_block_devices   
->nova.virt.block_device.DriverImageBlockDevice.attach:volume_api.create //怀疑这里在不同的创建方式下走不同的流程  
      ->Nova.volume.API.create  
        ->nova.volume.cinder.API.create  
          -> client = cinderclient(context) ; client.volumes.create(size, **kwargs)//调用cinder创建卷  [/mw_shl_code]

确认:

nova.virt.block_device.DriverImageBlockDevice.attach:volume_api.create //怀疑这里在不同的创建方式下走不同的流程

确认这里在“从云硬盘启动”、“从镜像启动”、“从镜像启动(创建一个新卷)”三种方式下会走不同的流程

(1)“从镜像启动”:

从镜像启动时,那几个类:DriverImageBlockDevice、DriverSnapshotBlockDevice、DriverBlankBlockDevice中的attach一个也没有调用

看下nova.compute.manager.ComputeManager.attach_block_devices 有没有被调用

1.png


接下来调到哪儿去了?“从镜像启动”创建云硬盘,应该是在_create_image中做的,这里attach什么呢?是不是也要准备块设备?_create_image中只是下载?

看这时候有没有卷信息呢?

��看下nova.compute.manager.ComputeManager.attach_block_devices 有没有被调用?那几个类:DriverImageBlockDevice、DriverSnapshotBlockDevice、DriverBlankBlockDevice中的attach一个也没有调用


“从镜像启动”的_prep_block_device:在attach_block_devices这里就直接返回了,如下是“从镜像启动”的打印:

2.png


看这时候有没有卷信息呢?从镜像启动时,那几个类:DriverImageBlockDevice、DriverSnapshotBlockDevice、DriverBlankBlockDevice中的attach一个也没有调用
看下nova.compute.manager.ComputeManager.attach_block_devices 有没有被调用

“从镜像启动(创建一个新卷)”的_prep_block_device:这里会去调用cinder的create建卷,和调用cinder的attach去挂载云硬盘。如下是“从镜像启动(创建一个新卷)”的log:

3.png


这里的调用流程:
[mw_shl_code=bash,true]->nova.compute.manager.ComputeManager._prep_block_device //准备块设备  
  ->nova.compute.manager.ComputeManager.attach_block_devices   
    ->nova.virt.block_device.attach_block_devices   
      ->nova.virt.block_device.DriverImageBlockDevice.attach  
        ->nova.virt.driver.create  
         ->nova.virt.libvrit.driver.create 创建一个新卷  
      ->nova.virt.block_device.DriverVolumeBlockDevice.attach (DriverVolumeBlockDevice是DriverImageBlockDevice的父类)  
        ->nova.virt.driver.attach  
          ->nova.virt.libvirt.driver.attach 挂载这个卷    [/mw_shl_code]

“从云硬盘启动”的_prep_block_device:这里不会去创建卷,因为卷已经存在了,所以这里会直接调用attach去挂载云硬盘。如下是“从云硬盘启动”的Log:

4.png

流程如下:
[mw_shl_code=bash,true]->nova.compute.manager.ComputeManager._prep_block_device //准备块设备  
  ->nova.compute.manager.ComputeManager.attach_block_devices   
    ->nova.virt.block_device.attach_block_devices   
      ->nova.virt.block_device.DriverVolumeBlockDevice.attach (DriverVolumeBlockDevice是DriverImageBlockDevice的父类)  
        ->nova.virt.driver.attach  
          ->nova.virt.libvirt.driver.attach 挂载这个卷    [/mw_shl_code]


也就是说,在_prep_block_device中的差别:

“从镜像启动”,这里不会去创建卷,也不会去调用任何attach,因为这时候不存在一个卷,这个卷要在后面_create_image中创建

“从云硬盘启动”这里卷早已经建好了,但是会去调用cinder的attach,去挂载这个卷,或者说挂载这个块设备,就是做block_device_mapping

“从镜像启动(创建一个新卷)”:这里会调用调用cinder的create去创建一个卷,并且会去调用cinder的attach去挂载这个卷


这里的_prep_block_device方法的更深入的原理是什么?


5.png


【需要搞清楚】对这里的流程清楚了,就是调到了哪里。但原理还不够清楚,怎么调过去的,为什么就调到后面的nova.compute.manager.ComputeManager.attach_block_devices了,后面的nova.compute.manager.ComputeManager.attach_block_devices为什么有的会调create和attach,有的会调用attach而不调用create,有的create和attach都不会调用?【需要搞清楚】

详细解析:

这里主要是以attach_block_devices的返回值作为block_device_mapping的成员,所以attach_block_devices返回什么很重要。

attach_block_devices:


6.png

要明白map(func, {{},{}})是什么意思,map是将第二个参数中的所有成员,在第一个参数的函数中执行一遍!

“从镜像启动(创建一个新卷)”打印:


7.png

8.png
这里可以看到,会去调用_log_and_attach,去调用实际的create和attach去创建卷和挂载卷。


“从镜像启动”打印:


这里可以看到,不会去调用_log_and_attach,这里不会去建卷,更不会去挂载卷,都在后面的_create_image中来处理。这里经过再次确认,确实不会调用。


“从云硬盘启动”,打印:


9.png

10.png


这里也会去执行map中的回调函数:_log_and_attach函数,去挂载已经创建好的云硬盘。


这里最根本的是要搞清楚:

block_device_mapping是什么?

map(_log_and_attach, block_device_mapping)做了什么?

这里还没搞明白,后面再研究。


来自:csdn
作者:lilicheung



没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条