KubeVirt:使用CRD扩展捕鱼大亨网络版以实现虚拟化工作负载

作者:大卫·沃瑟(红帽)

什么是KubeVirt?

KubeVirt 是一个捕鱼大亨网络版插件,可为用户提供与容器工作负载并排调度传统虚拟机工作负载的功能。通过使用 自定义资源定义 (CRD)和其他捕鱼大亨网络版功能,KubeVirt无缝扩展了现有的捕鱼大亨网络版群集,以提供一组可用于管理虚拟机的虚拟化API。

为什么在聚合的API服务器上使用CRD?

早在2017年中,从事KubeVirt工作的那些人处于十字路口。我们必须决定是否使用聚合的API服务器扩展捕鱼大亨网络版或使用新的自定义资源定义(CRD)功能。

当时,CRD缺少提供功能集所需的许多功能。创建自己的聚合API服务器的能力为我们提供了所需的所有灵活性,但是它有一个主要缺陷。 聚合的API服务器大大增加了安装和操作KubeVirt的复杂性。

对我们而言,问题的症结在于聚合的API服务器需要访问etcd才能实现对象持久性。这意味着集群管理员必须要么接受KubeVirt需要进行单独的etcd部署(这会增加复杂性),要么为KubeVirt提供对捕鱼大亨网络版 etcd存储的共享访问权限,这会带来风险。

我们对此权衡不满意。我们的目标不仅是扩展捕鱼大亨网络版来运行虚拟化工作负载,还在于以最无缝,最轻松的方式做到这一点。我们认为聚合API服务器涉及的增加的复杂性牺牲了与安装和操作KubeVirt有关的部分用户体验。

最终,我们选择了CRD,并相信捕鱼大亨网络版生态系统将与我们一起成长,以满足我们用例的需求。 我们的赌注位置不错。目前,已有解决方案或正在讨论中的解决方案,可以解决我们在2017年评估CRD与聚合API服务器时遇到的每个功能差距。

使用CRD构建分层的“类似捕鱼大亨网络版” API

我们设计KubeVirt的API时要遵循用户在捕鱼大亨网络版核心API中已经熟悉的相同模式。

例如,在捕鱼大亨网络版中,用户创建用来执行工作的最低级别的单元是Pod。是的,Pod确实有多个容器,但从逻辑上讲Pod是堆栈底部的单元。吊舱代表着致命的工作量。安排好Pod,最后终止Pod的工作量,这就是Pod生命周期的终点。

工作负载控制器(例如ReplicaSet和StatefulSet)位于Pod抽象的顶部,以帮助管理横向扩展和有状态的应用程序。从那里,我们有了一个称为Deployment的更高级别的控制器,它位于ReplicaSets之上,可帮助管理滚动更新之类的事情。

在KubeVirt中,分层控制器的概念是我们设计的核心。 KubeVirt VirtualMachineInstance(VMI)对象是KubeVirt堆栈最底部的最低级别单位。在概念上与Pod相似,VMI代表单个致命虚拟化工作负载,该工作负载执行一次直到完成(关闭电源)。

在VMI之上,我们有一个称为VirtualMachine(VM)的工作负载控制器。 VM控制器是我们真正开始看到用户管理虚拟化工作负载与容器化工作负载之间差异的地方。在现有捕鱼大亨网络版功能的背景下,描述VM控制器行为的最佳方法是将其与大小为1的StatefulSet进行比较。这是因为VM控制器表示单个有状态(永生)虚拟机,该虚拟机能够在节点故障和其基础VMI的多次重新启动之间保持状态。在AWS,GCE,OpenStack或任何其他类似的IaaS云平台中管理虚拟机的用户所熟悉的行为方式。用户可以关闭虚拟机,然后选择稍后再次启动完全相同的虚拟机。

除了虚拟机,我们还有一个VirtualMachineInstanceReplicaSet(VMIRS)工作负载控制器,用于管理相同VMI对象的横向扩展。该控制器的行为几乎与捕鱼大亨网络版 ReplicSet控制器相同。主要区别在于VMIRS管理VMI对象,而ReplicaSet管理Pod。如果我们想出一种方法来 使用捕鱼大亨网络版 ReplicaSet控制器扩展CRD?

