首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

运维加薪技术——Kubevirt

1. Kubevirt介绍

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

2. Kubevirt关键组件

2.1virt-api

      kubevirt是以CRD形式去管理vm pod, virt-api 就是所有虚拟化操作的入口,包括常规的 CRD 更新验证以及vm start、stop,为 Kubevirt 提供 API 服务能力,比如许多自定义的 API 请求,如开机、关机、重启等操作,通过 APIService 作为 Kubernetes Apiserver 的插件,业务可以通过 Kubernetes Apiserver 直接请求到 virt-api;

2.2virt-controller

     Virt-controller会根据 vmi CRD,生成对应的 virt-lancher pod,并维护 CRD 的状态,是Kubevirt 的控制器,功能类似于 Kubernetes 的 controller-manager,管理和监控 VMI 对象及其关联的 Pod,对其状态进行更新;

2.3 virt-handler

    该模块以 Daemonset 形式部署,功能类似于 Kubelet,通过 Watch 本机 VMI 和实例资源,管理本宿主机上所有虚机实例;

主要执行动作如下:

(1)使 VMI 中定义的 Spec 与相应的 libvirt (本地 socket 通信)保持同步;

(2)汇报及控制更新虚拟机状态;

(3)调用相关插件初始化节点上网络和存储资源;

(4)热迁移相关操作;

2.4virt-launcher

(1)Kubevirt 会为每一个 VMI 对象创建一个 Pod,该 Pod 的主进程为 virt-launcher,virt-launcher 的 Pod 提供了 cgroups 和 namespaces 的隔离,virt-launcher 为虚拟机实例的主进程。

(2)virt-handler 通过将 VMI 的 CRD 对象传递给 virt-launcher 来通知 virt-launcher 启动 VMI。然后,virt-launcher 在其容器中使用本地 libvirtd 实例来启动 VMI。virt-launcher 托管 VMI 进程,并在 VMI 退出后终止。

(3)如果 Kubernetes 运行时在 VMI 退出之前尝试关闭 virt-launcher 容器,virt-launcher 会将信号从Kubernetes 转发到 VMI 进程,并尝试推迟容器的终止,直到 VMI 成功关闭。

2.5其他

2.5.1libvirtd

    libvirtd的一个实例存在于每个VMI pod中。virt-launcher使用libvirtd来管理VMI进程的生命周期。

2.5.2virtctl

    virctl 是kubevirt自带类似kubectl命令,它是越过virt-lancher pod这层去直接管理vm,可以控制 vm 的start、stop、restart。

3.资源对象

3.1 VirtualMachine(VM)

   该结果为集群内的VirtualMachineInstance提供管理功能,例如开机、关机、重启虚拟机,确保虚拟机实例的启动状态,与虚拟机实例是1:1的关系,类似与spec.replica为1的StatefulSet。(虚拟机设置)

3.2 VirtualMachineInstance(VMI)

   VMI类似于kubernetes Pod,是管理虚拟机的最小资源。一个VirtualMachineInstance对象即表示一台正在运行的虚拟机实例,包含一个虚拟机所需要的各种配置。(vm的实例化)

3.3 VirtualMachineInstanceReplicaSet

   此结构类似ReplicaSet,可以启动指定数量的VirtualMachineInstance,并且保证指定数量的VirtualMachineInstance运行。

3.4 VirtualMachineInstanceMigrations

     此结构是虚拟机迁移需要的资源,一个资源对象表示为一次迁移任务,并反映出虚拟机迁移的状态。

4. VM构建流程

(1)执行 kubectl apply -f vm.yaml;

(2)k8s api-server 收到请求后会创建 vm 对象;

(3)virt-controller 监控到有新的 vm 对象,调用 k8s api-server,去创建对应的 vmi 对象;

(4)virt-controller 监控到有新的 vmi 对象,调用 k8s api-server去创建对应的 virt-launcher-xxx Pod 对象;

