云原生虚拟化:基于 Kubevirt 构建边缘计算实例

随着 Kubernetes 的普及,越来越多的业务开始运行在容器上,但是仍有部分业务形态更适合运行在虚拟机,如何同时管控虚拟机和容器逐渐成为了云原生时代的主流需求, Kubevirt 给出了完美的解决方案。

  1. Kubevirt 介绍
  2. 虚拟机管理
  3. 总结

在云原生时代,越来越多的业务开始逐步迁移到容器上来,容器也成为了一种不可或缺的资源发布和管理形式,容器的轻量性优势在边缘计算的场景下更加明显,因此,在边缘构建云平台必须考虑兼容容器的运行形态。

Kubernetes 目前是容器编排和调度最通用流行的平台,同时越来越多的云厂商选择使用 Kubernetes 来同时管理容器和虚拟机。在 Kubernetes 之上构建管理虚拟机的平台,业界也出现了不少优秀的项目,例如 Kubevirt、Virtlet 等,其中 Kubevirt 以其架构设计和功能优势成为解决虚机与容器兼容问题的最佳方案之一。本文将以 Kubevirt 为基础,详细分享如何基于 Kubevirt 构建边缘计算实例。

01 Kubevirt 介绍

Kubevirt 是什么

Kubevirt 是 Red Hat 开源的以容器方式运行虚拟机的项目,基于 Kubernetes 运行,通过使用自定义资源(CRD)和其它 Kubernetes 功能来无缝扩展现有的集群,以提供一组可用于管理虚拟机的虚拟化的 API。

整体架构

云原生虚拟化:基于 Kubevirt 构建边缘计算实例_第1张图片

上图描述了 Kubevirt 的整体架构,其中包含了主要的四个关键组件:

  • virt-api:

    • 为 Kubevirt 提供 API 服务能力,比如许多自定义的 API 请求,如开机、关机、重启等操作,通过 APIService 作为 Kubernetes Apiserver 的插件,业务可以通过 Kubernetes Apiserver 直接请求到 virt-api;
  • virt-controller:

    • Kubevirt 的控制器,功能类似于 Kubernetes 的 controller-manager,管理和监控 VMI 对象及其关联的 Pod,对其状态进行更新;
  • virt-handler:

    • 以 Daemonset 形式部署,功能类似于 Kubelet,通过 Watch 本机 VMI 和实例资源,管理本宿主机上所有虚机实例;
    • 主要执行动作如下:

      • 使 VMI 中定义的 Spec 与相应的 libvirt (本地 socket 通信)保持同步;
      • 汇报及控制更新虚拟机状态;
      • 调用相关插件初始化节点上网络和存储资源;
      • 热迁移相关操作;
  • virt-launcher:

    • Kubevirt 会为每一个 VMI 对象创建一个 Pod,该 Pod 的主进程为 virt-launcher,virt-launcher 的 Pod 提供了 cgroups 和 namespaces 的隔离,virt-launcher 为虚拟机实例的主进程。
    • virt-handler 通过将 VMI 的 CRD 对象传递给 virt-launcher 来通知 virt-launcher 启动 VMI。然后,virt-launcher 在其容器中使用本地 libvirtd 实例来启动 VMI。virt-launcher 托管 VMI 进程,并在 VMI 退出后终止。
    • 如果 Kubernetes 运行时在 VMI 退出之前尝试关闭 virt-launcher 容器,virt-launcher 会将信号从Kubernetes 转发到 VMI 进程,并尝试推迟容器的终止,直到 VMI 成功关闭。

下图为 virt-launcher 与 libvirt 通信概略图:

云原生虚拟化:基于 Kubevirt 构建边缘计算实例_第2张图片

资源对象

