qemu-kvm虚拟机live迁移源代码解读

首先,使用kvm正常打开一个虚拟机:sudo kvm ./xp1.qcow2

然后,再使用kvm命令打开同一个虚拟机。使用如下命令:sudo kvm ./xp1.qcow2 -incoming tcp:0.0.0.0:11111

读者可能会说,两台虚拟机同时使用同一个image,会造成image数据的丢失,可能会彻底破坏整个虚拟硬盘的数据完整性,从而造成数据丢失,甚至操作系统都无法启动!

是的,但这里的-incoming选项,实际上并没有真正启动虚拟机。它首先创建TCP等链接,准备接受虚拟机迁移的数据传入,然后就暂停了虚拟机的执行。直到虚拟机迁移完成后,才会恢复进入虚拟机运行状态。因此,它在迁移完成前,并没有操作虚拟磁盘,因此不会造成如上的问题。


qemu-kvm:versoin = 1.2.0


源端:

migrate命令会调用hmp_migrate()函数{defined in hmp.c}


hmp_migrate()会调用qmp_migrate(...)函数{defined migrate.c}

(1) 判断当前migrate状态是否为active;此时迁移状态应该为MIG_STATE_SETUP

(2) 判断是否有block migrate设备存在;

(3) 初始化;

      调用migrate_init(¶ms)

(4)  判断migrate协议:TCP/UNIX/EXEC/FD 开始迁移。

       调用 tcp_start_outgoing_migration(s, p, errp) {p:=host_port}

            exec_start_outgoing_migration(s, p)

            unix_start_outgoing_migration(s, p)

            fd_start_outgoing_migration(s, p)

在这里,假设用的是tcp协议。


tcp_start_outgoing_migration(s, p, errp){defined in migration-tcp.c}

(1)  MigrationState中的函数指针赋值

(2)  调用inet_connect(...)连接监听目的虚拟机

(3)  创建迁移处理线程

      调用migrate_fd_connect(MigrationState *s


migrate_fd_connect(MigrationState *s{defined in migration.c 真正的迁移方法}

(1) 当前迁移状态设为MIG_STATE_ACTIVE

(2) 定制QEMUFile文件

     调用qemu_fopen_ops_buffered(...)返回QEMUFile

          1) QEMUFileBuffered初始化

          2) 定制QEMUFile文件

                调用qemu_fopen_ops(...)返回QEMUFile

(3) 调用qemu_savevm_state_begin(QEMUFile, const MigrationParams)

          初始化 se->ops->save_live_setup block_save_setupram_save_setup)

(4) 调用migrate_fd_put_ready(MigrationState *)

     遍历实现设备内存状态保存


 

migrate_fd_put_ready(MigrationState *)

(1) 迭代预拷贝Iterative Pre-Copy{defined in the file savevm.c}

调用qemu_savevm_state_iterate(QEMUFile *)

          1)遍历实现设备内存状态保存

               调用se->ops->save_live_iterate(f, se->opaque){等于block_save_iterateram_save_iterate}

(2) 唤醒虚拟机状态

     调用等于qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER)

(3) 调用vm_stop_force_state(RUN_STATE_FINISH_MIGRATE)

     1) 终止虚拟机的运行

          调用vm_stop(RUN_STATE_FINISH_MIGRATE)

(4) 停机拷贝,保存新的数据{defined in the file savevm.c}

调用qemu_savevm_state_complete(QEMUFile *)

  1. 调用se->ops->save_live_complete(f, se->opaque){save_live_complete = block_save_completeraw_save_complete}

  2. 调用vmstate_save(…)完成停机拷贝

(5) 迁移完成,提交信息{defined in migration.c}

     调用migrate_fd_completed(MigrationStateb *)

          1) 迁移完成,释放资源

                    migrate_fd_cleanup(MigrationState *)

          2) 释放资源成功,设置迁移状态:= MIG_STATE_COMPLETED

                    s->state = MIG_STATE_COMPLETED;

          3) 否则,s->state = MIG_STATE_ERROR

(6) 判定迁移状态是否完成,s->state == MIG_STATE_COMPLETED or not。完成,则迁移结束;否则,重新运行源VM

 


目的端:

调用main(...) {defined in vl.c}

(1) 解析命令行参数,初始化等

(2) 判断是否有待迁移虚拟机

     调用qemu_start_incoming_migration(incoming, &errp)

          1) 选择KVM支持的迁移通道

               调用 tcp_start_incoming_migration(p, errp)

                     exec_start_incoming_migration(p)

                     unix_start_incoming_migration(p)

                     fd_start_incoming_migration(p)


tcp_start_incoming_migration(const char *host_port, Error **errp) {defined in migration-tcp.c}

(1) 打开监听

     调用inet_listen(...)

(2) 调用qemu_set_fd_handler2(...)

     注册IO事件,加入到io_handlers链表中,并将IO事件的函数指针初始化,尤其是ioh->fd_read = fd_read = tcp_accept_incoming_migration



tcp_accept_incoming_migration (void *opaque) {defined in migration-tcp.c}

(1) 接受连接

     调用qemu_accept(...)

     调用accept(...)

(2) 接受迁移,打开套接子

     调用qemu_fopen_socket(int ){defined savevm.c}

     通过调用qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close, NULL, NULL, NULL)注册文件套接字QEMUFileSocket

(3) 进程接受迁移

     调用process_incoming_migration(QEMUFile *)

