OpenStack之虚机热迁移的代码详细解析

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

首先,由API入口进入到nova/api/openstack/compute/contrib/admin_actions.py

@wsgi.action('os-migrateLive')
  def _migrate_live(self, req, id, body):
    """Permit admins to (live) migrate a server to a new host."""
    context = req.environ["nova.context"]
    authorize(context, 'migrateLive')

    try:
      block_migration = body["os-migrateLive"]["block_migration"]
      disk_over_commit = body["os-migrateLive"]["disk_over_commit"]
      host = body["os-migrateLive"]["host"]
    except (TypeError, KeyError):
      msg = _("host, block_migration and disk_over_commit must "
          "be specified for live migration.")
      raise exc.HTTPBadRequest(explanation=msg)

    try:
      block_migration = strutils.bool_from_string(block_migration,
                            strict=True)
      disk_over_commit = strutils.bool_from_string(disk_over_commit,
                             strict=True)
    except ValueError as err:
      raise exc.HTTPBadRequest(explanation=str(err))

    try:
      instance = self.compute_api.get(context, id, want_objects=True)
      self.compute_api.live_migrate(context, instance, block_migration,
                     disk_over_commit, host)
    except (exception.ComputeServiceUnavailable,
        exception.InvalidHypervisorType,
        exception.UnableToMigrateToSelf,
        exception.DestinationHypervisorTooOld,
        exception.NoValidHost,
        exception.InvalidLocalStorage,
        exception.InvalidSharedStorage,
        exception.MigrationPreCheckError) as ex:
      raise exc.HTTPBadRequest(explanation=ex.format_message())
    except exception.InstanceNotFound as e:
      raise exc.HTTPNotFound(explanation=e.format_message())
    except exception.InstanceInvalidState as state_error:
      common.raise_http_conflict_for_instance_invalid_state(state_error,
          'os-migrateLive')
    except Exception:
      if host is None:
        msg = _("Live migration of instance %s to another host "
            "failed") % id
      else:
        msg = _("Live migration of instance %(id)s to host %(host)s "
            "failed") % {'id': id, 'host': host}
      LOG.exception(msg)
      # Return messages from scheduler
      raise exc.HTTPBadRequest(explanation=msg)

    return webob.Response(status_int=202)

这里第一行可以看到是与API文档的第二行照应的:

 {
  "os-migrateLive": {
    "host": "0443e9a1254044d8b99f35eace132080",
    "block_migration": false,
    "disk_over_commit": false
  }
}

好了,源码中其实执行迁移工作的就是第26、27行的一条语句:

self.compute_api.live_migrate(context, instance, block_migration,
                 disk_over_commit, host)

由这句进入到nova/compute/api.py中,源码如下:

@check_instance_cell
  @check_instance_state(vm_state=[vm_states.ACTIVE])
  def live_migrate(self, context, instance, block_migration,
           disk_over_commit, host_name):
    """Migrate a server lively to a new host."""
    LOG.debug(_("Going to try to live migrate instance to %s"),
         host_name or "another host", instance=instance)

    instance.task_state = task_states.MIGRATING
    instance.save(expected_task_state=[None])

    self.compute_task_api.live_migrate_instance(context, instance,
        host_name, block_migration=block_migration,
        disk_over_commit=disk_over_commit)

第2行是一个装饰器,用于在进入API方法之前,检测虚拟机和/或任务的状态, 如果实例处于错误的状态,将会引发异常;接下来实时迁移虚机到新的主机,并将虚机状态置于“migrating”,然后由12行进入nova/conductor/api.py

def live_migrate_instance(self, context, instance, host_name,
                block_migration, disk_over_commit):
     scheduler_hint = {'host': host_name}
     self._manager.migrate_server(
       context, instance, scheduler_hint, True, False, None,
       block_migration, disk_over_commit, None)

将主机名存入字典scheduler_hint中,然后调用nova/conductor/manager.py方法migrate_server,