(5)Pod 的创建首先通过 kube-scheduler 选一台合适的 Node,再创建对应 Pod;(6)启动 virt-launcher 进程监听来自 virt-handler 的消息,对应更新 vmi 的 nodeName 字段

(7)节点上的 virt-handler 通过 Informer 监听到有新 vmi 创建到自己的节点后,发送 gRPC 消息把该 vmi 的各项配置发给 virt-launcher 进程,启动虚拟机;

(8)virt-launcher 接收到 vmi 的配置后转成虚拟机的 xml 文件,启动该虚拟机

(9)虚拟启动之后,更新 vmi 的虚拟机状态

5. 磁盘和卷

5.1PersistentVolumeClaim

    该结构使用 PVC 做为存储,适用于数据持久化,即在虚拟机重启或者重建后数据依旧存在。使用的 PV 类型可以是 block 和 filesystem,使用 filesystem 时,会使用 PVC 上的 /disk.img,格式为 RAW 格式的文件作为硬盘。block 模式时,使用 block volume 直接作为原始块设备提供给虚拟机。

5.2ephemeral

     基于后端存储在本地做一个写时复制(COW)镜像层,所有的写入都在本地存储的镜像中,VM 实例停止时写入层就被删除,后端存储上的镜像不变化。

5.3containerDisk

    基于 scratch 构建的一个 docker image,镜像中包含虚拟机启动所需要的虚拟机镜像,可以将该 docker image push 到 registry,使用时从 registry 拉取镜像,直接使用 containerDisk 作为 VMI 磁盘,数据是无法持久化的。

5.4hostDisk

    它使用节点上的磁盘镜像,类似于 hostpath,也可以在初始化时创建空的镜像。

5.5dataVolume

    提供了在虚拟机启动流程中自动将虚拟机磁盘导入 pvc 的功能,在不使用 DataVolume 的情况下,用户必须先准备带有磁盘映像的 pvc,然后再将其分配给 VM 或 VMI。

6. 安装Kubevirt前提条件

(1)k8s或K3s或Minikube(未能成功构建vm内核版本过低导致)

(2)QEMU与KVM(服务器能够支持虚拟化)

(3)Libvirt(virt-launcher调用)

7. Kubevirt通过源码更新

步骤一:通过docker images进行镜像创建,生成pod,以virt-api为例,从github仓库上下载压缩包;

步骤二:设置代理

go env -w GOPROXY=goproxy.cn,direct

     编译代码

go build ../virt-api/virt-api.go

     编译完成后,会在项目根目录下生成一个 virt-api 的可执行文件

步骤三:  制作镜像(正常通过Dockerfile制作docker镜像)

FROM ../virt-api:0.40.0-1

COPY virt-api /usr/bin/virt-api

     把上面编译好的 virt-api 也放到与 Dockerfile 同级目录,执行如下命令 

docker build -t ../virt-api:自定义版本号

     执行完成后,docker images 就会看到刚刚制作的 0.40.0-2 的镜像。

步骤四:然后使用如下命令,更新 virt-api pod 所使用的 image,对应的 Pod 就会自动重启。

$kubectl patch pod virt-api-775dd874f5-rjclv -n kubevirt -p '{"spec":{"containers":[{"name":"virt-api","image":"../virt-api:0.40.0-2"}]}}'

     替换为对应版本的image。

8. Docker网络原理

     也能够自定义类似Docker0的网络mynet(自定义)

     veth-pair 就是一对的虚拟设备接口,成对出现的。一端连着协议栈,一端彼此相连,每启动一个docker容器,docker就会给docker容器分配一个ip,并会生成一对(2个)网卡,安装docker容器时就会安装一个(docker0)docker专用的网卡,启动后容器里面有一个网卡,宿主机也存在一个网卡用来专门绑定容器用,一对一对出现的。

9. 磁盘存储(通过CDI组件进行管理,提供动态创建PVC并将数据导入PVC的工作流)

