openstack-cinder缓存机制
volume/flows/manager/create_volume.py:
# Try and use the image cache.
should_create_cache_entry = False
if self.image_volume_cache and not cloned:
internal_context = cinder_context.get_internal_tenant_context()
if not internal_context:
LOG.info(_LI('Unable to get Cinder internal context, will '
'not use image-volume cache.'))
else:
model_update, cloned = self._create_from_image_cache(
context,
internal_context,
volume,
image_id,
image_meta
)
# Don't cache encrypted volume.
if not cloned and not volume_is_encrypted:
should_create_cache_entry = True
下面逐行分析:
从以上代码发现,通过一些配置,image_cache(镜像缓存)是可以实现的。
if self.image_volume_cache and not cloned: internal_context = cinder_context.get_internal_tenant_context() if not internal_context: LOG.info(_LI('Unable to get Cinder internal context, will ' 'not use image-volume cache.'))
首先判断image_volume_cache参数,这个参数从上层传下来的,追溯到最后是根据配置文件中的image_volume_cache_enabled属性的True/False来决定的。
其次判断是否已经克隆cloned,如果还没有克隆:internal_context = cinder_context.get_internal_tenant_context() if not internal_context: LOG.info(_LI('Unable to get Cinder internal context, will ' 'not use image-volume cache.'))
获取“内部租户上下文”internal_tenant_context,其实就是配置文件中内部租户和其对应用户2个属性,配上就ok。这个上下文没有入库,好像通知时有用到,反正不影响核心业务。
else: model_update, cloned = self._create_from_image_cache( context, internal_context, volume, image_id, image_meta )
如果“内部租户上下文”有值,则从镜像缓存中创建_create_from_image_cache,这个函数命名很有深意,表明了其作用:如果没有缓存hit,则本次创建这个镜像的缓存;如果缓存hit,则直接拿来用:
def _create_from_image_cache(self, context, internal_context, volume, image_id, image_meta): """Attempt to create the volume using the image cache. Best case this will simply clone the existing volume in the cache. Worst case the image is out of date and will be evicted. In that case a clone will not be created and the image must be downloaded again. """ LOG.info('Attempting to retrieve cache entry for image = ' '%(image_id)s on host %(host)s.', {'image_id': image_id, 'host': volume.host}) # Currently can't create volume from source vol with different # encryptions, so just return if volume.encryption_key_id: return None, False try: cache_entry = self.image_volume_cache.get_entry(internal_context, volume, image_id, image_meta) if cache_entry: LOG.info('Creating from source image-volume %(volume_id)s', {'volume_id': cache_entry['volume_id']}) model_update = self._create_from_source_volume( context, volume, cache_entry['volume_id'] ) return model_update, True except exception.CinderException as e: LOG.warning(_LW('Failed to create volume from image-volume cache, ' 'will fall back to default behavior. Error: ' '%(exception)s'), {'exception': e}) return None, False
上面是_create_from_image_cache函数的代码,在image/cache.py中,逐行分析:
if volume.encryption_key_id:
return None, False
首先判断是否加密,如果加密则不做缓存
cache_entry = self.image_volume_cache.get_entry(internal_context, volume, image_id, image_meta)
其次通过镜像和卷的信息获取缓存入口,也就是判断是否会hit
if cache_entry: LOG.info('Creating from source image-volume %(volume_id)s', {'volume_id': cache_entry['volume_id']}) model_update = self._create_from_source_volume( context, volume, cache_entry['volume_id'] ) return model_update, True
如果hit,则更新模型(volume_id),然后返回克隆成功。
如果miss,则返回False
def get_entry(self, context, volume_ref, image_id, image_meta): cache_entry = self.db.image_volume_cache_get_and_update_last_used( context, image_id, volume_ref['host'] ) if cache_entry: LOG.info('Found image-volume cache entry: %(entry)s.', {'entry': self._entry_to_str(cache_entry)}) if self._should_update_entry(cache_entry, image_meta): LOG.info('Image-volume cache entry is out-dated, evicting: ' '%(entry)s.', {'entry': self._entry_to_str(cache_entry)}) self._delete_image_volume(context, cache_entry) cache_entry = None if cache_entry: self._notify_cache_hit(context, cache_entry['image_id'], cache_entry['host']) else: self._notify_cache_miss(context, image_id, volume_ref['host']) return cache_entry
从上面get_entry函数的代码中可以看出,缓存是存在数据库中的。
在cinder的库中搜索,可以找到:
刚刚缓存的镜像和卷的信息就可以看到了。
不仅是通过bdm以镜像为源、以卷为目的创建虚拟机,还是通过镜像创建卷,cinder都会使用缓存机制来提升性能。
使用cinder的缓存机制能有效减少镜像从glance上传到各虚拟化的时间,因此建议使用此功能。