当KubeVirt安装清单发布到集群时,这些KubeVirt对象(VMI,VM,VMIRS)中的每一个都向捕鱼大亨网络版注册为CRD。通过在捕鱼大亨网络版中将我们的API注册为CRD,管理捕鱼大亨网络版集群所涉及的所有工具(如kubectl)都可以访问KubeVirt API,就像它们是本地捕鱼大亨网络版对象一样。

用于API验证的动态Webhooks

捕鱼大亨网络版 API服务器的职责之一是在允许对象保留到etcd中之前拦截并验证请求。例如,如果有人尝试使用格式错误的Pod规范创建Pod,则捕鱼大亨网络版 API服务器会立即捕获该错误并拒绝POST请求。这一切都发生在对象被持久化到etcd中之前,以防止格式错误的Pod规范进入集群。

此验证发生在称为准入控制的过程中。直到最近,在不更改代码和编译/部署全新捕鱼大亨网络版 API服务器的情况下,才能扩展默认的捕鱼大亨网络版准入控制器。这意味着,如果我们想在KubeVirt的CRD对象发布到集群时对其进行访问控制,就必须构建自己的捕鱼大亨网络版 API服务器版本,并说服用户使用它。对于我们来说,这不是可行的解决方案。

使用新的 动态入场控制 该功能首次出现在捕鱼大亨网络版 1.9中,现在,我们有了一条通过使用以下命令在KubeVirt API上执行自定义验证的路径 ValidatingAdmissionWebhook。此功能允许KubeVirt在安装KubeVirt时向捕鱼大亨网络版动态注册HTTPS Webhook。注册自定义Webhook之后,所有与KubeVirt API对象有关的请求都将从捕鱼大亨网络版 API服务器转发到我们的HTTPS端点进行验证。如果我们的端点由于某种原因拒绝了请求,则该对象将不会持久保存到etcd中,并且客户端会收到我们的响应,概述拒绝的原因。

例如,如果某人发布了格式错误的VirtualMachine对象,他们将收到一条错误消息,指出问题所在。

$ kubectl create -f my-vm.yaml 
Error from server: error when creating "my-vm.yaml": admission webhook "virtualmachine-validator.kubevirt.io" denied the request: spec.template.spec.domain.devices.disks[0].volumeName 'registryvolume' not found.

在上面的示例输出中,该错误响应直接来自KubeVirt的准入控制Webhook。

CRD OpenAPIv3验证

除了验证Webhook,KubeVirt还使用该功能来提供 OpenAPIv3验证架构 向群集注册CRD时。尽管OpenAPIv3模式不允许我们表达验证Webhook提供的一些更高级的验证检查,但它确实提供了执行简单验证检查的功能,其中涉及诸如必填字段,最大/最小值长度以及验证值是否格式化等内容以与正则表达式字符串匹配的方式。

动态Webhooks的“ PodPreset Like”行为

捕鱼大亨网络版动态准入控制功能不仅限于验证逻辑,还为诸如KubeVirt之类的应用程序提供了在进入集群时拦截和变异请求的功能。这是通过使用 MutatingAdmissionWebhook 目的。在KubeVirt中,我们希望使用变异的Webhook来支持我们的VirtualMachinePreset(VMPreset)功能。

VMPreset的行为与PodPreset相似。就像PodPreset允许用户定义在创建时应自动注入到pod中的值一样,VMPreset允许用户定义在创建时应注入到VM中的值。通过使用变异的Webhook,KubeVirt可以拦截创建VM的请求,将VMPresets应用于VM规范,然后验证生成的VM对象。这一切都发生在将VM对象持久保存到etcd中之前,后者允许KubeVirt在发出请求时立即将任何冲突通知用户。

CRD的子资源

When comparing the use of CRDs to an aggregated API server, one of the features CRDs lack is the ability to support subresources. Subresources are used to provide additional resource functionality. For example, the pod/logs and pod/exec subresource endpoints are used behind the scenes to provide the kubectl logs and kubectl exec command functionality.