(4) 迁移结束相关操作


process_incoming_migration(QEMUFile *) {defined in migration.c}

(1) 导入状态,从套接字中获取VM状态

     调用qemu_loadvm_state(f)

          se->ops->load_state()

(2) qemu_announce_self ()

(3) bdrv_clear_incoming_migration_all ();

(4) bdrv_invalidate_cache_all ()

(5) 如果成功:vm_start();若失败,停止runstate_set()


qemu_loadvm_state(QEMUFile *f){ defined in savevm.c}

(1) 声明loadvm_handlers链表,并初始化,注册LoadStateEntry *le

(2) 判断是否有迁移,没有则return -EINVAL;否则,继续

(3) 循环,获取(loadsavevm section 标识,

(4) 如果是扇区开始:

     获取(loadsection_idinstance_idversion_id

     LoadStateEntry *le分配空间,并赋值其成员,将当前le插入loadvm_handlers链表

     并加载savevm状态

          调用vmstate_load(f, le->se, le->version_id)

     回到(3)

(5) 如果是扇区结束标识:

     获取(loadsection_id,遍历loadvm_handlers,寻找扇区section_id

     并加载savevm状态

          调用vmstate_load(f, le->se, le->version_id)

     回到(4)

(6) 同步所有cpu,初始化

(7) 注销loadvm_handlers,释放LoadStateEntry *le

(8) 成功返回0,失败返回<0


注意:所有支持虚拟机活迁移的虚拟设备,都需要调用register_savevm_live方法,提供保存状态的SaveVMHandlers*save_live_iterate函数,供活迁移开始时被调用 。正是因为块设备注册了SaveStateEntry对象,才使KVM能够支持image不共享的活迁移。

块设备live迁移初始化:

     调用blk_mig_init(void) 

参考:   http://www.cnblogs.com/armlinux/archive/2011/05/10/2390904.html

            http://blog.youkuaiyun.com/chenglinhust/article/details/8808731

            http://blog.youkuaiyun.com/chenglinhust/article/details/8703131

迭代预拷贝:http://yang19890314.blog.51cto.com/1620466/1163624

### KVM动态迁移的实现方式与原理 KVM 的动态迁移功能允许虚拟机在不停机的情况下从一台物理主机迁移到另一台物理主机,从而保证业务的连续性和高可用性。以下是关于 KVM 动态迁移的关键点: #### 1. **动态迁移的基本原理** 动态迁移的核心在于将运行中的虚拟机状态从源主机传输到目标主机,同时确保虚拟机在整个过程中保持正常运行。这一过程涉及内存、磁盘以及网络状态的同步[^2]。 - **内存迁移**: 虚拟机的内存数据被逐步复制到目标主机。为了减少停机时间,通常采用预拷贝(pre-copy)和迭代拷贝的方式,在最后一次拷贝完成后才暂停虚拟机并完成剩余的数据传输。 - **磁盘共享**: 动态迁移一般假设虚拟机使用的磁盘存储位于共享存储设备上(如NFS或SAN)。这样,目标主机可以直接访问相同的磁盘文件而不需重新上传整个磁盘镜像。 - **网络连接重定向**: 迁移期间,虚拟机的网络流量会被临时转发至新位置,直到迁移完成后再切换IP绑定关系。 #### 2. **实现方式** KVM 使用 libvirt 提供了一套 API 来简化动态迁移的操作流程。具体步骤如下: ```bash virsh migrate --live <domain> qemu+ssh://<destination_host>/system ``` 上述命令实现了以下操作: - `--live` 参数表示执行的是在线迁移而非冷迁移- `<domain>` 是要迁移虚拟机名称。 - 目标主机通过 SSH 协议指定,确保安全通信通道建立。 #### 3. **技术细节** - **性能优化机制**: - 预取策略:提前识别活跃页并将它们优先发送给目标节点。 - 后置写入零页面压缩:对于未初始化或者清零过的内存区域只记录一次即可[^1]。 - **错误处理**: 如果在网络中断或其他异常情况下发生失败,则会尝试回滚或将部分已完成的任务保留以便后续恢复。 #### 4. **注意事项** 成功实施动态迁移依赖几个前提条件: - 源端与目的端之间存在足够的带宽以支撑实时数据交换; - 双方均启用了相同版本的 QEMU/KVM 并配置一致; - 所有参与迁移的服务都处于稳定工作状态之下。 --- ### 示例代码片段展示如何设置 LibVirt 完成基本迁移任务 下面给出一段简单的 Python 脚本用于调用Libvirt库来进行跨节点迁移: ```python import libvirt def live_migrate(domain_name, dest_uri): conn_src = libvirt.open("qemu:///system") # Connect to source host dom = conn_src.lookupByName(domain_name) conn_dest = libvirt.open(dest_uri) # Connect to destination host flags = (libvirt.VIR_MIGRATE_LIVE | libvirt.VIR_MIGRATE_PEER2PEER | libvirt.VIR_MIGRATE_TUNNELLED) dom.migrate(conn_dest, flags, None, None, 0) if __name__ == "__main__": domain_to_move = "test_vm" remote_conn_str = "qemu+ssh://root@192.168.x.y/system" live_migrate(domain_to_move, remote_conn_str) ``` 此脚本定义了一个函数 `live_migrate`, 接受两个参数分别是待移动域的名字还有远程机器URI字符串形式描述的位置信息. ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值