本文翻译自 Libvirt 官方文档 VM Lifecycle。
虚拟机生命周期
本文介绍虚拟机生命周期的基本要素,目的在于提供创建、运行、停止、迁移和删除虚拟机的基本信息。
术语
知道文档中使用的术语及命令和语法的意义一直很重要。如果你不熟悉 Libvirt 的基本概念,如节点(Node)、域(Domain),以及为了获取 Libvirt 项目目标、覆盖范围的概况,请参考这篇文章。
概念
**Guest Domain 用 XML 来描述 **
在 Libvirt 中,XML 文件被用来保存所有东西的配置信息,包括 Domain、网络、存储和其他元素。XML 使得用户可以使用舒适的编辑器及易于集成其他技术和工具。
例如,Domain 中的设备(Device)通过赋予 XML 元素或子元素属性来表示。一个 XML 片段如下:
1 | <domain type='qemu'> |
Libvirt 使用了 XPath 技术来选取 XML 文档中的节点(node)。
短暂性 Guest Domain 和持久性 Guest Domain
Libvirt 区分两种不同类型的 Domain:短暂性的(Transient)和持久性的(Persistent)。
- 短暂性 Domain 只存在于 Domain 被关闭或者宿主机重启之前
- 持久性 Domain 会一直存在(直到被删除)
不管是什么类型,一旦一个 Domain 创建起来,它的状态就能够保存进一个文件和无限次地恢复(restore),只要原文件还存在。所以即使是一个短暂性 Domain 也能够一遍又一遍地恢复。
创建短暂性 Domain 和创建持久性 Domain 稍微有点不一样。持久性 Domain 需要在启动前定义(define);短暂性 Domain 只创建和启动一次。处理这两种类型的 Domain 的命令也不一样。
虽然短暂性 Domain 可以随时(on the fly)创建和销毁,但它所需的组件(如存储、网络、设备等)都需提前准备好。
Guest Domain 状态
Domain 可以有以下几个状态:
- Undefined - 这是初始状态(baseline state)。Libvirt 不知道处于这个状态的 Domain 的所有信息,因为 Domain 还没有定义或者创建。
- Defined 或 Stopped - Domain 已经定义,但还没有运行。这个状态也称为 stopped。只有持久性 Domain 才可以处于这个状态。当一个短暂性 Domain 停止或者关闭的时候,它就不存在了。
- Running - Domain 已经创建和启动为短暂性或持久性 Domain。这两种 Domain 处于这个状态都是已经在宿主机的 Hypervisor 执行起来。
- Paused - Domain 在 Hypervisor 上的运行被挂起。它的状态会临时地存储起来直到恢复运行。它本身并不知道自己是否被暂停。如果你熟悉操作系统中的进程,你会发现这两者很相似。
- Saved - 类似于 paused 状态,但(不同的是) Domain 的状态会被存储到持久性存储上。同样,处于这个状态的 Domain 可以被恢复,而且不会察觉到时间流逝。
下图展示了 Domain 状态之间的转化。长方形表示不同的状态,箭头表示将一个状态转化为另一个状态的命令。
从上图中,我们可以看到,shutdown 命令使得 Domain 从 running 状态转化为 defined 或者 undefined 状态。在这种情形下,短暂性 Domain 会变为 undefined 状态(销毁),持久性 Domain 会变为 defined 状态。
快照
快照是一个虚拟机操作系统及其所有应用在某一个时刻的视图(View)。在虚拟机化领域,能够创建虚拟机的可恢复快照是一个很基本的特性。快照允许用户及时保存虚拟机在某时刻的状态,以及回滚到那个状态。基本的用例包括生成快照、安装新的应用、更新或升级(发现它们很糟糕或引起问题),然后回滚到前一个时间点。
显而易见,快照生成之后的任何变化都不会包含在该快照之中。快照不会持续更新,它(只)代表了某一个时刻虚拟机的状态。
迁移
一个运行中的 Domain 或者虚拟机可以迁移到另一个宿主机,只要虚拟机的存储在宿主机之间共享以及目的宿主机可以支持虚拟机的 CPU 模式(CPU Model)。根据类型和应用,虚拟机的迁移不会引起任何服务的中断。
Libvirt 支持不同的迁移类型:
- Standard(标准模式) - Domain 被挂起,将其资源传送到目的宿主机。一旦传送完成,虚拟机从目的宿主机恢复运行。挂起的时间直接跟 Domain 的内存大小成正比。
- Peer-to-peer(对等模式) - 当原宿主机和目的宿主机能够直接通信时会使用该模式。
- Tunnelled(隧道模式) - 在原宿主机和目的宿主机之间建立一条隧道,如 SSH 隧道。所有原宿主机和目的宿主机或物理主机之间的网络通信都会通过该隧道。
- Live vs non-live(实时 VS 非实时模式) - 当在实时模式时,Domain 不会暂停,其上的所有服务也会继续运行。一开始,目的宿主机上的 Domain 或虚拟机处于关闭状态,而且在通过网络传输 Domain 状态的时候,在目的宿主机上的 Domain 实际是不可见的。实时迁移对应用负载很敏感。当实时迁移一个 Domain 的时候,它所分配的内存被发送到目的宿主机,同时在原宿主机监控其变化。在原宿主机上的 Domain 会保持 active 状态直到其在两个宿主机上的内存一致。在此时,目的宿主机上的 Domain 变为 active 状态,原宿主机上的 Domain 变为 passive 状态或不可见状态。
- Direct(直接模式) - Libvirt 利用 Hypervisor 触发迁移,而且整个迁移过程都在 Hypervisor 的监控之下。Hypervisor 通常有互相之间直接通信的特性(如原宿主机上的 Xen 可以跟目的宿主机上的 Xen 直接通信,而不需要 Libvirt 的干预)。
迁移的要求:
- 可以在相同路径和位置访问共享存储,如 iSCSI、NFS 等。
- 双方物理主机使用同一版本的 Hypervisor。
- 相同的网络配置。
- 相同或更好的 CPU。CPU 必须来自同一个厂商,而且目的宿主机上的 CPU flag 必须是原宿主机 CPU flag 的超集。
成功迁移的检查清单可以在这里找到。
删除 Domain 时的数据安全性
一些保存了敏感信息的应用都应该安全地处理。就像文件系统中的任一文件,当虚拟机被删除时,只是把文件系统的指针删除了。原先被占用的区块(block)还是继续被占用着,它们只是被文件系统标记为空。实际上,这取决于你的文件系统。
希望的是,如果一个应用处理的是敏感(heavy)数据,这个机器物理上就被安全防护,网络同样也被防护起来。
安全一直是一个要考虑的重要因素。
任务
创建 Domain
在运行 Domain 之前需要先创建一个。有很多方法可以创建 Domain。这篇文章介绍了通过 Virtual Machine Manager GUI 来创建 Domain。第二种方法是使用 virt-install 命令行工具:
1 | # virt-install \ |
这个命令创建了一个基于 KVM 的 Domain,名为 MyNewVM,RAM 为 512MB,磁盘空间为 8GB。更多信息请阅读 virt-install 手册。
最后一种方法是创建一个 Domain XML 定义文件和存储卷(volume),以及运行 virsh 命令:vol-create 和 define。
存储卷会被加入一个存储池(pool)中。默认地,存在一个叫为 default 的存储池。这是一个目录类型的存储池,意味着所有的卷都以文件的方式存储在一个目录中。如果你不是很清楚 Libvirt 的存储管理,请阅读这篇文章。
XML 定义文件(new_volume.xml)例子:
1 | <volume> |
这定义了一个容量为 10GB 的卷。在 default 存储池中创建卷:
1 | # virsh vol-create default new_volume.xml |
另一个 XML 定义文件(MyNewVM.xml)例子:
1 | <domain type='kvm'> |
定义一个新的持久性 Domain:
1 | # virsh define MyNewVM.xml |
Domain XML 文件格式有很多有用的可选元素。所以,请阅读这篇包括样例和大多共性场景的完整 Domain XML 格式参考文章。
编辑 Domain
任何 Domain 都可以用你最喜欢的编辑器编辑。你需要的只是设定 $VISUAL 或者 $EDITOR 环境变量,并且运行:
1 | # virsh edit <domain> |
如果这些变量都没有设置,那默认会使用 vi 编辑器。关闭编辑器后,Libvirt 会自动检查内容变化并应用(apply)它们。另外,在 Virtual Machine Manager 中编辑 Domain 也是可以的。
启动 Domain
一旦一个 Domain 创建好了,我们就可以开始运行它了。这可以通过 Virtual Machine Manager 或者执行 virsh start
1 | # virsh start MyNewVM |
这个命令可以执行干净启动(clean boot up)或者恢复 Domain 之前保存的状态。请参考 managedsave virsh 命令详情。需要注意的是,当 Domain 中任一组件(如网络)都没有起来的是,它是启动不起来的。
就如前面提到的,短暂性 Domain 可以直接运行而无需定义(define)动作:
1 | # virsh create /path/to/MyNewVM.xml |
关闭或重启 Domain
关闭 Domain:
1 | # virsh shutdown <domain> |
重启一个持久性 Domain:
1 | # virsh reboot <domain> |
重启一个短暂性 Domain 是不可能的,因为它在关机(shutdown)后已经不在了(undefined)。
粗野关机(inelegant shutdown),也称为强制关机(hard-stop):
1 | # virsh destroy <domain> |
这等同于拔掉电源。
暂停 Domain
暂停 Domain:
1 | # virsh suspend <domain> |
或者在 Virtual Machine Manager 中主工具栏点击 Pause 按钮。当虚拟机处于挂起状态(Suspended State),它占用系统 RAM 资源而不是处理器资源;磁盘和网络 I/O 也不会发生。
继续运行 Domain
任何一个暂停或挂起的 Domain 都可以恢复运行:
1 | # virsh resume <domain> |
或者通过反点击(unclick) Virtual Machine Manager 的 Pause 按钮。
生成快照
生成快照:
1 | # virsh snapshot-create <domain> |
列出快照
列出 Domain 快照:
1 | # virsh snapshot-list <domain> |
输出结果可能看起来像这样子:
1 | Name Creation Time State |
我们可以看到,一个快照是在当地时间 17:39:17 创建的,名为 1295973577,代表着 Unix 时间;另一个快照是在 19:07:17 创建,名为 1295978837。
从快照恢复 Domain
从一个快照恢复 Domain:
1 | # virsh snapshot-restore <domain> <snapshotname> |
这就恢复 Domain 到快照保存的状态。注意,Domain 的任何变化都会被销毁。
移除 Domain 的快照
移除 Domain 的快照:
1 | # virsh snapshot-delete <domain> <snapshotname> |
迁移
Libvirt 提供了虚拟机迁移支持。这意味着你可以通过网络来迁移虚拟机。迁移主要有两种模式:
- Plain migration(简单迁移):原宿主机新建一条通向目的宿主机的未加密 TCP 连接以传输数据。除非手工指定了端口,Libvirt 会在 49152-49215 中选择一个。我们需要在目的宿主机的防火墙上放行该端口。
- Tunneled migration(隧道迁移):原宿主机 libvirtd 进程新建一条通向目的宿主机 libvirtd 的直连通道以传输数据。这允许我们对数据流进行加密。这个模式不需要额外的防火墙配置,只需要 qemu 0.12.0 或更新,以及 libvirt 0.7.2 或更新。
一个成功的迁移有很多事情需要完成。对于虚拟机,存储配置需要匹配得上、待迁移 Domain 使用的存储卷都要放在同一路径下。完整的检查清单请参考该文。我们也推荐你使用安全迁移(secure migration)。
一旦迁移前检查完成,我们就可以利用 virsh 来迁移虚拟机了:
1 | # virsh migrate <domain> <remote host URI> --migrateuri tcp://<remote host>:<port> |
删除 Domain
我们可以删除一个 inactive Domain:
1 | # virsh undefine <domain> |
如前,我们也可以通过 Virtual Machine Manager 来删除快照,见这篇文章。
擦除 Domain 存储数据
Domain 使用的存储卷可能会包含保密信息,所以在移除它的时候有必要擦除其上的数据:
1 | # virsh vol-wipe <volume> |
这截断(truncate)或扩展(extend)该卷为其原始的大小。实际上是用 0 来填充文件。这保证之前存储在该卷上的数据不再可读。
最后,你就可以移除该卷了:
1 | # virsh vol-delete <volume> |