def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
      flavor, block_migration, disk_over_commit, reservations=None):
    if instance and not isinstance(instance, instance_obj.Instance):
      # NOTE(danms): Until v2 of the RPC API, we need to tolerate
      # old-world instance objects here
      attrs = ['metadata', 'system_metadata', 'info_cache',
           'security_groups']
      instance = instance_obj.Instance._from_db_object(
        context, instance_obj.Instance(), instance,
        expected_attrs=attrs)
    if live and not rebuild and not flavor:
      self._live_migrate(context, instance, scheduler_hint,
                block_migration, disk_over_commit)
    elif not live and not rebuild and flavor:
      instance_uuid = instance['uuid']
      with compute_utils.EventReporter(context, self.db,
                     'cold_migrate', instance_uuid):
        self._cold_migrate(context, instance, flavor,
                  scheduler_hint['filter_properties'],
                  reservations)
    else:
      raise NotImplementedError()

由于在nova/conductor/api.py中传过来的参数是

self._manager.migrate_server(
       context, instance, scheduler_hint, True, False, None,
       block_migration, disk_over_commit, None)

因此live是True,rebuild是Flase,flavor是None,执行第12、13行代码:

 if live and not rebuild and not flavor:
       self._live_migrate(context, instance, scheduler_hint,
                block_migration, disk_over_commit)
 _live_migrate代码如下:
def _live_migrate(self, context, instance, scheduler_hint,
           block_migration, disk_over_commit):
    destination = scheduler_hint.get("host")
    try:
      live_migrate.execute(context, instance, destination,
               block_migration, disk_over_commit)
    except (exception.NoValidHost,
        exception.ComputeServiceUnavailable,
        exception.InvalidHypervisorType,
        exception.InvalidCPUInfo,
        exception.UnableToMigrateToSelf,
        exception.DestinationHypervisorTooOld,
        exception.InvalidLocalStorage,
        exception.InvalidSharedStorage,
        exception.HypervisorUnavailable,
        exception.MigrationPreCheckError) as ex:
      with excutils.save_and_reraise_exception():
        #TODO(johngarbutt) - eventually need instance actions here
        request_spec = {'instance_properties': {
          'uuid': instance['uuid'], },
        }
        scheduler_utils.set_vm_state_and_notify(context,
            'compute_task', 'migrate_server',
            dict(vm_state=instance['vm_state'],
               task_state=None,
               expected_task_state=task_states.MIGRATING,),
            ex, request_spec, self.db)
    except Exception as ex:
      LOG.error(_('Migration of instance %(instance_id)s to host'
            ' %(dest)s unexpectedly failed.'),
            {'instance_id': instance['uuid'], 'dest': destination},
            exc_info=True)
      raise exception.MigrationError(reason=ex)

首先,第三行中将主机名赋给destination,然后执行迁移,后面的都是异常的捕捉,执行迁移的代码分为两部分,先看第一部分,在nova/conductor/tasks/live_migrate.py的184行左右:

def execute(context, instance, destination,
      block_migration, disk_over_commit):
  task = LiveMigrationTask(context, instance,
               destination,
               block_migration,
               disk_over_commit)
  #TODO(johngarbutt) create a superclass that contains a safe_execute call
  return task.execute()

先创建包含安全执行回调的超类,然后返回如下函数也即执行迁移的第二部分代码,在54行左右:

def execute(self):
    self._check_instance_is_running()
    self._check_host_is_up(self.source)

    if not self.destination:
      self.destination = self._find_destination()
    else:
      self._check_requested_destination()

    #TODO(johngarbutt) need to move complexity out of compute manager
    return self.compute_rpcapi.live_migration(self.context,
        host=self.source,
        instance=self.instance,
        dest=self.destination,
        block_migration=self.block_migration,
        migrate_data=self.migrate_data)
        #TODO(johngarbutt) disk_over_commit?

这里有三部分内容:

如果目前主机不存在,则由调度算法选取一个目标主机,并且进行相关的检测,确保能够进行实时迁移操作;

如果目标主机存在,则直接进行相关的检测操作,确保能够进行实时迁移操作;

执行迁移操作。

前两部分不再赘述,直接看第三部分代码,在nova/compute/rpcapi.py中:

def live_migration(self, ctxt, instance, dest, block_migration, host,
            migrate_data=None):
    # NOTE(russellb) Havana compat
    version = self._get_compat_version('3.0', '2.0')
    instance_p = jsonutils.to_primitive(instance)
    cctxt = self.client.prepare(server=host, version=version)
    cctxt.cast(ctxt, 'live_migration', instance=instance_p,
          dest=dest, block_migration=block_migration,
          migrate_data=migrate_data)

热迁移开始执行:

def live_migration(self, context, instance, dest,
            post_method, recover_method, block_migration=False,
            migrate_data=None):
    """Spawning live_migration operation for distributing high-load.

    :param context: security context
    :param instance:
      nova.db.sqlalchemy.models.Instance object
      instance object that is migrated.
    :param dest: destination host
    :param post_method:
      post operation method.
      expected nova.compute.manager.post_live_migration.
    :param recover_method:
      recovery method when any exception occurs.
      expected nova.compute.manager.recover_live_migration.
    :param block_migration: if true, do block migration.
    :param migrate_data: implementation specific params

    """

    greenthread.spawn(self._live_migration, context, instance, dest,
             post_method, recover_method, block_migration,
             migrate_data)

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

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

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

接着在绿色线程中调用_live_migration方法:

def _live_migration(self, context, instance, dest, post_method,
            recover_method, block_migration=False,
            migrate_data=None):
    """Do live migration.

    :param context: security context
    :param instance:
      nova.db.sqlalchemy.models.Instance object
      instance object that is migrated.
    :param dest: destination host
    :param post_method:
      post operation method.
      expected nova.compute.manager.post_live_migration.
    :param recover_method:
      recovery method when any exception occurs.
      expected nova.compute.manager.recover_live_migration.
    :param block_migration: if true, do block migration.
    :param migrate_data: implementation specific params
    """

    # Do live migration.
    try:
      if block_migration:
        flaglist = CONF.libvirt.block_migration_flag.split(',')
      else:
        flaglist = CONF.libvirt.live_migration_flag.split(',')
      flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
      logical_sum = reduce(lambda x, y: x | y, flagvals)

      dom = self._lookup_by_name(instance["name"])
      dom.migrateToURI(CONF.libvirt.live_migration_uri % dest,
               logical_sum,
               None,
               CONF.libvirt.live_migration_bandwidth)

    except Exception as e:
      with excutils.save_and_reraise_exception():
        LOG.error(_("Live Migration failure: %s"), e,
             instance=instance)
        recover_method(context, instance, dest, block_migration)

    # Waiting for completion of live_migration.
    timer = loopingcall.FixedIntervalLoopingCall(f=None)
if block_migration:
         flaglist = CONF.libvirt.block_migration_flag.split(',')

这个获取块迁移标志列表,block_migration_flag:这个参数定义了为块迁移设置迁移标志。

else:
         flaglist = CONF.libvirt.live_migration_flag.split(',')
       flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
      logical_sum = reduce(lambda x, y: x | y, flagvals)

这部分获取实时迁移标志列表,live_migration_flag这个参数定义了实时迁移的迁移标志。

 dom = self._lookup_by_name(instance["name"])

根据给定的实例名称检索libvirt域对象。

 timer = loopingcall.FixedIntervalLoopingCall(f=None)

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • CentOS系统中一键安装Openstack图文教程

    前言 首先找到一个国外的网站, http://openstack.redhat.com/Main_Page,进入到quickstart页面中 简单翻译如下: 用到的工具是一个被成为RDO的东东, 能够在基于RHEL内核的linux系统,如RedHat,CentOS,Scientific Linux下,快速实现三步安装.  一:安装RDO软件 sudo yum install -y http://rdo.fedorapeople.org/rdo-release.rpm 二:安装一个叫packsta

  • CentOS 6.4下安装部署OpenStack云计算平台的方法

    前言 OpenStack 是一个开源的 IaaS(基础设施及服务)云计算平台,让任何人都可以自行建立和提供云端运算服务. OpenStack 由一系列相互关联的项目提供云基础设施解决方案的各个组件,核心项目(9 个): 1.计算 (Compute) - Nova: 2.网络和地址管理 - Neutron: 3.对象存储 (Object) - Swift: 4.块存储 (Block) - Cinder: 5.身份 (Identity) - keystone: 6.镜像 (Image) - Glan

  • 一步一步教你安装openstack(图文)

    openstack,安装的门槛比较高,而且相当麻烦,很多的安装文档作者省了不少安装步骤.这对初学的人带来了很大的麻烦,也许作者轻易节省一步,就会创成后面的安装失败.而且初学者由于对openstack不是很了解,错误无法排查,因此很多人都在中途放弃对openstack的学习.其实有一个安装工具可以快速的安装openstack,不需要了解openstack各个组件的之间的关系,只需鼠标轻轻一点,就可以完成对openstack的安装:而且还有诸多功能,如对openstack健康检查,查看节点日志等.

  • 基于openstack安装部署私有云详细图文教程

    本文主要分享的是云计算.openstack的使用.私有云平台建设.云服务器云硬盘的构建和使用.从基本概念入手到私有云建设,信息量非常大.对于openstack的安装部署都是从官方文档中一步步的介绍,内容非常详细. 一.云计算 基本概念 云计算(cloud computing)是基于互联网的相关服务的增加.使用和交付模式,通常涉及通过互联网来提供动态易扩展且经常是虚拟化的资源.云是网络.互联网的一种比喻说法.过去在图中往往用云来表示电信网,后来也用来表示互联网和底层基础设施的抽象.因此,云计算甚至

  • 什么是OpenStack 开源的云计算管理平台项目

    OpenStack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目. OpenStack是一个开源的云计算管理平台项目,由几个主要的组件组合起来完成具体工作.OpenStack支持几乎所有类型的云环境,项目目标是提供实施简单.可大规模扩展.丰富.标准统一的云计算管理平台.OpenStack通过各种互补的服务提供了基础设施即服务(IaaS)的解决方案,每个服务提供API以进行集成. OpenStack是一个旨在为公共及私

  • CentOS7.2部署OpenStack(一)—环境准备

    本篇文章介绍CentOS7.2部署OpenStack,具体如下: 1.系统环境 # uname -r 3.10.0-327.el7.x86_64 # cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 2.服务器部署 3.基础环境准备 3.1.配置hosts # cat /etc/hosts 192.168.56.108 controller 192.168.56.109 compute 3.2.配置时间同步 [root@co

  • Openstack 创建项目和虚拟机详细介绍

    Openstack 创建项目和虚拟机                    最新学习Openstack 的知识,因为在公司新项目中使用,于是在网上学习如何创建项目,这里记录下,希望也能帮助到大家. 正规教程:Running an Instance 创建项目 1. 点击左边的 Identity–>Projects,并点击面板右上方创建项目 2. 创建一个属于你的项目并且在配额中调整你所需要的资源 创建用户 1. 点击左边的 Identity–>Users,并点击面板右上方创建用户 2. 设置密码

  • openstack 重启的服务命令整理总结

    openstack重启的服务命令集合 最近公司项目需要使用Openstack   由于Openstack 重启服务命令比较多,于是就在网上找了下资料,整理下来,希望能帮助到读者! 重启openstack的整个服务 openstack-service restart 1. 重启dashboard service httpd  restart service memcached restart 2. 重启 ceilometer 2.1 cinder service mongod restart 2.

  • OpenStack之虚机热迁移的代码详细解析

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

  • KVM虚拟机实现在线热迁移的方法步骤(图文)

    一.KVM虚拟机的迁移方式及需要注意的问题 KVM虚拟机的迁移有两种方法: 1.静态迁移(冷迁移):对于冷迁移,就是在虚拟机关闭状态下,将虚拟机的磁盘文件及.xml配置文件(这两个文件组成了一个虚拟机)复制到要迁移到的目标主机上,然后在目标主机上使用"virsh define *.xml"命令重新定义虚拟机即可. 2.动态迁移(热迁移):对于热迁移,比较常用,通常是这台服务器上正在跑着一些业务,而这些业务又不允许中断,那么就需要使用热迁移了,这篇博文将详细写出热迁移的步骤. 1.冷迁移

  • 详解云与备份之VMware虚机备份和恢复

    1. 与备份有关的VMWare基础知识 1.1 VMware 虚机磁盘在 ESXi 宿主机上的文件 简单来说,虚机的每个虚拟磁盘由ESXi 宿主机上的三个文件组成(这里的虚机名字是 sammy-target-win-small,下面是其第一个磁盘对应的三个文件): sammy-target-win-small.vmdk (配置文件,大小 633 字节) sammy-target-win-small-flat.vmdk (二进制文件,大小 12884901888 字节) sammy-target-

  • 如何获取C++类成员虚函数地址的示例代码

    本文主要给大家介绍了关于如何获取C++类成员虚函数地址的相关内容,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍: 1.GCC平台 GCC平台获取C++成员虚函数地址可使用如下方法[1]: class Base{ int i; public: virtual void f1(){ cout<<"Base's f1()"<<endl; } }; Base b; void (Base::*mfp)() = &Base::f1; printf(&qu

  • php中Swoole的热更新实现代码实例

    使用swoole_http_server替代php-fpm后,由于php长驻内存,修改了代码不能实时调试,需要去手动去重启服务,很是不方便,决定使用inotify来监控文件状态的改变,来给swoole发送reload信号,来实现swoole的热更新. 如何安装inotify就不写了,安装之后可以建立一个脚本文件,如php_reload.sh: #!/bin/sh # src 需要监控的地址 src=/home/server/Project/test/app/ /usr/bin/inotifywa

  • .net 6精简版webapi教程及热重载、代码自动反编译演示

    前言: .net6LTS版本发布已经有若干天了.此处做一个关于使用.net6开发精简版webapi(minimalapi)的入门教程,以及VS2022上面的两个强大的新技能(热重载.代码自动反编译)的顺带演示. 1.新建一个项目.此处就命名为SomeExample: 2.选择.net6版本,并且此处先去掉HTTPS配置以及去掉使用控制器的选项: 3.创建完成以后,原始画面如下所示.并且,解决方案一栏只剩下program文件,其他文件都被精简掉了.并且没有Main函数入口和命名空间等. 4.接下来

  • KVM 虚机镜像扩容和压缩的操作

    目录 KVM镜像操作 创建镜像 qemu-img create 查看镜像信息 qemu-img info 转换镜像格式 qemu-img convert 缩小镜像实际占用的磁盘大小 Linux虚机端 Windows虚机端 压缩虚机镜像 virt-filesystems命令 virt-resize命令 对镜像进行扩容操作 使用RAW格式对镜像扩容 参考 KVM镜像操作 qemu-img命令 创建镜像 qemu-img create # 创建一个设备空间大小为10G的镜像 qemu-img crea

  • WPF实现调用本机摄像头的示例代码

    此项目使用了OpenCVSharp加载本地摄像头,多个摄像头支持切换展示,也可以展示rtsp地址. 使用NuGet如下: 代码如下 一.创建MainWindow.xaml代码如下.  <ws:Window x:Class="OpenCVSharpExample.MainWindow"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x

  • Openstack 使用migrate进行数据库升级实现方案详细介绍

    Openstack 使用migrate进行数据库升级实现方案详细介绍 OpenStack中随着版本的切换,新版本加入一些数据库表或者增加字段等是必然的事情,如何比较容易的进行这些数据库升级的适配和管理,这里就要用到oslo_db中的migrate了,这里以为M版本的heat为例,讲解一下migrate管理db的原理. 我们使用migrate需要用到的主要包含以下两部分:1.versions里面的为版本号+数据库适配脚本:2.migrate.cfg为migrate需要用到的配置文件,两部分的命名是

  • RocketMQ重试机制及消息幂代码实例解析

    这篇文章主要介绍了RocketMQ重试机制及消息幂代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.重试机制 1.由于MQ经常处于复杂的分布式系统中,考虑网络波动,服务宕机,程序异常因素,很有可能出现消息发送或者消费失败的问题.因此,消息的重试就是所有MQ中间件必须考虑到的一个关键点.如果没有消息重试,就可能产生消息丢失的问题,可能对系统产生很大的影响.所以,秉承宁可多发消息,也不可丢失消息的原则,大部分MQ都对消息重试提供了很好

随机推荐