libvirt live migration 流程

http://chinaunix.net/uid-29634482-id-5163590.html

本文主要讲解通过libvirt命令virsh migrate做虚拟机在线迁移所涉及到的代码流程。

libvirt版本为1.1.1。

 

基本知识

 

1. peer to peer的在线迁移(Managed peer to peer migration):client只负责触发迁移,源host负责控制迁移流程。

    libvirt层的控制流图如下:

     http://chinaunix.net/attachment/201508/21/29634482_1440168175nQQU.pnglibvirt live migration 流程

2. 虚拟机本地数据传输(hypervisor native tranport):虚拟机之间直接传输数据,可能需要开多个端口(hypervisor native tranport)

    其数据流图如下:

     http://chinaunix.net/attachment/201508/21/29634482_1440168183gWGh.png

命令发起端

 

以增量迁移为例,迁移命令如下:

virsh migrate --live virtual01 --copy-storage-inc  qemu+tcp://192.168.136.96/system tcp://192.168.136.96  --verbose --persistent  --p2p


注:迁移前,需要保证源端和目的端虚拟机启动时需要的文件存在。

      virsh qemu-monitor-command instance-000045ec --hmp info version : 可以检查虚拟机instance-000045ec当前运行的版本


 

上述命令在libvirt代码中对应的处理函数为cmdMigrate(tools/virsh-domain.c)

> cmdMigrate: 

     |--> virThreadCreate(启动线程) --> doMigrate(线程的执行函数)

     |--> vshWatchJob(等待迁移结束,并获取迁移进度)

                   |--> virDomainGetJobInfo(获取进度)

                   |--> print_job_progress(打印进度)

                   |--> vshMigrationTimeout(超时suspend虚拟机)

                   |--> virDomainAbortJob(迁移结束:成功/失败)

     |--> virThreadJoin(结束线程)

 

> doMigrate:

     |--> virDomainMigrateToURI3(src/libvirt.c)

                  |--> virDomainMigratePeer2PeerParams

                              |--> virDomainMigratePeer2PeerFull

                                        |--> domain->conn->driver->domainMigratePerform3Params(注册为remoteDomainMigratePerform3Params:src/remote/remote_driver.c)

                                                 |--> call: REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3_PARAMS:305

 

从上述流程可以看到,最终virsh migrate命令触发了一个call函数(同步处理),这个需要和libvirtd服务通信,由libvirtd来处理该请求。

 

命令处理端 -- libvirtd线程

 

再来介绍libvirtd启动流程中与在线迁移相关的部分:

> main(daemon/libvirtd.c)

     |--> virNetServerNew(src/rpc/virnetserver.c)

               |--> virThreadPoolNew(src/util/virthreadpool.c):jobFunc函数指针被赋值为virNetServerHandleJob

                           |--> virThreadCreate:创建线程,执行函数为virThreadPoolWorker

    

> virThreadPoolWorker

     |--> virCondWait:等待job 

     ?--> virNetServerHandleJob:线程被唤醒后,调用jobFunc执行要做的操作

 

> virNetServerHandleJob(src/rpc/virnetserver.c)

     |--> virNetServerProcessMsg

                 |--> virNetServerProgramDispatch

                             |--> virNetServerProgramDispatchCall(virNetServerProgramDispatchCall):主要处理从client端发过来的请求

                                        |--> virNetServerProgramGetProc:根据client端virsh migrate最终发过来的请求,获得要处理的函数等内容(返回prog->procs[305])

                                        |--> dispatcher->func:执行函数根据client端发过来的请求而定(此处为命令virsh migrate)

                                        |--> virNetServerClientSendMessage:请求处理完毕,给client端发送信息


注:remoteProcs数组在文件daemon/remote_dispatch.h中定义,主要描述各个Method处理的函数与参数等。

如:REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3_PARAMS:305对应的函数为remoteDispatchDomainMigratePerform3ParamsHelper

       即,prog->procs[305] = remoteDispatchDomainMigratePerform3ParamsHelper

 

remoteProcs的赋值流程如下:

> libvirtd:main

     |--> virNetServerProgramNew(src/rpc/virnetserverprogram.c)

               |--> prog->procs = remoteProcs


 

到这里,可以看到virsh migrate命令的最终处理函数为remoteDispatchDomainMigratePerform3ParamsHelper!

> remoteDispatchDomainMigratePerform3ParamsHelper(daemon/remote_dispatch.h:编译后出现此文件,被daemon/remote.c引用)

      |--> remoteDispatchDomainMigratePerform3Params(daemon/remote.c)

                |--> virDomainMigratePerform3Params(src/libvirt.c)

                           |--> conn->driver->domainMigratePerform3Params(注册为qemuDomainMigratePerform3Params:src/qemu/qemu_driver.c)

 

> qemuDomainMigratePerform3Params(src/qemu/qemu_driver.c)

      |--> qemuMigrationPerform(src/qemu/qemu_migration.c)       <-- 分叉口


注:qemuMigrationPerform(src/qemu/qemu_migration.c)

1. 传入的参数包括p2p或者tunnelled,则进入qemuMigrationPerformJob

2. 传入的参数不包括p2p,也不包括tunnelled,则进入qemuMigrationPerformPhase


 

路线1:参数中包括p2p或者tunnelled(p2p迁移要走的代码路径)

> qemuMigrationPerformJob

     |--> qemuMigrationJobStart

     |--> doPeer2PeerMigrate因为传入的参数包括p2p,所以走进此函数

               |--> virConnectOpen,连接目的端虚拟机(处于paused状态)

               |--> doPeer2PeerMigrate3(v3协议)

 

