1 架构
Kubevirt 架构¶
kubevirt是Kubernetes的一个扩展插件,它通过k8s来执行虚拟机管理,为k8s提供虚拟化能力。
Kubevirt的技术栈如下:
+---------------------+
| KubeVirt |
~~+---------------------+~~
| Orchestration (K8s) |
+---------------------+
| Scheduling (K8s) |
+---------------------+
| Container Runtime |
~~+---------------------+~~
| Operating System |
+---------------------+
| Virtual(kvm) |
~~+---------------------+~~
| Physical |
+---------------------+
用户通过与Kubevirt提供的虚拟化API进行交互,请求创建和管理虚拟机实例(Virtual Machine Instances,VMI)。API将这些请求较交给k8s集群进行调度。k8s负责处理VMI的调度、网络配置、存储资源,而 Kubevirt则专注于提供虚拟化功能。上图中将KVM放在操作系统之下,我想是为了突出KVM是作为操作系统内核的一部分,而不是在操作系统之上。
Kubevirt 通过以下3个方面向k8s集群添加虚拟化功能: 1. 增加自定义资源类型 Custom Resource Definition (CRD):通过向k8s的API中 注册CRD,引入新的资源类型来管理虚拟机。 2. 创建额外的控制器:针对新的CRD设计并部署额外的控制器,控制器负责在整个集群范围内管理和维护这些自定义资源对象状态,确保它们达到预期状态,比如调度VMI至合适的宿主机上运行。 3. 实现额外的守护进程:部署名为virt-handler的守护进程,在每个节点上与kubelet协同工作,负责配置和启动VMI,使其符合所需状态。 最终,用户可以直接通过k8s的API来创建这些资源类型(如VMI对象),然后由Kubevirt提供的控制器和守护进程来负责具体的虚拟机生命周期管理工作。
下图说明了kubevirt控制器和守护程序如何与 k8s 通信。
简化版架构图如下:

KubeVirt的应用布局¶
- 集群层级
在k8s集群内,Kubevirt部署了一些核组件,包括virt-controller、virt-handler、virt-api、virt-launcher、virt-operator、libvirtd等。 以下是一些核心组件用途: * virt-api
virt-api是KubeVirt的API服务器,它提供了与KubeVirt相关的RESTful API端点,允许用户和其他组件与KubeVirt进行交互和通信。通过virt-api,用户可以管理虚拟机实例、虚拟机模板、存储和网络等资源。
- virt-controlller
virt-controller是KubeVirt的控制器,负责监视和管理KubeVirt中的虚拟机资源。它确保虚拟机的状态与定义的保持一致,并根据需要执行调谐操作,以满足预期状态。
- virt-handler
virt-handler负责在Kubernetes集群中创建和管理虚拟机实例(VMIs)。它与Kubernetes的调度器协作,确保VMIs被正确地调度到集群中的节点上,并负责监控和维护这些VMIs的运行状态。 Virt-handler会以Daemonset形式部署在每个节点上,负责监控节点上每个虚拟机实例状态变化,一旦检测到状态变化,会进行响应并确保相应操作能达到所需状态。
- virt-launcher
负责创建和管理虚拟机实例的生命周期,包括启动、停止、监控和维护。每个virt-lanuncher pod对应着一个VMI,也对应着一个libvirtd, kubelet只是负责virt-lanuncher pod运行状态,不会去关心VMI创建情况。 virt-lanuncher pod生命周期结束,virt-lanuncher也会通知VMI终止。 virt-lanuncher通过libvirtd去管理VM的生命周期,一个libvirtd只管理一个VM,而不像openstack那样,一个libvirtd管理多个VM。
- virt-operator
Kubevirt的运维组件,基于k8s的Operator模式,负责管理和操作KubeVirt的自定义资源(CRs)。virt-operator监视k8s集群中的CRs,包括但不限于 VirtualMachine(VM)、VirtualMachineInstance(VMI)、VirtualMachineInstanceReplicaSet(VMIRS)等。 virt-operator 监视 k8s 集群中与 KubeVirt 相关的自定义资源对象,并根据这些资源的状态和定义执行相应的操作, 比如安装、更新或卸载 KubeVirt 组件,以及确保 Kubevirt 所管理的虚拟机实例处于预期状态。 当用户创建、更新或删除了某个自定义资源时,virt-operator 将自动响应并采取相应措施来保持集群中 KubeVirt 环境的一致性和稳定性。
- virtctl
KubeVirt 提供的一个命令行工具,它允许用户与 k8s 集群中的虚拟化资源进行交互。通过 virtctl,您可以创建、删除、查看和管理 VirtualMachine、VirtualMachineInstance 以及 VirtualMachineInstanceReplicaSet 等资源。例如,您可以使用 virtctl 来启动或停止虚拟机实例、连接到虚拟机控制台、迁移虚拟机等操作。
- VirtualMachine (VM)
VM 是 KubeVirt 中提供的一种有状态的自定义资源对象,用于定义虚拟机的规范和配置,类似于虚拟机模板,描述了虚拟机的期望状态和属性。用户可以创建、更新和删除VM来定义虚拟机的规范和配置。 一个 VM 可以关联多个 VMI 实例,例如当希望在需要的时候能够恢复到某个已知的虚拟机状态时,可以通过 VM 这个高级抽象来实现。
- VirtualMachineInstance (VMI)
VMI 是Kubevirt中的自定义资源,代表了一个运行时的虚拟机实例。每个 VMI 对象描述了虚拟机实例的配置信息,包括镜像、内存大小、CPU核心数、存储大小、网络配置等。 当一个 VMI 被创建并调度至集群中的节点后,KubeVirt 将根据该配置启动实际的虚拟机,用户可以创建、更新和删除VMI。
- VirtualMachineInstanceReplicaSet (VMIRS)
VMIRS 类似于 k8s 中的 ReplicaSet 或 StatefulSet,用于管理一组相同配置的虚拟机实例集合。当创建一个 VMIRS 时,它将确保集群中始终保持指定数量且具有相同配置的 VMI 实例处于运行状态。 这种机制非常适合需要高可用性、负载均衡或者弹性伸缩的场景,可以根据需求自动调整 VMI 的副本数量。
原生工作负载(Native Workloads)¶
KubeVirt 是部署在 k8s 集群之上的,这意味着您可以在使用 KubeVirt 管理虚拟机实例 (VMIs) 的同时,继续运行基于 Kubernetes 的原生工作负载(如 Pods 和 Deployments)。 如果用户已经能够在集群中运行原生工作负载,并且已安装了 KubeVirt,则理论上他们也应该能够运行基于虚拟机的工作负载。 例如,应用程序操作员在使用 KubeVirt 功能管理虚拟机时,不应需要比管理普通 Pod 更多的权限。 安全性方面,安装和使用 KubeVirt 不应授予用户对原生工作负载任何额外的、他们原本不具备的权限。 比如,一个非特权的应用程序操作不应通过使用 KubeVirt 功能而获得访问特权 Pod 的权限。
设计决策原则(The Razor)¶
"The Razor" 在 KubeVirt 的上下文中是指一种设计决策原则,它源自 "Occam's Razor"(奥卡姆剃刀原理), 这是一个哲学上的原则,通常在科学和工程领域中被用来指导简化问题的解决方案。 简单来说,奥卡姆剃刀原理主张“如无必要,勿增实体”(entities should not be multiplied unnecessarily)。
KubeVirt 团队提出的 KubeVirt Razor 原则可以理解为:“如果某个功能对 Kubernetes 的 Pods 有用,我们不应该只为虚拟机 (VMs) 实现它。 ”这意味着他们在开发 KubeVirt 功能时倾向于采用通用、可复用的设计方法,尽量避免仅为 VMs 创建孤立的解决方案,而是要确保这些功能同样适用于基于容器的原生工作负载(Pods)。 通过遵循这个原则,KubeVirt 能够更好地融入 Kubernetes 生态系统,并保持其组件与整体架构的一致性和兼容性。
虽然 KubeVirt 团队非常重视虚拟机并努力使其在 Kubernetes 中易于使用,但他们更看重良好的设计和模块化、可重用的组件。 在开发过程中,团队经常面临这样的两难境地:应该专门为虚拟机优化解决方案,还是选择一条更长的路径,将解决方案也引入到基于 Pod 的工作负载中? 为了解决这种困境,团队提出了“KubeVirt Razor”原则:“如果某个功能对 Pods 有用,我们不应该只为 VMs 实现它。” 举例来说,在讨论如何将虚拟机连接到外部网络资源时,最直接的方法似乎是添加 KubeVirt 特定的代码,将 VM 连接到主机桥接器上。 但团队选择了较长的路径,即与 Multus 和 CNI CNI 集成并改进它们,以确保这些网络解决方案不仅适用于虚拟机,还同样支持基于 Pod 的工作负载。
VirtualMachine¶
VirtualMachine 是 Kubernetes 集群中提供给 VirtualMachineInstance(虚拟机实例)的额外管理功能。具体包括: API 稳定性:确保 API 的稳定性,即在 API 层面保持一致的行为和兼容性。 控制器级别的启动/停止/重启能力:在控制器级别提供了对 VirtualMachineInstance 的启动、停止和重启控制。 离线配置变更及应用:支持对 VirtualMachine 进行离线配置更改,并且在重新创建 VirtualMachineInstance 时将更改传播到新实例上。 确保运行状态:如果 VirtualMachine 应该处于运行状态,则会确保对应的 VirtualMachineInstance 实际上正在运行。 一对一关系:VirtualMachine 主要关注的是控制器实例与虚拟机实例之间的一对一对应关系。 从这个角度来看,它在很多方面类似于一个具有 spec.replicas 设置为 1 的 StatefulSet,这意味着每个 StatefulSet 控制器实例只管理一个持久化的 Pod, 同样地,每个 VirtualMachine 控制器实例也只负责管理一个具有持久化特性的 VirtualMachineInstance。
如何使用 VirtualMachine¶
当 VirtualMachine 中的 spec.running 字段设置为 true 时,VirtualMachine 将确保集群中存在一个名称相同的 VirtualMachineInstance 对象。 同样,如果将 spec.running 设置为 false,则 VirtualMachine 将确保从集群中移除对应的 VirtualMachineInstance。 此外,还有一个名为 spec.runStrategy 的字段,可以用来控制关联的 VirtualMachineInstance 对象的状态。 为了避免出现混淆和矛盾状态,这些字段是互斥使用的,即不能同时指定两种策略。有关 spec.runStrategy 与 spec.running 之间的详细区别和解释,请参阅 【运行策略】 文档。
启动或停止 VirtualMachine¶
在创建 VirtualMachine之后,可以通过如下命令来启动或停止 VirtualMachine
控制器状态¶
当 VirtualMachineInstance 创建后,其状态将通过 VirtualMachine 中的
status.created和status.ready字段进行跟踪。 若集群中存在一个 VirtualMachineInstance,则status.created字段值为true;如果该 VirtualMachineInstance 已经准备就绪,则status.ready字段也将设为true。 如果某个 VirtualMachineInstance 达到了最终状态(例如终止、失败等),但其对应的 VirtualMachine 的 spec.running 字段仍为 true, 则 VirtualMachine 控制器会将status.ready设置为false,并重新创建 VirtualMachineInstance。 另外,status.printableStatus字段提供了关于 VirtualMachine 状态的高级概要信息。这一信息在使用命令行工具列出 VirtualMachines 时也会显示出来, 方便用户快速了解虚拟机的整体运行状态。当前支持的状态列表及其含义。请注意,在将来的版本中可能会添加状态删除,因此如果被自动化程序使用,则应谨慎使用。 - 已停止(Stopped):虚拟机当前处于停止状态,并且预期不会启动。 - 配置中(Provisioning):与虚拟机相关的集群资源(例如 DataVolumes)正在进行配置和准备阶段。 - 启动中(Starting):虚拟机正在为运行做准备。 - 运行中(Running):虚拟机正在运行。 - 暂停中(Paused):虚拟机已被暂停。 - 迁移中(Migrating):虚拟机正处于迁移到另一主机的过程之中。 - 停止中(Stopping):虚拟机正在被停止的过程中。 - 终止中(Terminating):虚拟机以及与其关联的资源(如 VirtualMachineInstance、DataVolumes 等)正处于删除过程中。 - 未知(Unknown):无法获取到虚拟机的状态,这通常是因为与运行该虚拟机的主机通信出现错误导致的。
VM重启¶
可以通过删除 VirtualMachineInstance 来触发VM重启。这也将传播 VirtualMachine 中模板中的配置更改:
## 通过删除vmi来重启VM # kubectl delete vmi vm01 ## 通过virtctl命令重启VM # virtctl restart vm01 ## 这将对 VirtualMachineInstance 执行正常重启,并将 VirtualMachineInstance 重新调度到新的 virt-launcher Pod 上 ## 强制重启 # virtctl restart vm01 --force --grace-period=0 ## 这将尝试执行正常重启,并且还会删除 VirtualMachineInstance 的 virt-launcher Pod,并将 GracePeriodSeconds 设置为命令中传递的秒数。目前仅支持设置 grace-period=0 ## 强制重启可能会导致数据损坏,应只在VM出现内核崩溃或VM对正常重启无响应的情况下使用强制重启。
VM暂停¶
## 暂停虚拟机,类似被冻结或被挂起的一个进程。当虚拟机被暂停时,它的状态会被保存到内存中,并且虚拟机与外界交互(如网络、磁盘 I/O)都会停止。CPU资源将不会分配给暂停的虚拟机,但其内存状态会被保持,以便后续可以快速恢复运行。
# virtctl pause vm vm01
## 取消暂停虚拟机,就是重新运行已被暂停的虚拟机。这意味着虚拟机会从先前暂停状态继续运行,就好像从未中断过一样。内存中的内容会立即恢复为暂停前的状态,CPU和I/O活动也会立刻重新开始。
# virtctl unpause vm vm01
VM可以作为服务被公开访问。一旦 VirtualMachineInstance 启动,实际服务将可用,无需额外的交互。¶
例如,在创建 VirtualMachine 之后,但在启动之前,使用 'virtctl' 将 SSH 端口 (22) 公开为
详细可参考[service对象.md]ClusterIP(默认)类型服务:
