分享

OpenStack之虚机热迁移代码解析

xioaxu790 发表于 2014-10-30 21:25:03 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 13772
本帖最后由 pig2 于 2014-10-31 00:18 编辑
问题导读

1、使用热迁移,有什么好处?
2、如果实例处于错误的状态,迁移怎么实现?
3、VM迁移,是根据什么来检索libvirt域对象的?






话说虚机迁移分为冷迁移以及热迁移,所谓热迁移用度娘的话说即是:热迁移(Live Migration,又叫动态迁移、实时迁移),即虚机保存/恢复(Save/Restore):将整个虚拟机的运行状态完整保存下来,同时可以快速的恢复到原有硬件平台甚至是不同硬件平台上。恢复以后,虚机仍旧平滑运行,用户不会察觉到任何差异。OpenStack的虚机迁移是基于Libvirt实现的,下面来看看Openstack虚机热迁移的具体代码实现。

  首先,由API入口进入到nova/api/openstack/compute/contrib/admin_actions.py
  1. @wsgi.action('os-migrateLive')
  2.    def _migrate_live(self, req, id, body):
  3.        """Permit admins to (live) migrate a server to a new host."""
  4.          context = req.environ["nova.context"]
  5.         authorize(context, 'migrateLive')
  6.          try:
  7.            block_migration = body["os-migrateLive"]["block_migration"]
  8.             disk_over_commit = body["os-migrateLive"]["disk_over_commit"]
  9.             host = body["os-migrateLive"]["host"]
  10.         except (TypeError, KeyError):
  11.             msg = _("host, block_migration and disk_over_commit must "
  12.                    "be specified for live migration.")
  13.             raise exc.HTTPBadRequest(explanation=msg)
  14.         try:
  15.             block_migration = strutils.bool_from_string(block_migration,
  16.                                                          strict=True)
  17.             disk_over_commit = strutils.bool_from_string(disk_over_commit,
  18.                                                           strict=True)
  19.         except ValueError as err:
  20.              raise exc.HTTPBadRequest(explanation=str(err))
  21.         try:
  22.             instance = self.compute_api.get(context, id, want_objects=True)
  23.             self.compute_api.live_migrate(context, instance, block_migration,
  24.                                            disk_over_commit, host)
  25.         except (exception.ComputeServiceUnavailable,
  26.                 exception.InvalidHypervisorType,
  27.                  exception.UnableToMigrateToSelf,
  28.                  exception.DestinationHypervisorTooOld,
  29.                 exception.NoValidHost,
  30.                  exception.InvalidLocalStorage,
  31.                  exception.InvalidSharedStorage,
  32.                exception.MigrationPreCheckError) as ex:
  33.             raise exc.HTTPBadRequest(explanation=ex.format_message())
  34.          except exception.InstanceNotFound as e:
  35.              raise exc.HTTPNotFound(explanation=e.format_message())
  36.          except exception.InstanceInvalidState as state_error:
  37.              common.raise_http_conflict_for_instance_invalid_state(state_error,
  38.                     'os-migrateLive')
  39.          except Exception:
  40.             if host is None:
  41.                 msg = _("Live migration of instance %s to another host "
  42.                          "failed") % id
  43.             else:
  44.                  msg = _("Live migration of instance %(id)s to host %(host)s "
  45.                          "failed") % {'id': id, 'host': host}
  46.              LOG.exception(msg)
  47.              # Return messages from scheduler
  48.              raise exc.HTTPBadRequest(explanation=msg)
  49.          return webob.Response(status_int=202)
复制代码



    这里第一行可以看到是与API文档的第二行照应的:
  1. {
  2.      "os-migrateLive": {
  3.          "host": "0443e9a1254044d8b99f35eace132080",
  4.          "block_migration": false,
  5.          "disk_over_commit": false
  6.      }
  7. }
复制代码



  好了,源码中其实执行迁移工作的就是第26、27行的一条语句:
  1. self.compute_api.live_migrate(context, instance, block_migration,
  2.                                           disk_over_commit, host)
复制代码



  由这句进入到nova/compute/api.py中,源码如下:
  1.     @check_instance_cell
  2.     @check_instance_state(vm_state=[vm_states.ACTIVE])
  3.     def live_migrate(self, context, instance, block_migration,
  4.                       disk_over_commit, host_name):
  5.         """Migrate a server lively to a new host."""
  6.          LOG.debug(_("Going to try to live migrate instance to %s"),
  7.                   host_name or "another host", instance=instance)
  8.         instance.task_state = task_states.MIGRATING
  9.          instance.save(expected_task_state=[None])
  10.          self.compute_task_api.live_migrate_instance(context, instance,
  11.                  host_name, block_migration=block_migration,
  12.                  disk_over_commit=disk_over_commit)
复制代码




  第2行是一个装饰器,用于在进入API方法之前,检测虚拟机和/或任务的状态, 如果实例处于错误的状态,将会引发异常;接下来实时迁移虚机到新的主机,并将虚机状态置于“migrating”,然后由12行进入nova/conductor/api.py
  1. def live_migrate_instance(self, context, instance, host_name,
  2.          scheduler_hint = {'host': host_name}
  3.         self._manager.migrate_server(
  4.              context, instance, scheduler_hint, True, False, None,
  5.             block_migration, disk_over_commit, None)
复制代码


  将主机名存入字典scheduler_hint中,然后调用nova/conductor/manager.py方法migrate_server,
  1. def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
  2.             flavor, block_migration, disk_over_commit, reservations=None):
  3.          if instance and not isinstance(instance, instance_obj.Instance):
  4.             # NOTE(danms): Until v2 of the RPC API, we need to tolerate
  5.              # old-world instance objects here
  6.             attrs = ['metadata', 'system_metadata', 'info_cache',
  7.                       'security_groups']
  8.             instance = instance_obj.Instance._from_db_object(
  9.                context, instance_obj.Instance(), instance,
  10.                 expected_attrs=attrs)
  11.         if live and not rebuild and not flavor:
  12.              self._live_migrate(context, instance, scheduler_hint,
  13.                                 block_migration, disk_over_commit)
  14.         elif not live and not rebuild and flavor:
  15.             instance_uuid = instance['uuid']
  16.              with compute_utils.EventReporter(context, self.db,
  17.                                           'cold_migrate', instance_uuid):
  18.                  self._cold_migrate(context, instance, flavor,
  19.                                     scheduler_hint['filter_properties'],
  20.                                     reservations)
  21.          else:
  22.             raise NotImplementedError()
复制代码


  由于在nova/conductor/api.py中传过来的参数是
  1. self._manager.migrate_server(
  2.             context, instance, scheduler_hint, True, False, None,
  3.              block_migration, disk_over_commit, None)
复制代码


  因此live是True,rebuild是Flase,flavor是None,执行第12、13行代码:
  1. if live and not rebuild and not flavor:
  2.             self._live_migrate(context, instance, scheduler_hint,
  3.                                block_migration, disk_over_commit)
复制代码



  _live_migrate代码如下:
  1. def _live_migrate(self, context, instance, scheduler_hint,
  2.                      block_migration, disk_over_commit):
  3.          destination = scheduler_hint.get("host")
  4.          try:
  5.             live_migrate.execute(context, instance, destination,
  6.                              block_migration, disk_over_commit)
  7.        except (exception.NoValidHost,
  8.                 exception.ComputeServiceUnavailable,
  9.                 exception.InvalidHypervisorType,
  10.                 exception.InvalidCPUInfo,
  11.                 exception.UnableToMigrateToSelf,
  12.                 exception.DestinationHypervisorTooOld,
  13.                 exception.InvalidLocalStorage,
  14.                 exception.InvalidSharedStorage,
  15.                 exception.HypervisorUnavailable,
  16.                 exception.MigrationPreCheckError) as ex:
  17.              with excutils.save_and_reraise_exception():
  18.                  #TODO(johngarbutt) - eventually need instance actions here
  19.                 request_spec = {'instance_properties': {
  20.                     'uuid': instance['uuid'], },
  21.                 }
  22.                 scheduler_utils.set_vm_state_and_notify(context,
  23.                          'compute_task', 'migrate_server',
  24.                         dict(vm_state=instance['vm_state'],
  25.                             task_state=None,
  26.                               expected_task_state=task_states.MIGRATING,),
  27.                         ex, request_spec, self.db)
  28.          except Exception as ex:
  29.              LOG.error(_('Migration of instance %(instance_id)s to host'
  30.                         ' %(dest)s unexpectedly failed.'),
  31.                         {'instance_id': instance['uuid'], 'dest': destination},
  32.                         exc_info=True)
  33.              raise exception.MigrationError(reason=ex)
复制代码



  首先,第三行中将主机名赋给destination,然后执行迁移,后面的都是异常的捕捉,执行迁移的代码分为两部分,先看第一部分,在nova/conductor/tasks/live_migrate.py的184行左右:
  1. def execute(context, instance, destination,
  2.              block_migration, disk_over_commit):
  3.     task = LiveMigrationTask(context, instance,
  4.                               destination,
  5.                              block_migration,
  6.                              disk_over_commit)
  7.     #TODO(johngarbutt) create a superclass that contains a safe_execute call
  8.     return task.execute()
复制代码



  先创建包含安全执行回调的超类,然后返回如下函数也即执行迁移的第二部分代码,在54行左右:
  1. def execute(self):
  2.         self._check_instance_is_running()
  3.         self._check_host_is_up(self.source)
  4.          if not self.destination:
  5.             self.destination = self._find_destination()
  6.          else:
  7.             self._check_requested_destination()
  8.          #TODO(johngarbutt) need to move complexity out of compute manager
  9.         return self.compute_rpcapi.live_migration(self.context,
  10.                  host=self.source,
  11.                  instance=self.instance,
  12.                 dest=self.destination,
  13.                  block_migration=self.block_migration,
  14.                  migrate_data=self.migrate_data)
  15.                  #TODO(johngarbutt) disk_over_commit?