9.1 磁盘访问类型

     四种不同的类型来访问磁盘lun、disk、cdrom与floppy (被弃用),除了floppy,允许您指定bus属性。这bus属性确定磁盘将如何呈现给客户操作系统。floppy磁盘不支持bus属性:它们总是附加到fdc。

指定bus类型bus:virtio

9.1.1lun

     该磁盘方案会将卷作为LUN设备向虚拟机公开。这使得虚拟机可以执行任意iSCSI命令。

metadate:

name: testvmi-lun

apiVersion: kubevirt.io/v1alpha3

kind: VirtualMachineInstance

spec:

domain:

resources

requests:

memory: 64M

devices:

disks:

- name: mypvcdisk

# This makes it a lun device

lun: {}

volumes:

- name: mypvcdisk

persistentVolumeClaim:

claimNae: mypvc

9.1.2 Disk

      该磁盘方案会将卷作为普通磁盘公开给虚拟机。

9.1.3 Cdrom

       该磁盘方案将卷作为cdrom驱动器向虚拟机公开。默认情况下,它是只读的。

9.2 Volumes类型

9.2.1cloudInitNoCloud

      允许附加cloudInitNoCloud虚拟机的数据源。如果VM包含正确的cloud-init设置,它将把磁盘作为用户数据源。

metadata:

name: testvmi-cloudinitnocloud

apiVersion: kubevirt.io/v1alpha3

kind: VirtualMachineInstance

spec:

domain:

resources:

requests:

memory: 64M

devices:

disks:

- name: mybootdisk

lun: {}

- name: mynoclouddisk

disk: {}

volumes:

- name: mybootdisk

persistentVolumeClaim:

claimName: mypvc

- name: mynoclouddisk

cloudInitNoCloud:

secretRef:

name: testsecret

9.2.2cloudInitConfigDrive

     允许附加cloudInitConfigDrive虚拟机的数据源。如果VM包含正确的cloud-init设置,它将把磁盘作为用户数据源。

9.2.3persistentVolumeClaim(主要)

     当虚拟机终止后,虚拟机实例的磁盘需要保留时,这允许虚拟机的数据在重启之间保持持久。

9.2.4ephemeral

     临时卷是使用网络卷作为只读后备存储的本地COW(写入时复制)映像。有了临时卷,网络后备存储器就永远不会发生变化。相反,所有写入都存储在本地存储中的临时映像上。KubeVirt在虚拟机启动时动态生成与虚拟机相关的临时映像,并在虚拟机停止时丢弃这些临时映像。当VM达到最终状态(例如,成功、失败)时,COW映像被丢弃。目前,只有PersistentVolumeClaim可以用作临时卷的后备存储器。

metadata:

name: testvmi-ephemeral-pvc

apiVersion: kubevirt.io/v1alpha3

kind: VirtualMachineInstance

spec:

domain:

resources:

requests:

memory: 64M

devices:

disks:

- name: mybvcdisk

lun: {}

volumes:

- name: mybvcdisk

ephemeral:

persistentVolumeClaim:

claimName: mypvc

9.2.5 containerDisk

     最初是registryDisk,该特性提供了在容器映像注册表中存储和分发虚拟机磁盘的能力。containerDisks可以在VirtualMachineInstance规范的磁盘部分分配给虚拟机。

     containerDisks是临时存储设备,可以分配给任意数量的活动VirtualMachineInstances。这使得它们成为想要复制大量不需要持久数据的虚拟机工作负载的用户的理想工具。containerDisks通常与VirtualMachineInstanceReplicaSets一起使用。

     但是containerDisks对于任何需要跨虚拟机重启的持久化根磁盘的工作负载,不是一个好的解决方案。

(1) containerDisk工作流示例

     containerdisks能够并且应该基于scratch

     将本地VirtualMachineInstance磁盘注入容器映像。

cat << END> Dockerfile

FROM scratch

