云计算国产化之路 vnc登录 文件系统直通(virtio-9p) 扩展qemu接口 gpg WARNING 珍藏博客 虚拟内存情况dommemstat分析 免密码自动登录脚本 Linux网桥 测试网段IP占用情况 Linux 进程状态 systemc强制依赖 调试openstack ut uefi p2v 重做ubuntu内核 virsh创建虚拟机简介 virtio IO路径 虚拟化层升级后磁盘无法卸载卷 vmtouch使用 Taint flags 主机和虚拟机文件共享处理的几种方法 kvm分析工具 kvm中对磁盘的io cache 虚拟化不同导致的guestos中软件安装差异(未解决) 设备直通(PCI Assignment)到底是咋整的 virtio到底是咋整的 内核启动参数 虚拟化实时性提升(零)之配置步骤 中断虚拟化(pic)到底是咋整的 中断虚拟化(apic)到底是咋整的 Raid卡配置丢失导致服务器无法启动 tainted kernels cpu stuck for 23s问题分析 虚拟化实时性提升(一)之hostOS切换为强实时系统 内存虚拟化到底是咋整的 qemu-kvm中vcpu虚拟化到底是咋整的 风河虚拟化技术点分析 使用qga的好处 qemu迁移代码分析 虚拟机串口配置及其导出到主机pts和console.log System-based I/O vs. Raw I/O 虚拟机使用Hugepage(大页) 硬件辅助分页(hardware assisted paging) 修改centos7默认启动项目 virtio的工作流程——kernel中virtio-pci初始化(2) virtio的工作流程——qemu中virtio-backend初始化(1) qmp ceilometer取不到memory.usage指标 Virtio-Balloon超详细分析 slabtop输出 虚拟机磁盘cache导致的host os kernel崩溃 虚拟机cpu和memory性能优化测评 PCI配置空间(PCI Configuration Space) centos下网卡设备直通(VT-dpci passthrough)遇到的问题及其解决思路 libguestfs详解 yum卸载软件包及其依赖 通过原始Centos ISO来定制自己的ISO centos下网卡设备直通(VT-d,pci passthrough) (占位符)window虚拟机中拔盘如何通知到libvirt和qemu后端的 cirrus漏洞分析CVE-2017-2615 XSA-208 qcow2随笔 控制寄存器概览 ceilometer对接postgresql 解压initrd和vmlinuz qemu guest agent验证 QEMU升级指南(待续) ubuntu中kdump的配置 qemu(2.3)接口梳理 热迁移导致的FC存储场景下的multipath卷残留问题分析 virsh命令(4)secret,snapshot,pool,volume部分 virsh命令(3)之interface,filter,network virsh命令(2)monitor,host,nodedev部分 virsh命令(1)之domain部分 QEMU内存管理之FlatView模型(QEMU2.0.0) ovirt基于sanock的高可用(主机粒度HA) Sanlock防脑裂场景功能测试用例 gnocchi配置及与ceilometer对接指南 make patch for libvirt in centos centos使用sanlock指导 高可用nfs资料 ubuntu14中使用sanlock指导 LVM操作指南 sanlock相关功能验证流程汇总 make patch for libvirt in ubuntu libvirt.so.0-version `LIBVIRT_PRIVATE_1.2.7' not found gdb debug libvirt 基于ubuntu社区源码包编译libvirt compile libvirt(centos) No PCI buses available nfs lead to Linux halt nfs install and config anti-virus for cloud platform nova fetch image from glance(something about _base) token auth process ovs入门指南 virt software anti-virus something about ceilometer disk sample context interview questions openstack vm injection openstack Restful and RPC murano 概览 创建虚拟机流程(compute节点)之网络创建 创建虚拟机流程之compute_api之虚拟机实例填充之配额检测 创建虚拟机流程之compute_api之基本参数生成 创建虚拟机流程之compute_api 创建虚拟机流程(主) 创建虚拟机之image 创建虚拟机流程之准备网桥 创建虚拟机流程之virt 创建虚拟机流程之compute节点 CI/CD研发流程之工程创建 CI/CD研发流程之代码合入 CI/CD研发流程之UT(单元测试) 向openstack社区合入代码记 openstack/ceilometer/gnocchi杂谈 影子页表原理 mem_add(exec.c) qemu编译安装调试 openstack/ceilometer/gnocchi之Grafana简介 openstack wiki etcd openstack计量ceilometer openstack计费cloudKitty enventlet backdoor USB 安装VMWARE ESX pycharm设置指南 无法执行modprobe -a kvm-intel解决办法 QEMU配置项 网络不通小记 libvirt之XML(虚拟机定义文件) openstack-horizon 证书认证 ceilometer与ceph对接 openstack定时任务剖析(TODO) 服务器重启后mongodb进程无法启动 ubuntu14下新增openstack服务到service的导引 ERROR 1045(28000)-数据库连不上 Python两个内置函数—locals和globals unknown exit, hardware reason 31

虚拟机迁移之热迁移(live_migrate)

2016年05月03日

背景知识

迁移主要是将虚拟机从一个地方挪动到另外一个地方,一般情况下为从一个物理机移动至另外一个物理机。
这样,为管理员迁空物理机提供了途径,进而管理员可以对物理机维护。再就是,迁移可以作为DRS的基础,实现资源的负载均衡等。

迁移分为热迁移和冷迁移。
冷迁移在openstack中同resize类似,本文不做分析。
热迁移,迁移过程中虚拟机保持不当机,保证了服务的可持续性,提高了云使用者的体验。

本文主要分析了nova中的热迁移流程,qemu层面的迁移,看本人这篇博文

核心总结

openstack的热迁移是调用了libvirt接口virDomainMigrateToURI2 或者virDomainMigrateToURI
进行的,openstack本质上只做了两件事。

  1. 迁移前的源主机和目标机之间的协商确认。看看,两者之间能否迁移。
  2. 迁移的后续处理。成功了,需要在源主机上进行清理操作;失败了,需要到目标主机上进行清理操作,回滚。

源码分析

nova-api

热迁移的入口在nova/nova/api/openstack/compute/migrate_server.py中
主要是对请求相关参数进行读取,获取到当前实例信息后,调用compute/api对应服务。
这里注意下,热迁移一般情况下是指定目标主机的,当然没有指定主机后续代码会遴选一个。
磁盘超量参数目前没有实现(具体见迁移任务实施部分)

compute/api

对实例状态等进行一系列校验

  • 虚拟机必须处于非锁定状态
  • 环境启用cell场景下,校验cell是否存在是否只读
  • 虚拟机实例状态必须是运行态或者暂停态

更新虚拟机实例的任务状态为迁移中
在instance_action中记录一条action为live-migration信息
调用conductor的api进行后续的业务处理

nova-conductor

将目标主机名称作为调度条件传入,rpc方式调用conductor服务

在conducotr的rpc后端(nova/nova/conductor/manager.py)根据前面的入参走到热迁移分支

_live_migrate方法的主要功能是构造出migration对象,migration对象入库,数据库字段如下

然后基于migrate对象和已有的数据,通过_build_live_migrate_task
构造了一个迁移任务,并且运行迁移任务,见下图

迁移任务执行

在conductor/tasks/live_migrate.py中_execute方法
校验实例状态,确保为running获取paused
校验迁移的源主机是否启动(详细见后)
校验目标主机是否适合进行迁移(没有目标主机,根据相关条件选出一个,详细见后)
向源主机发送rpc请求,通知源主机开始热迁移。

验证源主机是否启动(_check_host_is_up)

主要根据配置的driver不同,servicegroup_api.service_is_up会采用不同的方式判断当前主机是否启动

这里的dirver有三种

  • db(如果不指定,使用这种方式)
    • 从service表中读取出最后更新状态,与当前时间比较查看是否超出阈值。
  • mc(使用memcache)
    • 我们在nova.conf中指定的是这种方式
    • mc类型的driver对应的is_up方法如下

    • -zk(zookeeper)

_find_destination

我们看下没有有目标主机,生成目标主机策略(conductor/tasks/live_migrate.py方法_find_destination)
不停的调度获取主机,然后判断主机是否可以进行热迁移,直到找到一个满足要求的主机。
判断是否迁移主要从两个方面
1.hpyervisor类型是否满足要求
2.目标主机能否适合这次热迁移(这里和有主机的情况下一致,详细分析见后)

有目标主机的情况下,判断目标主机是否适合进行热迁移

主要有以下五个判断项目
这里只重点分析:
内存判断(_check_destination_has_enough_memory)
是否支持热迁移判断(_call_livem_checks_on_host)

  • 内存判断(_check_destination_has_enough_memory)
    数据库的compute_nodes表中记录了计算节点的内存大小。

  • 是否可以热迁移(_call_livem_checks_on_host)
  • 目标节点上的底层判断

  • 反向rpc调用源主机。


nova-compute

计算节点接收到rpc请求后孵化一个线程,调用_do_live_migration执行迁移过程

线程内主要干了两件事情: 1,与目标主机进行协商准备进行迁移,到目标节点创建一系列必要资源等(后面详细分析)
2,调用底层接口进行迁移,同时处理后事:迁移成功,则清理源节点,失败则清理目标节点(后面详细分析)

与目标主机进行协商预迁移(pre_live_migration)

1.调用driver层预迁移 (后面详细分析)
2.绑定网络至目标主机(什么没做)
3.目标主机增加虚拟机的过滤规则(什么没做)

dirver层的预迁移(nova/virt/libvirt/driver.py)

实例路径不共享,则需要在目标主机上创建相应的目录
块设备不共享(虚拟机实例路径共享)需要从虚拟机指定路径下载相关镜像文件到目标主机

不进行块迁移(如果进行的话上面会进行操作)且虚拟机目录不共享(共享的话不用处理)
需要在虚拟机实例目录下创建console文件,并尝试获取kernel和ramdisk

由于后端使用的是rbd,这里什么也没有执行
‘rbd=nova.virt.libvirt.volume.net.LibvirtNetVolumeDriver’

有附加盘,情况下不允许进行块迁移
todo1.block_device_mapping转换(需要深入理解)
todo 2 .在理解块迁移实质的基础上,理解这里做此规定的原因

下面这段代码比较重要,会在创建虚拟机时候复用
主要是在目标主机上为虚拟机准备网络,当虚拟机迁移过来后直接和对应的网络进行对接即可。

整理必要的信息进行返回

绑定网络至目标主机(我们用的是neutronv2/api这里什么也没干) ####\

调用底层进行迁移(driver中 _live_migration)

在driver层面是通过_live_migration_operation进行迁移操作
然后通过_live_migration_monitor检测迁移进度

迁移操作(_live_migration_operation)

迁移操作是通过调用libvirt对应接口(virDomainMigrateToURI2 或者virDomainMigrateToURI)实现的
接口入参中很重要的一项就是迁移配置项。
配置项约束了迁移过程中的动作,作为入参传给上述的libvirt迁移函数,间接决定了迁移过程中的相应动作,相关参数可以如下:




那么下面的代码就不难理解了,读取出迁移配置项目
配置项之间逻辑或出一个值logical_sum = six.moves.reduce(lambda x, y: x | y, flagvals)
分析见后 没配置VIR_DOMAIN_XML_MIGRATABLE,或者没有VNC和附加盘走接口1,其他情况走接口2


迁移检测(_live_migration_monitor)

由于迁移是一个比较耗时的操作,所以在上述的迁移动作执行后,由这里进行迁移状态的侦测。
通过host.DomainJobInfo.for_domain(dom)获取到当前操作的信息,根据info.type获取迁移的状态。

如果状态完成,则调用外面传入进来的_post_live_migration方法处理(代码太长逐段分析)

  1. 调用driver的post_live_migration,其实是想在libvirt层面上去断开虚拟机与卷的链接(由于使用的rbd实际pass)
  2. 通知cinder去断开链接
  3. 源主机网络处理
    1. 清理防火墙规则(driver.unfilter_instance)
    2. 对应网卡信息从源主机拔出
  4. 目标主机上执行后续操作


    5.进行一系列的清理操作及其节点信息的更新
    如果状态失败,则调用传入进来的_rollback_live_migration方法进行回滚
    1.目标节点卷断连
    2.目标节点清理实例,包括之前创建的网络信息等。nplug vif等

附加: logical_sum = six.moves.reduce(lambda x, y: x | y, flagvals)

1.lambda算式,类似于一个小函数 2.x|y x和y的逻辑或

  1. six.moves.reduce 从flagvals中取出两个值调用lambda算式,得出结果作为x,再从flagvals中取出一个值 (lambda x, y: x+y, [1, 2, 3, 4, 5])类似于 ((((1+2)+3)+4)+5) dfaf 综上所述,这句代码是求算出flagvals所有值的一个逻辑或。