复制代码



  这里有三部分内容:

如果目前主机不存在,则由调度算法选取一个目标主机,并且进行相关的检测,确保能够进行实时迁移操作;
如果目标主机存在,则直接进行相关的检测操作,确保能够进行实时迁移操作;
执行迁移操作。
  前两部分不再赘述,直接看第三部分代码,在nova/compute/rpcapi.py中:
  1. def live_migration(self, ctxt, instance, dest, block_migration, host,
  2.                       migrate_data=None):
  3.         # NOTE(russellb) Havana compat
  4.         version = self._get_compat_version('3.0', '2.0')
  5.         instance_p = jsonutils.to_primitive(instance)
  6.         cctxt = self.client.prepare(server=host, version=version)
  7.          cctxt.cast(ctxt, 'live_migration', instance=instance_p,
  8.                     dest=dest, block_migration=block_migration,
  9.                     migrate_data=migrate_data)
复制代码



  热迁移开始执行:
  1. def live_migration(self, context, instance, dest,
  2.                        post_method, recover_method, block_migration=False,
  3.                         migrate_data=None):
  4.          """Spawning live_migration operation for distributing high-load.
  5.         :param context: security context
  6.         :param instance:
  7.              nova.db.sqlalchemy.models.Instance object
  8.             instance object that is migrated.
  9.          :param dest: destination host
  10.         :param post_method:
  11.             post operation method.
  12.              expected nova.compute.manager.post_live_migration.
  13.         :param recover_method:
  14.              recovery method when any exception occurs.
  15.              expected nova.compute.manager.recover_live_migration.
  16.          :param block_migration: if true, do block migration.
  17.          :param migrate_data: implementation specific params
  18.          """
  19.          greenthread.spawn(self._live_migration, context, instance, dest,
  20.                           post_method, recover_method, block_migration,
  21.                           migrate_data)
复制代码



  这个方法中建立一个绿色线程来运行方法_live_migration,来执行实时迁移; 主要是调用libvirt python接口方法virDomainMigrateToURI,来实现从当前主机迁移domain对象到给定的目标主机;

  spawn:建立一个绿色线程来运行方法“func(*args, **kwargs)”,这里就是来运行方法_live_migration;

   _live_migration:执行实时迁移; 主要是调用libvirt python接口方法virDomainMigrateToURI,来实现从当前主机迁移domain对象到给定的目标主机;

   接着在绿色线程中调用_live_migration方法:
  1. def _live_migration(self, context, instance, dest, post_method,
  2.                         recover_method, block_migration=False,
  3.                        migrate_data=None):
  4.         """Do live migration.
  5.         :param context: security context
  6.          :param instance:
  7.             nova.db.sqlalchemy.models.Instance object
  8.              instance object that is migrated.
  9.          :param dest: destination host
  10.          :param post_method:
  11.              post operation method.
  12.              expected nova.compute.manager.post_live_migration.
  13.        :param recover_method:
  14.              recovery method when any exception occurs.
  15.              expected nova.compute.manager.recover_live_migration.
  16.          :param block_migration: if true, do block migration.
  17.          :param migrate_data: implementation specific params
  18.          """
  19.          # Do live migration.
  20.          try:
  21.              if block_migration:
  22.                  flaglist = CONF.libvirt.block_migration_flag.split(',')
  23.              else:
  24.                 flaglist = CONF.libvirt.live_migration_flag.split(',')
  25.              flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
  26.              logical_sum = reduce(lambda x, y: x | y, flagvals)
  27.             dom = self._lookup_by_name(instance["name"])
  28.              dom.migrateToURI(CONF.libvirt.live_migration_uri % dest,
  29.                              logical_sum,
  30.                              None,
  31.                              CONF.libvirt.live_migration_bandwidth)
  32.          except Exception as e:
  33.              with excutils.save_and_reraise_exception():
  34.                 LOG.error(_("Live Migration failure: %s"), e,
  35.                           instance=instance)
  36.                 recover_method(context, instance, dest, block_migration)
  37.          # Waiting for completion of live_migration.
  38.         timer = loopingcall.FixedIntervalLoopingCall(f=None)
  39. if block_migration:
  40.                  flaglist = CONF.libvirt.block_migration_flag.split(',')
复制代码



  这个获取块迁移标志列表,block_migration_flag:这个参数定义了为块迁移设置迁移标志。
  1.   else:
  2.                  flaglist = CONF.libvirt.live_migration_flag.split(',')
  3.             flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
  4.             logical_sum = reduce(lambda x, y: x | y, flagvals)
复制代码



  这部分获取实时迁移标志列表,live_migration_flag这个参数定义了实时迁移的迁移标志。
  1. dom = self._lookup_by_name(instance["name"])
复制代码


  根据给定的实例名称检索libvirt域对象。
  1. timer = loopingcall.FixedIntervalLoopingCall(f=None)
复制代码


  获取等待完成实时迁移的时间。

  热迁移代码部分至此结束。


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

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

本版积分规则

关闭

推荐上一条 /2 下一条