ADD --chown=107:107 fedora25.qcow2 /disk/

END

docker build -t vmidisks/fedora25:latest .

    将远程VirtualMachineInstance磁盘插入容器映像。

cat << END> Dockerfile

FROM scratch

ADD --chown=107:107 cloud.centos.org/centos/7/images/Centos-7-x86_64-GenericCloud.qcow2 /disk/

END

docker push vmidisks/fedora25:latest .

     将容器磁盘作为临时磁盘连接到虚拟机。

metadata:

name: testvmi-containerdisk

apiVersion: kubevirt.io/v1alpha3

kind: VirtualMachineInstance

spec:

domain:

resources:

requests:

memory: 64M

devices:

disks:

- name: containerdisk

lun: {}

volumes:

- name: containerdisk

containerdisk:

image: vmidisks/fedora25:latest

     注意containerDisk是基于文件的,因此不能作为lun虚拟机的设备。

     自定义磁盘映像路径:ContainerDisk还允许在需要时将磁盘映像存储在任何文件夹中。

     构建容器磁盘映像:

cat << END> Dockerfile

FROM scratch

ADD fedora25.qcow2 /custom-disk-path/fedora25.qcow2

END

docker build -t vmidisks/fedora25:latest .

docker push vmidisks/fedora25:latest .

    使用指向自定义位置的容器磁盘创建VMI:

metadata:

name: testvmi-containerdisk

apiVersion: kubevirt.io/v1alpha3

kind: VirtualMachineInstance

spec:

domain:

resources:

requests:

memory: 64M

devices:

disks:

- name: containerdisk

disk: {}

volumes:

- name: containerdisk

containerdisk:

image: vmidisks/fedora25:latest

path: /custom-disk-path/fedora25.qcow2

9.2.6emptyDisk

     工作方式类似于在Kubenetes中的emptyDir。分配额外的一部分的qcow2磁盘,并且将与虚拟机的寿命一样长。因此,它将在客户端虚拟机重新启动后存活,但不能在虚拟机重新创建后存活。磁盘capacity需要指定大小。

apiVersion: kubevirt.io/v1alpha3

kind: VirtualMachineInstance

metadata:

name: testvmi-nocloud

spec:

terminationGracePeriodSeconds: 5

domain:

resources:

requests:

memory: 64M

devices:

disks:

- name: containerdisk

disk:

bus: virtio

volumes:

- name: containerdisk

containerdisk:

image: kubvirt/cirros-registry-disk-dome:latest

- name: emptydisk

emptyDisk:

capacity: 2Gi

     短暂的虚拟机通常带有只读根映像和有限的tmpfs空间。在许多情况下,这不足以安装应用程序依赖项并为应用程序数据提供足够的磁盘空间。虽然这些数据并不重要,因此可能会丢失,但在应用程序的生命周期中,仍然需要这些数据来正常运行,空磁盘经常被使用并安装在/var/lib或者/var/run.

9.2.7hostDisk

     hostDisk卷类型提供了创建或使用位于节点某处的磁盘映像的能力。它的工作方式类似于hostPath在Kubernetes中,提供了两种使用类型:DiskOrCreate如果给定位置不存在磁盘映像,则创建一个;Disk磁盘映像必须存在于给定的位置。

     注意:您需要启用主机磁盘。

     创建位于/data/disk.img的1Gi磁盘映像,并将其附加到虚拟机

apiVersion: kubevirt.io/v1alpha3

kind: VirtualMachineInstance

metadata:

labels:

special: vmi-host-disk

name: vmi-host-disk

spec:

terminationGracePeriodSeconds: 0

domain:

resources:

requests:

memory: 64M

devices:

disks:

- name: host-disk

disk:

bus: virtio

mechine:

type: ""

volumes:

- hostDisk:

capacity: 1Gi

path: /data/disk.img

name: host-disk

status: {}

