openstack ice 生成虚拟机磁盘文件详解
2016-02-29 00:00
941 查看
感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如有转载,请保留源作者博客信息。
Better Me的博客:blog.csdn.net/tantexian
虚拟机生成选择类型表单:
生成成功后的虚拟机截图:
linux后台:
(1)、其中_base目录保存着虚拟机镜像模板文件(即cirros-0.3.2-x86_64镜像文件):
格式为raw,即不可变文件,相对于qcow2性能更高,更多详细请自行查找相关资料
该文件也即前端页面的镜像:
比较纳闷的是为啥两个数据不一致(disk size: 17M != 13167616K),猜测是获取OS-EXT-IMG-SIZE:size时,做了一些处理。具体没仔细研究,有兴趣请自行研究。
(2)、其中一连串数字的目录(4184b91d-ef74-4c74-b88f-2f28048ec89b)下面保存了特定虚拟机自己的文件信息:
disk详情:
其中格式为qcow2,virtual size为前台flavor分配的root disk大小。disk size为目前实际占用的磁盘空间大小(因为qcow2是实际使用多少分配多少)
backing file:指明此虚拟机的后端文件,因此实例变化时候,只会改变差异信息到disk文件,_base下面的backing file镜像文件不会改变。
disk.info保存该实例的基本信息:
libvirt.xml启动虚拟机的整个配置文件:
以上讲解了整个实例创建完毕后,虚拟机实例文件是如何保存的。(注,本例中用的本地存储,假若用ceph作为后端,操作会有些许不同,请自行实验)
接下来对上述虚拟机文件的生成源码进行解析:
1、创建虚拟机前序流程不再重复,具体自行参考博文:
create instance 生成虚拟机(pycharm debug)流程:
直接从virt/libvird/driver.py开始分析:
附上源码,详解:
图1-1-2:判断是% 5��存在config_drive:
图1-1:
图1-2
图1-3:
图1-4:
1.1:image('disk').cache(fetch_func=libvirt_utils.fetch_image,
注:上述的
create_image函数拼装执行一个qemu-img create命令,最后一行代码则拼装执行一个
fallocate命令。
图1.1-1
图1.1-2
1.1.1:def create_image(self, prepare_template, base, size, *args, **kwargs):
在imagebackend.py文件中有四个create_image函数存在:
虚拟机镜像后端配置backend:
通过类和名字可以知道,不同的配置调用不同的create_image函数。Lvm、Qcow2、Raw、Rbd都继承了Image类,实现了对应的create_image函数。本示例中执行的为Qcow2类中create_image函数。(注:ceph对应为Rbd)
图1.1.1-1:
1.1.2:copy_image(此函数作用为使用cp或者scp或者rsync命令复制disk image文件到指定目录)
1.1.4:def copy_qcow2_image(base, target, size):
1.1.5:libvirt_utils.create_cow_image(base, target)
图1.1.5-1:
注:
更多请参考博文:
qemu-img命令详解
上述命令执行完在生成disk文件:
跟进讲解到此结束。后续生成xml文件,请自行参考其他博文,此处不再重复讲解。
如有转载,请保留源作者博客信息。
Better Me的博客:blog.csdn.net/tantexian
如需交流,欢迎大家博客留言。
首先上图生成一个虚拟机在后台中的文件如何保存:虚拟机生成选择类型表单:
生成成功后的虚拟机截图:
linux后台:
(1)、其中_base目录保存着虚拟机镜像模板文件(即cirros-0.3.2-x86_64镜像文件):
格式为raw,即不可变文件,相对于qcow2性能更高,更多详细请自行查找相关资料
该文件也即前端页面的镜像:
比较纳闷的是为啥两个数据不一致(disk size: 17M != 13167616K),猜测是获取OS-EXT-IMG-SIZE:size时,做了一些处理。具体没仔细研究,有兴趣请自行研究。
(2)、其中一连串数字的目录(4184b91d-ef74-4c74-b88f-2f28048ec89b)下面保存了特定虚拟机自己的文件信息:
disk详情:
其中格式为qcow2,virtual size为前台flavor分配的root disk大小。disk size为目前实际占用的磁盘空间大小(因为qcow2是实际使用多少分配多少)
backing file:指明此虚拟机的后端文件,因此实例变化时候,只会改变差异信息到disk文件,_base下面的backing file镜像文件不会改变。
disk.info保存该实例的基本信息:
libvirt.xml启动虚拟机的整个配置文件:
以上讲解了整个实例创建完毕后,虚拟机实例文件是如何保存的。(注,本例中用的本地存储,假若用ceph作为后端,操作会有些许不同,请自行实验)
接下来对上述虚拟机文件的生成源码进行解析:
1、创建虚拟机前序流程不再重复,具体自行参考博文:
create instance 生成虚拟机(pycharm debug)流程:
直接从virt/libvird/driver.py开始分析:
附上源码,详解:
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): import pydevd #远程debug断点 pydevd.settrace('192.168.10.10', port=51234, stdoutToServer=True, stderrToServer=True) if not suffix: suffix = '' booted_from_volume = self._is_booted_from_volume( #判断是否为卷启动,本示例此处值为false,见图1-1 instance, disk_mapping) def image(fname, image_type=CONF.libvirt.images_type): return self.image_backend.image(instance, fname + suffix, image_type) def raw(fname): return image(fname, image_type='raw') # ensure directories exist and are writable fileutils.ensure_tree(libvirt_utils.get_instance_path(instance)) #确认目录存在,且具有可写权限,变量值看图1-2 LOG.info(_('Creating image'), instance=instance) # NOTE(dprince): for rescue console.log may already exist... chown it. self._chown_console_log_for_instance(instance) #控制台日志输出 # NOTE(yaguang): For evacuate disk.config already exist in shared # storage, chown it. self._chown_disk_config_for_instance(instance) #修改disk保存路径的权限,具体参考图1-3 # NOTE(vish): No need add the suffix to console.log libvirt_utils.write_to_file( #写空字符串''到控制台路径文件中 self._get_console_log_path(instance), '', 7) if not disk_images: #获取一个disk%Ew��信息字典,本示例此处kernel_id,ramdisk_id都为空‘’ disk_images = {'image_id': instance['image_ref'], 'kernel_id': instance['kernel_id'], 'ramdisk_id': instance['ramdisk_id']} if disk_images['kernel_id']: fname = imagecache.get_cache_fname(disk_images, 'kernel_id') raw('kernel').cache(fetch_func=libvirt_utils.fetch_image, context=context, filename=fname, image_id=disk_images['kernel_id'], user_id=instance['user_id'], project_id=instance['project_id']) if disk_images['ramdisk_id']: fname = imagecache.get_cache_fname(disk_images, 'ramdisk_id') raw('ramdisk').cache(fetch_func=libvirt_utils.fetch_image, context=context, filename=fname, image_id=disk_images['ramdisk_id'], user_id=instance['user_id'], project_id=instance['project_id']) inst_type = flavors.extract_flavor(instance) #获取本次生成实例的类型即规格,见图1-4 # NOTE(ndipanov): Even if disk_mapping was passed in, which # currently happens only on rescue - we still don't want to # create a base image. #是否从卷启动,不是从卷启动,则不需要创建base image,即共用_base文件夹下面镜像,作为后端文件backfile if not booted_from_volume: root_fname = imagecache.get_cache_fname(disk_images, 'image_id')#获取image_id的一个hash值 size = instance['root_gb'] * units.Gi #获取页面选择的Root disk值 if size == 0 or suffix == '.rescue': size = None image('disk').cache(fetch_func=libvirt_utils.fetch_image, #跟进:1.1 context=context, filename=root_fname, size=size, image_id=disk_images['image_id'], user_id=instance['user_id'], project_id=instance['project_id']) # Lookup the filesystem type if required os_type_with_default = disk.get_fs_type_for_os_type( #从instance参数中获取os_type,没有的话,则为default instance['os_type']) ephemeral_gb = instance['ephemeral_gb'] #获取页面的flavor中的ephemeral_gb 值 if 'disk.local' in disk_mapping: #判断是否存在local分区,本示例不存在 disk_image = image('disk.local') fn = functools.partial(self._create_ephemeral, fs_label='ephemeral0', os_type=instance["os_type"], is_block_dev=disk_image.is_block_dev) fname = "ephemeral_%s_%s" % (ephemeral_gb, os_type_with_default) size = ephemeral_gb * units.Gi disk_image.cache(fetch_func=fn, filename=fname, size=size, ephemeral_size=ephemeral_gb) for idx, eph in enumerate(driver.block_device_info_get_ephemerals( block_device_info)): disk_image = image(blockinfo.get_eph_disk(idx)) fn = functools.partial(self._create_ephemeral, fs_label='ephemeral%d' % idx, os_type=instance["os_type"], is_block_dev=disk_image.is_block_dev) size = eph['size'] * units.Gi fname = "ephemeral_%s_%s" % (eph['size'], os_type_with_default) disk_image.cache( fetch_func=fn, filename=fname, size=size, ephemeral_size=eph['size']) if 'disk.swap' in disk_mapping: #判断是否存在swap分区,本示例不存在 mapping = disk_mapping['disk.swap'] swap_mb = 0 swap = driver.block_device_info_get_swap(block_device_info) if driver.swap_is_usable(swap): swap_mb = swap['swap_size'] elif (inst_type['swap'] > 0 and not block_device.volume_in_mapping( mapping['dev'], block_device_info)): swap_mb = inst_type['swap'] if swap_mb > 0: size = swap_mb * units.Mi image('disk.swap').cache(fetch_func=self._create_swap, filename="swap_%s" % swap_mb, size=size, swap_mb=swap_mb) # Config drive if configdrive.required_by(instance): #判断是否存在configdrive,本示例不存在。具体见图1-1-2: LOG.info(_('Using config drive'), instance=instance) extra_md = {} if admin_pass: extra_md['admin_pass'] = admin_pass inst_md = instance_metadata.InstanceMetadata(instance, content=files, extra_md=extra_md, network_info=network_info) with configdrive.ConfigDriveBuilder(instance_md=inst_md) as cdb: configdrive_path = self._get_disk_config_path(instance) LOG.info(_('Creating config drive at %(path)s'), {'path': configdrive_path}, instance=instance) try: cdb.make_drive(configdrive_path) except processutils.ProcessExecutionError as e: with excutils.save_and_reraise_exception(): LOG.error(_('Creating config drive failed ' 'with error: %s'), e, instance=instance) # File injection only if needed #如果inject_files ==True且inject_partition != -2则执行文件注入 elif inject_files and CONF.libvirt.inject_partition != -2: if booted_from_volume: LOG.warn(_('File injection into a boot from volume ' 'instance is not supported'), instance=instance) self._inject_data( instance, network_info, admin_pass, files, suffix) if%r0CONF.libvirt.virt_type == 'uml': #如果配置文件配置为uml,则将disk的权限修改为root libvirt_utils.chown(i-age('disk').path, 'root') |
def required_by(instance): return instance.get('config_drive') or CONF.force_config_drive |
图1-2
图1-3:
图1-4:
1.1:image('disk').cache(fetch_func=libvirt_utils.fetch_image,
def cache(self, fetch_func, filename, size=None, *args, **kwargs): """Creates image from template. #根据template创建image Ensures that template and image not already exists. Ensures that base directory exists. Synchronizes on template fetching. :fetch_func: Function that creates the base image Should accept `target` argument. :filename: Name of the file in the image directory :size: Size of created image in bytes (optional) """ @utils.synchronized(filename, external=True, lock_path=self.lock_path) def fetch_func_sync(target, *args, **kwargs): fetch_func(target=target, *args, **kwargs) base_dir = os.path.join(CONF.instances_path, #获取base镜像的目录路径,见图1.1-1 CONF.image_cache_subdirectory_name) if not os.path.exists(base_dir): fileutils.ensure_tree(base_dir) base = os.path.join(base_dir, filename) #获取镜像文件路径,见图1.1-2 if not self.check_image_exists() or not os.path.exists(base): self.create_image(fetch_func_sync, base, size, #镜像和路径存在,创建镜像,跟进到1.1.1(此处为生成虚拟机文件核心代码) *args, **kwargs) if (size and self.preallocate and self._can_fallocate() and os.access(self.path, os.W_OK)): utils.execute('fallocate', '-n', '-l', size, self.path) #linux命令:为文件预分配物理空间,本示例此处代码不执行 |
create_image函数拼装执行一个qemu-img create命令,最后一行代码则拼装执行一个
fallocate命令。
图1.1-1
图1.1-2
1.1.1:def create_image(self, prepare_template, base, size, *args, **kwargs):
在imagebackend.py文件中有四个create_image函数存在:
虚拟机镜像后端配置backend:
通过类和名字可以知道,不同的配置调用不同的create_image函数。Lvm、Qcow2、Raw、Rbd都继承了Image类,实现了对应的create_image函数。本示例中执行的为Qcow2类中create_image函数。(注:ceph对应为Rbd)
def create_image(self, prepare_template, base, size, *args, **kwargs): filename = os.path.split(base)[-1] #获取镜像文件名 @utils.synchronized(filename, external=True, lock_path=self.lock_path) def copy_qcow2_image(base, target, size): # TODO(pbrady): Consider copying the cow image here # with preallocation=metadata set for performance reasons. # This would be keyed on a 'preallocate_images' setting. libvirt_utils.create_cow_image(base, target) if size: disk.extend(target, size, use_cow=True) # Download the unmodified base image unless we already have a copy. if not os.path.exists(base): prepare_template(target=base, max_size=size, *args, **kwargs) else: #验证base的size是否满足小于size(其中base的size为'/var/lib/nova/instances/_base/70ec07731557ccdb9287d902cf15ec3d26ab9b41'文件的大小,第二个size为flovar中的root disk大小即1GB),如果不满足则说明,该镜像不能在小于自己容量的flaver中生成,报错 self.verify_base_size(base, size) legacy_backing_size = None legacy_base = base #以下针对qcow2格式disk进行逻辑处理,因为qcow2文件占用空间是按需动态增长的 # Determine whether an existing qcow2 disk uses a legacy backing by # actually looking at the image itself and parsing the output of the # backing file it expects to be using. if os.path.exists(self.path): #判断路径是否存在,见图1.1.1-1 backing_path = libvirt_utils.get_disk_backing_file(self.path) if backing_path is not None: backing_file = os.path.basename(backing_path) backing_parts = backing_file.rpartition('_') if backing_file != backing_parts[-1] and \ backing_parts[-1].isdigit(): legacy_backing_size = int(backing_parts[-1]) legacy_base += '_%d' % legacy_backing_size legacy_backing_size *= units.Gi # Create the legacy backing file if necessary. if legacy_backing_size: if not os.path.exists(legacy_base): with fileutils.remove_path_on_error(legacy_base): libvirt_utils.copy_image(base, legacy_base)#跟进代码1.1.2 disk.extend(legacy_base, legacy_backing_size, use_cow=True) if not os.path.exists(self.path): #代码同上,同样可以参考图1.1.1-1 with fileutils.remove_path_on_error(self.path): copy_qcow2_image(base, self.path, size) #此处跟进代码1.1.4 |
1.1.2:copy_image(此函数作用为使用cp或者scp或者rsync命令复制disk image文件到指定目录)
1.1.4:def copy_qcow2_image(base, target, size):
@utils.synchronized(filename, external=True, lock_path=self.lock_path) def copy_qcow2_image(base, target, size): # TODO(pbrady): Consider copying the cow image here # with preallocation=metadata set for performance reasons. # This would be keyed on a 'preallocate_images' setting. libvirt_utils.create_cow_image(base, target) #跟进到1.1.5: if size: disk.extend(target, size, use_cow=True) |
def create_cow_image(backing_file, path, size=None): #拼装qemu-img 创建命令 """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 # This doesn't seem to get inherited so force it to... # http://paste.ubuntu.com/1213295/ # TODO(harlowja) probably file a bug against qemu-img/qemu if base_details and base_details.cluster_size is not None: cow_opts += ['cluster_size=%s' % base_details.cluster_size] # For now don't inherit this due the following discussion... # See: http://www.gossamer-threads.com/lists/openstack/dev/10592 # if 'preallocation' in base_details: # cow_opts += ['preallocation=%s' % base_details['preallocation']] if base_details and base_details.encryption: cow_opts += ['encryption=%s' % base_details.encryption] 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) #直接拼装出来的命令,本示例具体值,见图1.1.5-1(此处即拼装处生成虚拟机文件的命令) |
注:
如果“-o”选项中使用了backing_file这个选项来指定其后端镜像文件,那么这个创建的镜像文件仅记录与后端镜像文件的差异部分。后端镜像文件不会被修改,除非在QEMU monitor中使用“commit”命令或者使用“qemu-img commit”命令去手动提交这些改动。这种情况下,size参数不是必须需的,其值默认为后端镜像文件的大小。另外,直接使用“-b backfile”参数也与“-o backing_file=backfile”效果相同。 size选项用于指定镜像文件的大小,其默认单位是字节(bytes),也可以支持k(或K)、M、G、T来分别表示KB、MB、GB、TB大小。另外,镜像文件的大小(size)也并非必须写在命令的最后,它也可以被写在“-o”选项中作为其中一个选项。 |
qemu-img命令详解
上述命令执行完在生成disk文件:
跟进讲解到此结束。后续生成xml文件,请自行参考其他博文,此处不再重复讲解。
相关文章推荐
- 创建虚拟机生成虚拟机全程日志打印输出流程详解(openstack开发必备)
- OpenStack基于Libvirt的虚拟化平台调度实现----Nova虚拟机动态迁移源码分析
- centos7上win2003 virtio (openstack)镜像制作成功(图文详解)
- OpenStack文件注入相关分析(转载)
- 制作OpenStack CentOS 6.5 & win7镜像
- 制作openstack Centos镜像 -- Example: CentOS image
- CREATING A WINDOWS IMAGE FOR OPENSTACK(转)
- 系统原理分析架构-五-squid(反代理即web缓存服务器)
- 系统原理分析架构-六-负载均衡(定义及介绍及LVS/Nginx/Haproxy比较)
- 系统原理分析架构-一-DNS负载均衡
- 系统原理分析架构-四-squid(简介及正向代理)
- 系统原理分析架构-开篇 (对于架构师与开发语言及被青春饭的一些想法)
- 系统原理分析架构-三--代理服务器简介及分类
- 关于scrolltop 兼容 IE6/7/8, Safari,FF的方法
- 绝对详细!Nginx基本配置、性能优化指南
- nginx 配置
- Tomcat下部署多个项目
- Linux CentOS 6.5 中安装与配置JDK-7-tomcat-nginx-rabbitmq
- linux之ssh无密码访问
- nginx 初识