> doPeer2PeerMigrate3(src/qemu/qemu_migration.c)

     |--> qemuMigrationBeginPhase:获取虚拟机xml信息,迁移状态为QEMU_MIGRATION_PHASE_BEGIN3

     |--> dconn->driver->domainMigratePrepare3Params,注册为qemuDomainMigratePrepare3Params,迁移状态为QEMU_MIGRATION_PHASE_PREPARE

     |--> doNativeMigrate --> qemuMigrationRun,调用之前迁移状态为QEMU_MIGRATION_PHASE_PERFORM3,成功则迁移状态为QEMU_MIGRATION_PHASE_PERFORM3_DONE

     |--> dconn->driver->domainMigrateFinish3Params,注册为qemuDomainMigrateFinish3Params,迁移状态为QEMU_MIGRATION_PHASE_FINISH3

     |--> qemuMigrationConfirmPhase,迁移状态为QEMU_MIGRATION_PHASE_CONFIRM3

 

以上可以看到,整个p2p迁移过程中,迁移状态图如下:

PHASE_BEGIN3 --> PHASE_PREPARE --> PHASE_PERFORM3 --> PHASE_FINISH3 --> PHASE_CONFIRM3

                                      |--> PHASE_PERFORM3_DONE(迁移成功)

                                      |--> PHASE_CONFIRM3_CANCELLED(迁移失败)


注:doPeer2PeerMigrate3(src/qemu/qemu_migration.c),共经历以下几个阶段:

1. Begin阶段,发起端调用函数qemuDomainDefFormatXML完成xml的生成时,解析运行态虚拟机xml使用了标记MIGRATABLE、SECURE、UPDATE_CPU;

2. Prepare阶段,目的端调用函数qemuMigrationPrepareDirect --> qemuMigrationPrepareAny

                         |--> qemuProcessStart,在目的端启动一个带-incoming参数的kvm虚拟机

                         |--> qemuMigrationStartNBDServer,启动NBD Server

3. Perform阶段,发起端调用函数qemuMigrationRun,用于虚拟机的内存、数据、状态的迁移,在结尾部分会专门讲解此函数

4. Finish阶段,目的端调用函数qemuMigrationFinish完成迁移后的处理工作:

                         |--> qemuMigrationStopNBDServer,停止NBD Server

                         |--> qemuProcessStartCPUs,恢复目的端虚拟机为running状态

5. Confirm阶段,发起端调用函数qemuProcessStop,停止源端虚拟机


 

路线2:如果参数中没有p2p,也没有tunnelled,则走下面这个逻辑

> qemuMigrationPerformPhase(src/qemu/qemu_migration.c)

     |--> qemuMigrationJobStartPhase,迁移状态QEMU_MIGRATION_PHASE_PERFORM3 --> qemuMigrationCleanup

     |--> doNativeMigrate --> qemuMigrationRun

     |--> qemuMigrationJobStartPhase,迁移状态QEMU_MIGRATION_PHASE_PERFORM3_DONE 

               ?--> qemuMigrationCleanup:异步处理job

                         |--> qemuDomainObjDiscardAsyncJob

以上可以看到迁移状态只有PHASE_PERFORM3,可以确定,迁移走入此路径之前,必然已经完成了PHASE_BEGIN3 和 PHASE_PREPARE,此处不作讲解。

 

下面再介绍下Perform阶段调用的迁移核心函数qemuMigrationRun:

> qemuMigrationRun(src/qemu/qemu_migration.c)

     |--> qemuMigrationDriveMirror:通过driver-mirror方式同步源端和目的端的数据盘数据,有几个数据盘,就调几次此函数,底层使用了NBD实现(调用qmp_dirve_mirror命令)

     |--> qemuMonitorMigrateToFd:通过此命令同步内存数据和虚拟机状态(调用qmp_migrate命令)

     |--> qemuMigrationWaitForCompletion:等待内存数据和虚拟机状态迁移完成,然后退出migrate(调用qmp_migrate_cancel命令)
     |--> qemuMigrationCancelDriveMirror:数据盘数据迁移完成,然后退出dirve-mirror(调用qmp_block_job_cancel命令)
  

drive_mirror依赖nbd,所以可以看到:

在Prepare阶段还调用了函数qemuMigrationStartNBDServer

> qemuMigrationStartNBDServer(src/qemu/qemu_migration.c)

     |--> qemuMonitorNBDServerStart:在目的端启动nbd server(调用qmp_nbd_server_start命令)

     |--> qemuMonitorNBDServerAdd:将目的端的disk通过nbd导出(调用qmp_nbd_server_add命令)

在Finish阶段还调用了函数qemuMigrationStopNBDServer:

> qemuMigrationStopNBDServer(src/qemu/qemu_migration.c)

     |--> qemuMonitorNBDServerStop:在目的端停止nbd server(调用qmp_nbd_server_stop命令)
 

至此,可以看到,libvirt的在线迁移最终调用了至少四个qemu-kvm接口(NBD相关命令这里不做讲解)

1. qmp_drive_mirror:同步数据盘数据;

2. qmp_migrate:同步内存数据与虚拟机状态;

3. qmp_migrate_cancel:停止内存数据与虚拟机状态的迁移,一般完成时调用

4. qmp_block_job_cancel:停止drive-mirror数据盘同步job,一般完成时调用

 

后续会再介绍qemu-kvm中虚拟机迁移的实现。

 

参考:

http://libvirt.org/migration.html

http://www.cnblogs.com/sammyliu/p/4572287.html