Kubevirt 是 Kubernetes 的虚拟机管理插件,通过自定义控制器和资源实现对虚拟机的管理功能,通过自定义资源(CRD)机制,同时 Kubevirt 可以自定义额外的操作,来调整常规容器中不可用的行为。这里介绍几个关键资源:

  • VirtualMachineInstance(VMI) :是管理虚拟机的最小资源,一个 VirtualMachineInstance 对象表示一台正在运行的虚拟机实例,包含一个虚拟机所需要的各种配置。
  • VirtualMachine( VM :为集群内的 VirtualMachineInstance 提供管理功能,例如开机/关机/重启虚拟机,确保虚拟机实例的启动状态,与虚拟机实例是 1:1 的关系。
  • VirtualMachineInstanceMigrations:虚拟机迁移需要的资源,一个资源对象表示为一次迁移任务,并反映出虚拟机迁移的状态。
  • VirtualMachineInstanceReplicaSet:类似 ReplicaSet,可以指定数量,批量创建虚拟机。
  • DataVolume:   是对 PVC 之上的抽象,通过自定义数据源,由 CDI 控制器自动创建 PVC 并导入数据到 PVC 中供虚拟机使用。

以下为 VM 资源示例:

kind: VirtualMachine
metadata:
  labels:
    kubevirt.io/vm: vm-cirros
  name: vm-cirros
spec:
  running: false
  template:
    metadata:
      labels:
        kubevirt.io/vm: vm-cirros
    spec:
      domain:
        devices:
          disks:
          - disk:
              bus: virtio
            name: containerdisk
          - disk:
              bus: virtio
            name: cloudinitdisk
        machine:
          type: ""
        resources:
          requests:
            memory: 64M
      terminationGracePeriodSeconds: 0
      volumes:
      - name: containerdisk
        containerDisk:
          image: kubevirt/cirros-container-disk-demo:latest
      - cloudInitNoCloud:
          userDataBase64: IyEvYmluL3NoCgplY2hvICdwcmludGVkIGZyb20gY2xvdWQtaW5pdCB1c2VyZGF0YScK
        name: cloudinitdisk
02

虚拟机管理

\

在了解了 Kubevirt 是什么,它的主要架构以及比较关键的资源对象后,我们来看看如何使用 Kubevirt 进行虚拟机管理。这里主要分为虚拟机创建、存储和网络三个部分。\

虚拟机创建

**\
**

虚拟机创建分为创建 DataVolume(为虚拟机准备存储)和 VMI 两个部分。简要流程如下:\

  1. 用户通过 kubectl/api 创建 VM 对象;
  2. virt-api 通过 webhook 校验 VM 对象;
  3. virt-controller 监听到 VM 的创建,生成 VMI 对象;
  4. virt-controller 监听到 VMI 的创建,判断虚拟机 DataVolume 是否被初始化,如果没有被初始化,则创建 DateVolume 初始化准备虚拟机需要的数据;
  5. 虚拟机 DataVolume 初始化完成后,virt-controller 创建 virt-launcher Pod 来启动虚机;
  6. kubernetes 调度虚拟机 Pod 到集群中的一台主机上;
  7. virt-controller Watch 到 VMI 的容器已启动,更新 VMI 对象中的 nodeName 字段。后续工作由 virt-handler 接管以进行进一步的操作;
  8. virt-handler(DaemonSet)Watch 到 VMI 已分配给运行它的主机上,通过获取 Domain 与 vmi 状态来决定发送命令启动虚拟机;
  9. virt-launcher 获取到 virt-handler 命令,与 libvirtd 实例通信来操作虚拟机。

云原生虚拟化:基于 Kubevirt 构建边缘计算实例_第3张图片

以上,我们便完成了虚拟机的初步创建。不过在虚拟机创建过程,可能出现不同的状态提示,本文列举了常见的几种状态及其示意:

  • Pending: 虚拟机实例已经创建,等待后续控制流程;
  • Scheduling: 虚拟机 Pod 已经创建,正在调度中;
  • Scheduled: 虚拟机 Pod 调度完成,并处于 running 状态,此状态后 virt-controller 控制结束,由 virt-handler 接管后续工作;
  • Running: 虚拟机正常运行;
  • Succeeded: 虚拟机由于收到 sigterm 信号或者内部关机操纵而退出;
  • Failed: 由于异常情况导致虚拟机 crash;

虚拟机存储

存储部分 Kubevirt 可以基于 Kubetnetes 运行,因此可以复用 Kubernetes 中的存储设计,如 PVC、PV 等。同时,Kubevirt 在 PVC 之上自定义了 DataVolume 资源。

DataVolume 是由 Kubevirt 团队开发并开源的容器数据导入插件 CDI(containerized-data-importer)所定义的 CRD。

虚机启动之前会创建 DataVolume 对象,由 CDI 控制器自动创建 PVC,并根据支持的数据源下载数据。虚机的 Pod 启动之前 Kubernetes 会调用 CSI 将云盘挂载到宿主机上,表现形式为块设备并挂在设备到 launcher Pod 中,最终虚拟机通过 virtio 驱动访问磁盘设备。

云原生虚拟化:基于 Kubevirt 构建边缘计算实例_第4张图片

虚拟机网络

在网络方面,Kubevirt 复用了 Kubernetes 的容器网络,并在此之上提供4种虚拟机网络模型。当前 Kubevirt 主要支持四种网络模式:

// +k8s:openapi-gen=true
type InterfaceBindingMethod struct {
   Bridge     *InterfaceBridge     `json:"bridge,omitempty"`
   Slirp      *InterfaceSlirp      `json:"slirp,omitempty"`
   Masquerade *InterfaceMasquerade `json:"masquerade,omitempty"`
   SRIOV      *InterfaceSRIOV      `json:"sriov,omitempty"`
}

虚拟机和容器网络是互通的,可以实现虚拟机和容器不同形态的业务之间互联互通。这里介绍下Bridge模式,这种方式实现较简单,但是网络损耗也较大:

  • Bridge 模式下 pod 的 veth pair 仍然由 cni 管理创建,而 virt-launcher 会将 Pod IP 摘掉,pod veth 设备 eth0 仅作为虚拟机的虚拟网卡与外部网络通信的桥梁。
  • virt-launcher 在 pod 中创建 tap 设备 vnet0 和 bridge 桥设备 br1,同时实现了简单的单 ip dhcp server,虚拟机中启动 dhclient, Virt-launcher 将 IP 分配给虚拟机。

云原生虚拟化:基于 Kubevirt 构建边缘计算实例_第5张图片

03 总结

以上是基于 Kubevirt 构建边缘计算实例的技术方案,也对 Kubevirt 的架构及主要模块进行了详细说明。Kubevirt 作为 Kubernetes 的 CRD 插件,不仅为 Kubernetes 补全了调度虚拟机的能力,也为解决容器和虚拟机的融合调度提供了一个较为成熟、可行的解决方案。

参考资料:

[1] https://github.com/kubevirt/k...
[2] https://kubevirt.io/

你可能感兴趣的:(云原生虚拟化:基于 Kubevirt 构建边缘计算实例)