热迁移,也叫在线迁移(live migration),是虚拟机从一台主机上迁移到另一台主机上但是“不中断VM服务”的一种机制。这里“不中断”指的是在用户观感上,VM一直提供服务。而实际上,在热迁移的过程中,VM会短暂地暂停一段时间。
openstack的热迁移是nova组件调用libvirtd完成的。我们一般通过nova配置文件(path:/etc/nova/nova.conf)来设置有关热迁移的参数,从而让VM按照我们希望的方式进行在线迁移。
nova配置文件有关热迁移的参数有如下几个:
- live_migration_bandwidth
- live_migration_downtime
- live_migration_downtime_steps
- live_migration_downtime_delay
- live_migration_completion_timeout
- live_migration_permit_post_copy
- live_migration_permit_auto_converge
下面详细地介绍这几个参数代表的意义以及对热迁移产生的影响。
live_migration_bandwidth
它是迁移期间要使用的最大带宽(以MiB / s为单位),默认值是0,意味着不限制迁移带宽。
live_migration_downtime、live_migration_downtime_steps、live_migration_downtime_delay
这三个参数都是有关热迁移停机时间(downtime)的。我们说热迁移过程中VM会短暂的暂停,而VM暂停的时间就是停机时间,它与这三个参数有关。
实际上,热迁移是转移内存(或存储)的过程。源主机不断把虚拟机的内存转移到目的主机,直到源主机仅仅省一部分可以一次转移完成的内存未被转移,此时把源主机上的虚拟机暂停,转移掉最后这一部分。这样做是因为,在转移内存时,源主机上的虚拟机有可能在提供有关内存写入的服务,生成新的未被迁移的内存,这就是所谓的“脏内存”,如果不暂停虚拟机,“脏内存”一直产生,迁移永远不能完成。
openstack下热迁移的停机时间(downtime)不是单纯设置一个数字那么简单,它停机策略要稍微复杂一点。openstack在迁移过程中的停机时间是变化的,该值不断增加,在一段时间后停机时间达到最终的最大值,这个值就是由live_migration_downtime设定的。而live_migration_downtime_steps表示达到最大值所经历的次数,live_migration_downtime_delay表示每一次增加downtime的间隔时间。下面举一个实验的例子,来具体说明停机时间是如何变化的:
进行实验的虚拟机的内存为4GiB。在/etc/nova/nova.conf设置的三个参数的值如下:
live_migration_downtime=5000 / live_migration_downtime_steps=7 / live_migration_downtime_steps=75
查看nova的log日志(path:/var/log/nova/nova-compute.log),找到了nova的变化情况
- Increasing downtime to 714 ms after 0 sec elapsed time
- Increasing downtime to 1326 ms after 300 sec elapsed time
- Increasing downtime to 1938 ms after 600 sec elapsed time
- Increasing downtime to 2550 ms after 900 sec elapsed time
- Increasing downtime to 3162 ms after 1200 sec elapsed time
- Increasing downtime to 3774 ms after 1500 sec elapsed time
- Increasing downtime to 4386 ms after 1800 sec elapsed time
- Increasing downtime to 4998 ms after 2100 sec elapsed time
可以看到,初始的downtime是一个较小的值,经历七步以后,downtime增加到接近5000(ms)。初始的downtime值是由live_migration_downtime/live_migration_downtime_steps得来的,这里就是5000/7=714(ms)。每一步增加的时间是由初始的downtime*(live_migration_downtime_steps-1)/live_migration_downtime_steps得到的,这里就是714*(7-1)/7=612(ms)。
除此之外,我们发现,迁移每进行300s,downtime就变化一次,300s这个时间是由live_migration_downtime_delay决定的。在这里,300s=live_migration_downtime_delay*VM内存大小(GiB),即75*4=300。
在实际实际工作中,在最大停机时间一定的情况下,有时我们需要尽快完成迁移,不在乎停机的时间,这时我们减少live_migration_downtime_steps和live_migration_downtime_delay,尽快达到最大停机时间;有时我们不在乎迁移总时长,希望服务中断的时间越短越好, 就尽量增大live_migration_downtime_steps和live_migration_downtime_delay,慢慢地达到最大停机时间,这样在有可能在最大停机时间内完成VM的迁移。
live_migration_completion_timeout
这个参数表示整个迁移过程所允许的最大迁移时间,若超过这个时间迁移未完成,则迁移失败,取消迁移。这个参数跟live_migration_downtime_delay一样,也与内存有关系。实际的最大完成时间=live_migration_completion_timeout*VM内存大小(GiB)。live_migration_completion_timeout的默认值为800,假如虚拟机的内存大小是4GiB,那么意味着在3200s的时候,未迁移完成的该虚拟机停止迁移,迁移失败。
live_migration_permit_post_copy
live_migration_permit_post_copy是一种迁移模式,叫做后拷贝。在不设定此模式的情况下,VM都是通过前拷贝完成的,所谓前拷贝指的是VM上所有内存数据都是在切换到目标主机之前拷贝完的。而后拷贝会先尽可能快的切换到目标主机,因此后拷贝模式会先传输设备的状态和一部分(10%)数据到目标主机,然后就简单的切换到目标主机上面运行虚拟机。当发现访问虚拟机的某些内存page不存在时,就会产生一个远程页错误,触发从源主机上面拉取该page的动作。这种后拷贝的迁移模式相对于前拷贝更加危险,当网络不同,或任意一台主机出现故障的时候,虚拟机会出现异常。
live_migration_permit_auto_converge
live_migration_permit_auto_converge是另一种迁移模式,叫做自动收敛。它在虚拟机长时间处于高业务下而影响迁移的时候,调整vcpu的参数减少vcpu的负载,降低“脏内存”的增长速度,从而使迁移能够顺利完成。
在libvirtd中,当“脏内存“的增长速度大于迁移内存传输速度时,触发自动收敛模式,libvirtd在一开始降低了20%的vcpu性能,如果“脏内存“的增长速度依旧大于迁移内存传输速度,则在此基础上再降低10%的性能,直到“脏内存“的增长速度小于迁移内存传输速度。值得注意的是,该模式只能保证虚拟机未迁移的内存持续减少,但不能保证迁移在设定的最大迁移时间内迁移完成。
-
-
NOVA在迁移流程中所做的事
-
Conductor:
-
构建一个task,异步执行。
-
如果没有指定目标主机,向scheduler申请目标主机。
-
检查一些条件:不能迁移到源主机、目标主机必须是up状态、hypervisor类型必须一致、目标主机 hypervisor版本必须大于等于源主机hypervisor版本。
-
-
S_compute:
-
检查cpu架构兼容、共享存储等等。
-
向libvirtd发送迁移指令:
根据迁移flag参数选择迁移模型
-
几个重要的flag参数的含义如下
-
VIR_MIGRATE_TUNNELLED:是否使用隧道网络传输数
-
VIR_MIGRATE_PEER2PEER:是否启用托管模式
-
VIR_MIGRATE_LIVE:是否启用热迁移
-
VIR_MIGRATE_PERSIST_DEST:是否持久化域定义文件到目标主机(也即使迁移过后,目标主机上面也有改虚拟机的域定义文件)
-
VIR_MIGRATE_UNDEFINE_SOURCE:是否在迁移之后在源主机删除域定义文件
-
VIR_MIGRATE_PAUSED:是否让目标侧的域一直处于挂起状态
还有其他的一些参数,可以参考libvirt.py文件(centos7:/usr/lib64/python2.7/site-packages/libvirt.py)
-
- 更新xml域定义文件
- 配置热迁移带宽,live_migration_bandwidth,如果设置成0,则会自动选择一个合适的带宽
-
监控libvirtd迁移进度
-
libvirtd数据迁移逻辑:libvirtd迁移分3个阶段完成
- step1:标记所有的脏内存
- step2:传输所有的脏内存,然后开始重新计算新产生的脏内存,如此迭代,直到某一个条件退出
- step3:暂停虚拟机,传输剩余数据
因此这里最关键的点就在于step2阶段的退出条件,早期的退出条件有:
- 50%或者更少的内存需要迁移
- 不需要进行2次迭代或迭代次数超过30次。
- 动态配置downtime时间
- host主机策略(比如host 5min后关机,这个时候就需要迁移所有的VMs)
在L版openstack的时候,支持的是downtime配置时间,因此step2的每次迭代都会重新计算新的脏内存以及每次迭代所花掉的时间来估算带宽,再根据带宽和当前迭代的脏页数,就可以计算出传输剩余数据的时间。如果这个时间在配置的downtime时间内是可以接受的,就转到step3,否则就继续step2。
配置downtime的step2存在一个问题,如果虚拟机的脏内存产生速度很快,也就是意味每次迭代的数据量都很大,downtime时间一直无法满足推出条件,无法进入step3。因此针对这种情况下,libvirt出了一些新的特性:
- post-copy模式:前面所说的都属于pre-copy模式,也就是说所有的数据都是在切换到目标主机之前拷贝完的。在post-copy模式下,会先尽可能快的切换到目标主机,因此后拷贝模式会先传输设备的状态和一部分(10%)数据到目标主机,然后就简单的切换到目标主机上面运行虚拟机。当发现访问虚拟机的某些内存page不存在时,就会产生一个远程页错误,触发从源主机上面拉取该page的动作。当然这种模式也引发了另一个重要的问题:如果其中一台主机宕机,或出现故障,或网络不通等等,会导致整个虚拟机异常。
-
自动收敛模式:如果虚拟机长时间处于高业务下而影响迁移的话,libvirtd会自动调整vcpu的参数减少vcpu的负载,达到降低脏内存的增长速度,从而保证迁step2的推出条件。
这两个新的特性在N版的openstack中都已经应用进来,可以通过live_migration_permit_auto_converge和live_migration_permit_post_copy来配置自动收敛和后拷贝模式。
-
nova监控
nova在迁移的过程中是作为客户端的角色而存在,nova只有不断的轮询libvirtd才能获得迁移的信息,因此nova监控最核心的就是一个轮询处理,nova会每隔0.5s向libvirtd轮询迁移信息,并以此来做一些处理,具体的处理也很简单,不在这里详细说,这里重点看下轮询的几个最核心的变量的含义:
- downtime_steps:前面有说到libvirtd迁移由step2进入step3的条件是在downtime时间内,libvirtd能够将剩余的数据传输完毕。因此这个参数的主要设计逻辑是:分steps次给libvirtd喂downtime时间。那么多久喂一次?一次喂多少?nova通过一个算法来计算,这个算法的变量有data_db,live_migration_downtime,live_migrate_downtime_steps,live_migrate_downtime_delay.其中后面3个参数都是可以通过配置文件配置的。这个算法最终会计算出一个元组列表,比如:[(0, 46), (150, 47), (300, 48)...]每个元组的第一个值是delay时间,第二个值downtime时间。直到最后一次就是你配置的live_migrate_downtime_delay和live_migration_downtime。
- progress_timeout、progress_watermark:这两个变量的设计主要是用来防止libvirtd迁移出现某种异常导致迁移数据在一定时间没有发生变化,这时候就会启动progress_timeout来中止轮询。理论上,这种情况应该由libvirtd的配置来配置,而nova只需取出异常就可以,但是目前libvirtd并没有相关配置。progress_watermark是用来标示上次查询到的剩余数据,在单次迭代中,如果数据有在迁移,那么水位总是递减的。社区在水位的处理上面是有问题的,社区并没有考虑到当下次迭代开始之后,轮询到的剩余数据比上次迭代的水位要大,因此这时候应该把水位重新置位到当前迭代来重新标记,而不是让水位停留在上次迭代的位置,从而错误的触发progress_timeout。 我给社区提了一个patch,就是为了在下次迭代的时候,将水位重新置到当前迭代。在N版里面,已经加入对后拷贝模式的支持,正如前面说的,后拷贝模式虽然能假象地提高迁移速度,但是是非安全性的。patch:https://review.openstack.org/#/c/374587/
- completion_timeout:这个变量的设计主要就是防止libvirtd长时间处于迁移过程,可能是原因是当时的网络带宽太低等等。这个时间是从一开始轮询就开始计时的,一旦在completion_timeout时间内迁移没有完成,就中止迁移。
-
-
post_live_migration:在虚拟机数据迁移成功之后,还需要做一些其他的配置信息,比如:断开磁盘连接、清除网络设备、flag标志位、实例事件,更新源主机可用资源、更新实例调度信息、清除实例控制台tokens
-
-
D_compute
-
pre_live_migration:配置实例目录、镜像磁盘准备、vnc端口等等一些需要修改在xml文件里面的信息。
-
post_live_migration:设置网络信息、更新xml文件、更新实例信息。
-
virsh 查看迁移进度
-
-
nova 查看迁移任务