Just like 捕鱼大亨网络版 uses the pod/exec subresource to provide access to a pod’s environment, in KubeVirt we want subresources to provide serial-console, VNC, and SPICE access to a virtual machine. By adding virtual machine guest access through subresources, we can leverage RBAC to provide access control for these features.

因此,考虑到KubeVirt团队决定使用CRD而不是聚合API服务器来提供自定义资源支持,那么当CRD功能明确性不支持子资源时,我们如何拥有CRD的子资源?

通过实现仅用于服务子资源请求的无状态聚合API服务器,我们为此限制创建了一种解决方法。没有状态,我们不必担心我们先前发现的有关访问etcd的任何问题。这意味着KubeVirt API实际上是通过两个CRD(用于资源)和聚合API服务器(用于无状态子资源)的组合来支持的。

对于我们来说,这不是一个完美的解决方案。聚合API服务器和CRD都要求我们向捕鱼大亨网络版注册API GroupName。这个API GroupName字段实质上是一种API REST路径的命名空间,可以防止其他第三方应用程序之间的API命名冲突。由于CRD和聚合的API服务器不能共享相同的GroupName,因此我们必须注册两个单独的GroupName。一个由我们的CRD使用,另一个由聚合的API服务器用于子资源请求。

在我们的API中有两个GroupName有点不方便,因为这意味着服务KubeVirt子资源请求的端点的REST路径的基本路径与资源略有不同。

例如,创建VMI对象的端点如下。

/apis/kubevirt.io/v1alpha2/namespaces/my-namespace/virtualmachineinstances/my-vm

但是,用于访问图形VNC的子资源端点看起来像这样。

/apis/subresources.kubevirt.io/v1alpha2/namespaces/my-namespace/virtualmachineinstances/my-vm/vnc

请注意,第一个请求使用 kubevirt.io 第二个请求使用 subresource.kubevirt.io。我们不喜欢这样,但这是我们设法将CRD与用于子资源的无状态聚合API服务器结合在一起的方式。

One thing worth noting is that in 捕鱼大亨网络版 1.10 a very basic form of CRD subresource support was added in the form of the /status and /scale subresources. This support does not help us deliver the virtualization features we want subresources for. However, there have been discussions about exposing custom CRD subresources as webhooks in a future 捕鱼大亨网络版 version. If this functionality lands, we will gladly transition away from our stateless aggregated API server workaround to use a subresource webhook feature.

CRD终结者

A CRD定稿器 是一项功能,可让我们提供一个预删除挂钩,以便在允许从持久性存储中删除CRD对象之前执行操作。在KubeVirt中,我们使用终结器来确保虚拟机已完全终止,然后才允许从etcd中删除相应的VMI对象。

CRD的API版本控制

The 捕鱼大亨网络版 core APIs have the ability to support multiple versions for a single object type and perform conversions between those versions. This gives the 捕鱼大亨网络版 core APIs a path for advancing the v1alpha1 version of an object to a v1beta1 version and so forth.

Prior to 捕鱼大亨网络版 1.11, CRDs did not have support for multiple versions. This meant when we wanted to progress a CRD from kubevirt.io/v1alpha1 to kubevirt.io/v1beta1, the only path available to was to backup our CRD objects, delete the registered CRD from 捕鱼大亨网络版, register a new CRD with the updated version, convert the backed up CRD objects to the new version, and finally post the migrated CRD objects back to the cluster.

对于我们来说,该策略并不是一个可行的选择。

幸亏最近有一些 努力纠正捕鱼大亨网络版中的此问题,最新的捕鱼大亨网络版 v1.11现在支持 具有多个版本的CRD。但是请注意,此初始多版本支持有限。尽管CRD现在可以具有多个版本,但是该功能当前不包含用于在版本之间执行转换的路径。在KubeVirt中,缺乏转换使我们难以在升级版本时演化API。幸运的是,目前正在支持版本之间的转换,我们期待该功能在将来的捕鱼大亨网络版发行版中得到利用。