10. VM网络和接口(通过安装calico与flannel进行管理)

     Kubernetes网络(Kubernetes CNI负责配置),libvirt网络,虚拟机网络

10.1 网络类型

10.1.1pod

     网络代表每个pod中的集群网络解决方案配置的接口是通过默认的eth0。

kind: vm

spec:

domain:

devices:

interfaces:

- name: default

masquerade: {}

networks:

- name: default

pod: {} # Stock pod network

10.1.2Multus

     Multus CNI是Kubernetes的一个容器网络接口(CNI)插件,可以将多个网络接口附加到pod上。通常,在Kubernetes中,每个pod只有一个网络接口(除了一个环回接口)——使用Multus,您可以创建一个具有多个接口的多宿主pod。这是通过Multus充当“元插件”来实现的,元插件是一个CNI插件,可以调用多个其他CNI插件。

     注意:multus默认网络和pod网络类型是互斥的。virt-launcher pod启动VMI时不使用pod网络的配置。选用默认的multus委托必须至少返回一个IP地址。

10.2 接口

     每个接口还可能有额外的配置字段,用于修改客户实例中“可见”的属性。

kind: vm

spec:

domain:

devices:

interfaces:

- name: default

model: e1000 # expose e1000 NIC to the guest

masquerade: {} # connect through a masquerade

ports:

- name: http

port: 80

networks:

- name: default

pod: {}

     注意:使用slirp接口时,只有已配置的端口会被转发到虚拟机.

kind: vm

spec:

domain:

devices:

autoattachPodInterface: false

10.2.1bridge

     在bridge模式下,虚拟机通过linux“桥”连接到网络后端。pod网络IPv4地址通过DHCPv4委派给虚拟机。虚拟机应配置为使用DHCP获取IPv4地址。

     注意:如果虚拟机接口规范中未配置特定的MAC地址,则来自相关pod接口的MAC地址将被委派给虚拟机。

kind: vm

spec:

domain:

devices:

interfaces:

- name: red

bridge: {} #connect through a bridge

networks:

- name: red

multus:

networkName: red

     这时,bridge模式不支持附加配置字段。

     注意:由于IPv4地址委派,在bridge模式pod没有配置IP地址,这可能会给依赖它的第三方解决方案带来问题。例如,Istio可能无法在此模式下工作。

     注意:管理员可以禁止使用bridge通过指定配置标志的pod网络接口类型。要实现这一点,管理员应该将以下选项设置为false:

apiVersion: kubevirt.io/v1alpha3

kind: Kubevirt

metadata:

name: kubevirt

namespace: kubevirt

spec:

configuration:

networks:

permitBridgeInterfaceOnPodNetwork: false

10.2.2slirp

     在slirp模式下,虚拟机使用QEMU用户网络模式连接到网络后端。在这种模式下,QEMU为虚拟机分配内部IP地址,并将它们隐藏在NAT之后。

kind: vm

spec:

domain:

devices:

interfaces:

- name: red

slirp: {} #connect using SLIRP mode

networks:

- name: red

pod: {}

     这时,slirp模式不支持附加配置字段。

     注意:在slirp模式下,唯一支持的协议是TCP和UDP。ICMP是不支持。

10.2.3masquerade

     在masquerade模式下,KubeVirt将内部IP地址分配给虚拟机,并将其隐藏在NAT之后。所有离开虚拟机的流量都使用pod IP地址进行“NAT”。应该将客户操作系统配置为使用DHCP来获取IPv4地址。

     为了允许特定端口的流量进入虚拟机,模板ports接口的一部分应配置如下。如果ports部分丢失,所有端口都转发到虚拟机。

kind: vm

spec:

domain:

devices:

interfaces:

- name: red

masquerade: {} #connect using masquerade mode

ports:

- port: 80 # allow incoming traffic on port 80 to get into the virtual machine

networks:

- name: red

pod: {}

     注意:伪装只允许连接到pod网络。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OeuwRqWV28Kt-ugcnP5qe_rw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券