openstack热迁移机制分析(libvirt热迁移模型、nova热迁移控制逻辑、调试方法)

前段时间在解决大内存热迁移失败的时候,查阅了下openstack热迁移相关知识,有了一些记录跟大家分享下。以基于L版openstack+qemu/kvm,跟大家分享下API库libvirt的热迁移机制和nova控制逻辑的一些记录。注:这篇博客我同步发布在我司的公众号上:openstack热迁移机制分析

一、libvirt热迁移原理

libvirt的迁移主要分为两个层面:网络数据传输层面和控制层面

1. 网络数据传输平面

网络数据传输指的是虚拟机的数据传输路径,libvirt提供了两种数据的传输路径。

1.1 本地传输

本地传输指:两个hypervisor之间直接建立连接传输数据。优点:相关的数据拷贝少。缺点:需要管理员额外的配置网络信息,需要在防火墙上面打开更多的端口来支持并发迁移,数据不一定支持加密(关键看hypervisor是否支持)。

1.2 隧道传输

隧道传输指:通过源主机和目标主机两个libvirtd之间的RPC隧道传输数据,数据要先拷贝到libvirtd,再由libvirtd传输到目标主机的libvirtd。优点:不需要重新配置网络,防火墙上面只需要一个端口就可以支持并发迁移,数据强加密。缺点:相关的数据拷贝多,所有流量都通过一个端口,容易造成网络拥堵。

综上来分析,对于私有云来说,数据是否加密并不是最重要的因素,关键是看在出现紧急情况下能否保证VM快速的迁移到目标主机。在隧道传输模式下,同一份数据需要被拷贝多次,并且所有的流量都通过一个端口走,在大内存、高业务(快速增加脏内存)的场景下,同样的网络带宽,隧道传输模式迁移速率较慢。因此,对于大内存、业务繁忙的场景下,首选本地传输模式。

2. 控制层面

控制层面只要是用来定义谁来负责整个迁移的过程,从大类分可分为管理类和非管理类两种控制角色。

2.1 管理类控制角色:

管理类控制角色指的是迁移交由libvirtd来控制整个迁移过程,具体又分为如下两种:

2.1.1 直连管理控制

由客户端自己直接管理控制迁移:客户端即跟源libvirtd连接,也跟目的libvirtd连接,当目的主机出现异常,反馈给客户端,再由客户端通知源libvirtd,整个过程,客户端是主角。

2.1.2 托管管理控制

对于托管模式,客户端的角色变成只是一个命令发起者,客户端异步发送迁移命令给源libvirtd,接下来的迁移过程由源libvirtd来负责,如果目的主机出现异常,直接反馈给源libvirtd,整个过程,源libvirtd是主角。这种控制模型有个好处就是客户端的状态不会影响迁移,比如客户端挂掉或者出现异常,迁移过程依然可以完成。

2.2 非管理类控制角色

非管理类控制角色指的是:迁移过程libvirtd不参与,而是由hypervisor自己的管理程序来负责整个迁移过程。客户端发送迁移命令给源hypervisor管理程序,由源hypervisor管理程序来负责整个迁移过程。这种模型的前提是hypervisor管理程序必须支持热迁移才可以,这个模型的好处就是客户端或者libvirtd程序出现异常挂死,整个迁移过程还是可以完成。

二、nova热迁移控制逻辑

nova迁移的大致流程如下图,api和scheduler模块并没有做太多的事,主要核心在conductor和compute模块,下面会重点介绍下这两个模块在迁移流程中所做的事。

  1. Conductor:

    1. 构建一个task,异步执行。

    2. 如果没有指定目标主机,向scheduler申请目标主机。

    3. 检查一些条件:不能迁移到源主机、目标主机必须是up状态、hypervisor类型必须一致、目标主机 hypervisor版本必须大于等于源主机hypervisor版本。

  2. S_compute:

    1. 检查cpu架构兼容、共享存储等等。

    2. 向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)

    3. 更新xml域定义文件
    4. 配置热迁移带宽,live_migration_bandwidth,如果设置成0,则会自动选择一个合适的带宽
    5. 监控libvirtd迁移进度

      1. 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来配置自动收敛和后拷贝模式。

      2. 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时间内迁移没有完成,就中止迁移。
    6. post_live_migration:在虚拟机数据迁移成功之后,还需要做一些其他的配置信息,比如:断开磁盘连接、清除网络设备、flag标志位、实例事件,更新源主机可用资源、更新实例调度信息、清除实例控制台tokens

  3. D_compute

    1. pre_live_migration:配置实例目录、镜像磁盘准备、vnc端口等等一些需要修改在xml文件里面的信息。

    2. post_live_migration:设置网络信息、更新xml文件、更新实例信息。

下面是调试过程中用到的两个方法:
  1. 在一个16G内存的虚拟机内模拟快速增加脏内存

mkdir /mnt/test

mount -t tmpfs -o size=65535M tmpfs /mnt/test/

fio -directory=/mnt/test -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=8k -size=12G -loops=100 -numjobs=1 -group_reporting -name=mytest -rate=100m

  1. 在执行迁移任务时候,可以通过下面命令观察迁移任务的状态

virsh domjobinfo instance-0000002b 


  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值