容器云原生虚拟化-Kubevirt

举报
小源博客 发表于 2023/04/11 12:20:34 2023/04/11
【摘要】 Kubevirt 是 Red Hat 开源的以容器方式运行虚拟机的项目,通过 CRD 的方式来管理虚拟机实例,它的所有概念都和一般的 Kubernetes 容器应用差不多,不需要增加学习成本。1. Kubevirt 架构设计Kubevirt 主要实现了下面几种资源,以实现对虚拟机的管理:VirtualMachineInstance(VMI) : 类似于 kubernetes Pod,是管理虚...
Kubevirt 是 Red Hat 开源的以容器方式运行虚拟机的项目,通过 CRD 的方式来管理虚拟机实例,它的所有概念都和一般的 Kubernetes 容器应用差不多,不需要增加学习成本。

1. Kubevirt 架构设计
Kubevirt 主要实现了下面几种资源,以实现对虚拟机的管理:
  • VirtualMachineInstance(VMI) : 类似于 kubernetes Pod,是管理虚拟机的最小资源。一个 VirtualMachineInstance 对象即表示一台正在运行的虚拟机实例,包含一个虚拟机所需要的各种配置。
  • VirtualMachine(VM) : 为群集内的 VirtualMachineInstance 提供管理功能,例如开机/关机/重启虚拟机,确保虚拟机实例的启动状态,与虚拟机实例是 1:1 的关系,类似与 spec.replica 为 1 的 StatefulSet。
  • VirtualMachineInstanceReplicaSet : 类似 ReplicaSet,可以启动指定数量的 VirtualMachineInstance,并且保证指定数量的 VirtualMachineInstance 运行,可以配置 HPA。
Kubevirt 的整体架构如图:


  • virt-api : 负责提供一些 KubeVirt 特有的 api,像是 console, vnc, startvm, stopvm 等。
  • virt-controller : 管理和监控 VMI 对象及其关联的 Pod,对其状态进行更新。
  • virt-hander : 以 DaemonSet 运行在每一个节点上,监听 VMI 的状态向上汇报,管理 VMI 的生命周期。
  • virt-launcher : 以 Pod 方式运行,每个 VMI Object 都会对应一个 virt-launcher Pod,容器内有单独的 libvirtd,用于启动和管理虚拟机。


2. 磁盘和卷
虚拟机镜像(磁盘)是启动虚拟机必不可少的部分,KubeVirt 中提供多种方式的虚拟机磁盘,虚拟机镜像(磁盘)使用方式非常灵活。这里列出几种比较常用的:
  • PersistentVolumeClaim : 使用 PVC 做为后端存储,适用于数据持久化,即在虚拟机重启或者重建后数据依旧存在。使用的 PV 类型可以是 block 和 filesystem,使用 filesystem 时,会使用 PVC 上的 /disk.img,格式为 RAW 格式的文件作为硬盘。block 模式时,使用 block volume 直接作为原始块设备提供给虚拟机。
  • ephemeral : 基于后端存储在本地做一个写时复制(COW)镜像层,所有的写入都在本地存储的镜像中,VM 实例停止时写入层就被删除,后端存储上的镜像不变化。
  • containerDisk : 基于 scratch 构建的一个 docker image,镜像中包含虚拟机启动所需要的虚拟机镜像,可以将该 docker image push 到 registry,使用时从 registry 拉取镜像,直接使用 containerDisk 作为 VMI 磁盘,数据是无法持久化的。
  • hostDisk : 使用节点上的磁盘镜像,类似于 hostpath,也可以在初始化时创建空的镜像。
  • dataVolume : 提供在虚拟机启动流程中自动将虚拟机磁盘导入 pvc 的功能,在不使用 DataVolume 的情况下,用户必须先准备带有磁盘映像的 pvc,然后再将其分配给 VM 或 VMI。dataVolume 拉取镜像的来源可以时 http,对象存储,另一块 PVC 等。


虚拟机创建流程
  1. client 发送创建VMI命令达到 k8s API server.
  2. K8S API 创建VMI 对象
  3. virt-controller监听到VMI创建时,根据VMI spec生成pod spec文件,创建pods
  4. k8s调度创建pods
  5. virt-controller监听到pods创建后,根据pods的调度node,更新VMI 的nodeName
  6. virt-handler监听到VMI nodeName与自身节点匹配后,与pod内的virt-launcher通信,virt-laucher创建虚拟机,并负责虚拟机生命周期管理
虚拟机存储
kubevirt目前提供了多种方式的虚拟机的磁盘:
  • registryDisk 可定义image来创建作为虚拟机的root disk。 virt-controller会在pod定义中创建registryVolume的container,container中的entry服务负责 将spec.volumes.registryDisk.image转化为qcow2格式,路径为pod根目录
  • cloudInitNoCloud 对虚拟机利用cloudinit做初始化,类似与nova中的configdrive,会根据spec.volumes.cloudInitNoCloud 创建包含iso文件,包含 meta-data 和 user-data。
  • emptyDisk 创建空的qcow2格式image挂载给虚拟机
  • PVC 上述几种disk都是非持久化的,随之pod的生命周期消亡,PVC是k8s提供的持久化存储。目前kubevirt利用pvc挂载方式都是文件系统模式挂载, PVC首先被挂载在virt-laucher pod中, 且需要存在名称为/disk/*.img的文件,才挂载给虚拟机。 file模式虚拟化方式对虚拟机磁盘存储性能有很大的影响。 熟悉openstack的朋友应该也了解nova-compute中如何使用ceph rbd image的,实质上是libvirt使用librbd以network方式 将rbd image远程改在给虚拟机。而kubevirt中将POD ip移交给了虚拟机,那将意味着pod内的libvirt服务其实是无法直接使用network disk的。 要么增加网络代理转发即通过host来与网络设备通讯,要么就是采用k8s volumeMount:block feature来实现。 kubevirt社区有PR已经实现了以Block的方式去使用是rbd image, 笔者手动merge并测试通过。 实质是使用了kernel rbd.ko,首先将rbd image map到host,block的mount方式将不再以文件系统方式去挂载/dev/rbdx,而是为作为原始设备给pod,而pod内的libvirt就可以block方式将rbd image作为 磁盘挂载给虚拟机。 相较于PVC先格式化为文件系统并必须创建disk.img文件的使用方式,显然rbd image 以block device直接作为块设备给虚拟机少了本地文件系统层 单从存储效率讲都能提高不少。至于librbdrbd.ko的性能本文没有对比测试,有时间再补充。 k8s PVC后续版本应该也可以提供block mode方式的mount。
  • dataVolume dataVolume是kubevirt下的一个子项目containerized-data-importer(CDI), 也是以CDR的方式增加DataVolumeresource。 可以看成是从PVC和registryDisk衍生出来的,上面提过PVC使用是比较麻烦的,不仅需要PVC还需要创建disk.img, dataVolume其实将这个过程简化了,自动化的将disk.img创建在PVC中。 创建DataVolume是可以定义source即image/data来源可以是http或者s3的URL,CDI controller会将 自动将image转化并拷贝到PVC文件系统/disk/
虚拟机网络
kubevirt虚拟机网络使用的是pod网络也就是说,虚拟网络原生与pod之间是打通的。虚拟机具体的网络如图所示, virt-launcher pod网络的网卡不再挂有pod ip,而是作为虚拟机的虚拟网卡的与外部网络通信的交接物理网卡。 那么虚拟机是如何拿到pod的ip的呢,virt-launcher实现了简单的单ip dhcp server,就是需要虚拟机中启动dhclient,virt-launcher 服务会分配给虚拟机。

使用命令kubectl exec $virt-launch-pod -c compute -- brctl show可以看到bridge信息。 其中eth0就是pod与host网络通讯的veth peer网卡。也可以通过virsh dumpxml命令查看虚拟机的xml定义文件。


3. 准备工作

kubeneters版本:
[root@master ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.1", GitCommit:"632ed300f2c34f6d6d15ca4cef3d3c7073412212", GitTreeState:"clean", BuildDate:"2021-08-19T15:45:37Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.1", GitCommit:"632ed300f2c34f6d6d15ca4cef3d3c7073412212", GitTreeState:"clean", BuildDate:"2021-08-19T15:39:34Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"linux/amd64"}
内核:
[root@master ~]# uname -r 5.4.119-1.el7.elrepo.x86_64
[root@master ~]#



在安装 Kubevirt 之前,需要做一些准备工作。先安装 libvrt 和 qemu 软件包:
[root@master ~]# yum -y install qemu-kvm libvirt virt-install bridge-utils


解决
[root@node mnt]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet intel_iommu=on"
GRUB_DISABLE_RECOVERY="true"
[root@node mnt]# 

[root@node mnt]# grub2-mkconfig -o /boot/grub2/grub.cfg

###使用最新版
[root@master ~]# kubectl create namespace kubevirt
namespace/kubevirt created
[root@master ~]# kubectl create configmap -n kubevirt kubevirt-config \
    --from-literal debug.useEmulation=true
configmap/kubevirt-config created

$ export VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases | grep tag_name | grep -v -- '-rc' | head -1 | awk -F': ' '{print $2}' | sed 's/,//' | xargs)
$ kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml
$ kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml



[root@master kubevirt]# kubectl apply -f kubevirt-operator.yaml 

[root@master kubevirt]# kubectl apply -f kubevirt-cr.yaml 

部署CRD
$ export VERSION=$(curl -s https://github.com/kubevirt/containerized-data-importer/releases/latest | grep -o "v[0-9]\.[0-9]*\.[0-9]*")
$ kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
$ kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml

[root@master kubevirt]# kubectl apply -f cdi-operator.yaml 

[root@master kubevirt]# kubectl apply -f cdi-cr.yaml

部署完成
[root@master ~]# kubectl get all -n kubevirt   
NAME                                   READY   STATUS    RESTARTS        AGE
pod/virt-api-6677fb87c6-96tw6          1/1     Running   1 (3m46s ago)   6m59s
pod/virt-api-6677fb87c6-vj9kb          1/1     Running   1 (3m46s ago)   6m59s
pod/virt-controller-5dd87c95c4-kbkzd   1/1     Running   1 (3m51s ago)   6m34s
pod/virt-controller-5dd87c95c4-xrsq7   1/1     Running   1 (3m52s ago)   6m34s
pod/virt-handler-7xzqc                 1/1     Running   0               6m34s
pod/virt-handler-zkthk                 1/1     Running   0               6m34s
pod/virt-operator-85b8997768-26tgv     1/1     Running   0               7m31s
pod/virt-operator-85b8997768-9mg9v     1/1     Running   0               7m31s

NAME                                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/kubevirt-operator-webhook     ClusterIP   10.108.59.192    <none>        443/TCP   7m1s
service/kubevirt-prometheus-metrics   ClusterIP   10.101.241.110   <none>        443/TCP   7m1s
service/virt-api                      ClusterIP   10.96.153.34     <none>        443/TCP   7m1s

NAME                          DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
daemonset.apps/virt-handler   2         2         2       2            2           kubernetes.io/os=linux   6m34s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/virt-api          2/2     2            2           6m59s
deployment.apps/virt-controller   2/2     2            2           6m34s
deployment.apps/virt-operator     2/2     2            2           7m31s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/virt-api-6677fb87c6          2         2         2       6m59s
replicaset.apps/virt-controller-5dd87c95c4   2         2         2       6m34s
replicaset.apps/virt-operator-85b8997768     2         2         2       7m31s

NAME                            AGE     PHASE
kubevirt.kubevirt.io/kubevirt   7m19s   Deployed


CRD

[root@master ~]# kubectl get all -n cdi     
NAME                                   READY   STATUS    RESTARTS   AGE
pod/cdi-apiserver-85bcc64cd8-fcpk9     1/1     Running   0          4m29s
pod/cdi-deployment-6cfb88c6f-9p6f2     1/1     Running   0          4m23s
pod/cdi-operator-566cfb699-mc2f9       1/1     Running   0          5m7s
pod/cdi-uploadproxy-54859dddc8-b8jq4   1/1     Running   0          4m19s

NAME                             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/cdi-api                  ClusterIP   10.96.127.212   <none>        443/TCP    4m29s
service/cdi-prometheus-metrics   ClusterIP   10.106.32.177   <none>        8080/TCP   4m23s
service/cdi-uploadproxy          ClusterIP   10.96.5.140     <none>        443/TCP    4m19s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/cdi-apiserver     1/1     1            1           4m29s
deployment.apps/cdi-deployment    1/1     1            1           4m23s
deployment.apps/cdi-operator      1/1     1            1           5m7s
deployment.apps/cdi-uploadproxy   1/1     1            1           4m19s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/cdi-apiserver-85bcc64cd8     1         1         1       4m29s
replicaset.apps/cdi-deployment-6cfb88c6f     1         1         1       4m23s
replicaset.apps/cdi-operator-566cfb699       1         1         1       5m7s
replicaset.apps/cdi-uploadproxy-54859dddc8   1         1         1       4m19s
虚拟机
KubeVirt 提供了一个名为virtctl的附加二进制文件,用于快速访问 VM 的串行端口和图形端口,并处理启动/停止操作。


[root@master kubevirt]# VERSION=$(kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.observedKubeVirtVersion}")
[root@master kubevirt]# ARCH=$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/amd64/') || windows-amd64.exe
[root@master kubevirt]# echo ${ARCH}
linux-amd64
[root@master kubevirt]# 

[root@master kubevirt]# curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-${ARCH}

[root@master kubevirt]# chmod +x virtctl
[root@master kubevirt]# install virtctl /usr/local/bin

[root@master kubevirt]# virtctl version  
Client Version: version.Info{GitVersion:"v0.52.0", GitCommit:"836afc8c556206fc7715615ddb6516d76f4240db", GitTreeState:"clean", BuildDate:"2022-04-08T16:25:37Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{GitVersion:"v0.52.0", GitCommit:"836afc8c556206fc7715615ddb6516d76f4240db", GitTreeState:"clean", BuildDate:"2022-04-08T16:25:37Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}

虚拟机创建分为创建DataVolume和VMI两个流程:
创建DataVolume后,CDI组件创建对应的PVC并且关联到合适的PV,然后通过临时Importer Pod拉取虚拟机容器镜像绑定到DataVolume生成的PV中,并且将镜像转换成disk.img文件存储在PV中供虚拟机使用。
创建VMI后,等待disk.img转换成功,然后在对应的Node上启动Launcher Pod,并将CDI流程生成的PV挂载到Pod内,当作虚拟机启动的系统盘。Launcher根据VMI的定义生成定义虚拟机的XML文件,然后调用libvirt进程调用Qemu命令创建并且启动虚拟机。VMI会对Launcher Pod状态进行同步,反应VM运行的状态。
VM资源清单文件模板如下:
apiVersion: kubevirt.io/v1  # API版本号
kind: VirtualMachine  # 对象类型,VM
metadata:  # 元数据
  creationTimestamp: null
  labels:  # 自定义标签的名称和值
    kubevirt-vm: vm-${NAME}
    kubevirt.io/os: fedora27
  name: ${NAME}  # VM名称
spec:
  running: false  # 决定是否启动VMI
  template:
    metadata:
      creationTimestamp: null
      labels:
        kubevirt-vm: vm-${NAME}
        kubevirt.io/os: fedora27
    spec:
      domain:
        cpu:
          cores: ${{CPU_CORES}}  # vCPUS数量
        devices:
          disks:
          - disk:  # 提供用户数据user-data和元数据meta-data
              bus: virtio
            name: cloudinitdisk
          - disk:  # 系统盘(必选)
              bus: virtio
            name: containerdisk
          interfaces:  # 网络接口
          - masquerade: {}
            model: virtio
            name: default
          networkInterfaceMultiqueue: true
          rng: {}
        resources:  # 资源请求
          requests:
            memory: ${MEMORY}
      networks:
      - name: default
        pod: {}  # 使用Kubernetes默认Pod网络
      terminationGracePeriodSeconds: 0
      volumes:
      - cloudInitNoCloud:  # 初始化操作
          userData: |-
            #cloud-config
            password: fedora
            chpasswd: { expire: False }
        name: cloudinitdisk
      - containerDisk:
          image: ${IMAGE}  # 系统镜像
        name: containerdisk


创建虚拟机:


[root@master kubevirt]# cat vm.yaml 
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: testvm
spec:
  running: false
  template:
    metadata:
      labels:
        kubevirt.io/size: small
        kubevirt.io/domain: testvm
    spec:
      domain:
        devices:
          disks:
            - name: containerdisk
              disk:
                bus: virtio
            - name: cloudinitdisk
              disk:
                bus: virtio
          interfaces:
          - name: default
            masquerade: {}
        resources:
          requests:
            memory: 64M
      networks:
      - name: default
        pod: {}
      volumes:
        - name: containerdisk
          containerDisk:
            image: quay.io/kubevirt/cirros-container-disk-demo
        - name: cloudinitdisk
          cloudInitNoCloud:
            userDataBase64: SGkuXG4=
启动虚拟机:


[root@master kubevirt]# kubectl get vms 
NAME     AGE   STATUS    READY
testvm   60s   Stopped   False
[root@master kubevirt]# virtctl start testvm
VM testvm was scheduled to start
[root@master kubevirt]# 

[root@master ~]# kubectl get vmis
NAME     AGE   PHASE     IP            NODENAME   READY
testvm   26s   Running   10.244.0.64   master     False
[root@master ~]# virtctl console testvm
Successfully connected to testvm console. The escape sequence is ^]

login as 'cirros' user. default password: 'gocubsgo'. use 'sudo' for root.
testvm login: cirros
Password: 
$ 
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc pfifo_fast qlen 1000
    link/ether 52:54:00:af:c0:83 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.2/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:feaf:c083/64 scope link 
       valid_lft forever preferred_lft forever
$ 
案例学习:
实时迁移
实时迁移是 KubeVirt 支持的一种常见虚拟化功能,其中运行在一个集群节点上的虚拟机移动到另一个集群节点,而无需关闭来宾操作系统或其应用程序。
要在 Kubernetes 测试环境中试验 KubeVirt 实时迁移,需要进行一些设置。


[root@master ~]# kubectl get nodes -o wide
NAME     STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION                CONTAINER-RUNTIME
master   Ready    master   44m   v1.18.1   10.0.0.10     <none>        CentOS Linux 7 (Core)   5.4.119-1.el7.elrepo.x86_64   docker://19.3.13
node     Ready    <none>   42m   v1.18.1   10.0.0.11     <none>        CentOS Linux 7 (Core)   5.4.119-1.el7.elrepo.x86_64   docker://19.3.13
查看是否完整部署成功:
[root@master ~]# kubectl -n kubevirt get kubevirt
NAME AGE PHASE
kubevirt 27m Deployed

启用实时迁移
在撰写本文时,实时迁移还不是 KubeVirt 的标准功能。ConfigMap要启用该功能,请在“kubevirt”中创建一个Namespace名为“kubevirt-config”的文件。


kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: kubevirt-config
  namespace: kubevirt
  labels:
    kubevirt.io: ""
data:
  feature-gates: "LiveMigration"
EOF

在虚拟机上启动服务
使用virtctl,公开两个端口进行测试,ssh并且http/8080


[root@master ~]# virtctl expose vmi testvm --name=testvm-ssh --port=22 --type=NodePort
Service testvm-ssh successfully exposed for vmi testvm
[root@master ~]# virtctl expose vmi testvm --name=testvm-http --port=8080 --type=NodePort
Service testvm-http successfully exposed for vmi testvm
[root@master ~]# virtctl console testvm
Successfully connected to testvm console. The escape sequence is ^]

$ 

控制台登录提示中提到了默认用户“cirros”及其密码,使用它们登录。接下来,运行以下 while 循环以使用测试消息持续响应任何 http 连接尝试:
$ while true; do ( echo "HTTP/1.0 200 Ok"; echo; echo "Migration test" ) | nc -l
 -p 8080; done
 
迁移虚拟机:


[root@master ~]# cat feature-gate.yaml              
apiVersion: kubevirt.io/v1
kind: KubeVirt
metadata:
  name: kubevirt
  namespace: kubevirt
spec:
  configuration:
    developerConfiguration: 
      featureGates:
      - LiveMigration  #启用实时迁移
      - DataVolumes    
      - HotplugVolumes  #启用热拔插
      - HostDisk   #开启主机盘
      - Snapshot    #启用快照

[root@master ~]# kubectl get vmis      
NAME     AGE   PHASE     IP            NODENAME   READY
testvm   13h   Running   10.244.0.43   master     False

[root@master ~]# virtctl migrate testvm
VM testvm was scheduled to migrate

[root@master ~]# kubectl get vmis
NAME     AGE     PHASE     IP            NODENAME   READY
testvm   6m31s   Running   10.244.1.22   node       False
V M I资源启动虚拟机:


下载qcow2镜像:
http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-Azure-1907.qcow2

制作镜像:

[root@master ~]# yum -y install qemu-img
[root@master ~]# qemu-img convert -p -f qcow2 -O raw CentOS-7-x86_64-Azure-1907.qcow2 CentOS-1907.raw

[root@master ~]# mkdir disk
[root@master ~]# mv CentOS-1907.raw disk/
[root@master ~]# 

修改镜像权限,查看qemu_id
id qemu
[root@master ~]# chown -R 107.107 disk/CentOS-1907.raw 
[root@master ~]# ll disk/
总用量 1558872
-rw-r--r-- 1 qemu 107 8589934592 4月  19 12:05 CentOS-1907.raw

修改云镜像密码:
[root@master ~]# yum -y install libguestfs-tools-c
[root@master ~]# systemctl restart libvirtd
改密码:
[root@master ~]# virt-sysprep --root-password password:centos -a disk/CentOS-1907.raw 
histDisk方式启动虚拟机:
复制到node节点:
[root@master images]# vi kubevirt-cr.yaml

[root@master ~]# cat virt-centos.yaml 
apiVersion: kubevirt.io/v1
kind: VirtualMachineInstance
metadata:
  name: centos
spec:
  terminationGracePeriodSeconds: 30
  domain:
    resources:
      requests:
        memory: 1024M
    devices:
      disks:
      - name: hostdisk
        disk:
          bus: virtio
  volumes:
  - name: hostdisk
    hostDisk:
      path: /disk/disk.img 
      type: DiskOrCreate

[root@master ~]# kubectl get pods
NAME                         READY   STATUS      RESTARTS   AGE
virt-launcher-centos-jrldh   1/2     Running     0          30s
virt-launcher-testvm-8lkvh   2/3     Running     0          101m

[root@master ~]# kubectl get vmis
NAME     AGE    PHASE     IP            NODENAME   READY
centos   44s    Running   10.244.1.27   node       False
testvm   107m   Running   10.244.1.22   node       False
[root@master ~]# 


编写VM资源清单文件:
[root@k8s-master-node1 KubeVirt]# vi vm-fedora.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  labels:
    kubevirt.io/vm: vm-fedora
  name: vm-fedora
spec:
  running: false
  template:
    metadata:
      labels:
        kubevirt.io/vm: vm-fedora
    spec:
      domain:
        resources:
          requests:
            memory: 1Gi
        devices:
          disks:
          - name: containerdisk
            disk:
              bus: virtio
      volumes:
      - name: containerdisk
        containerDisk:
          image: 10.24.2.7/library/fedora-virt:v1.0
创建VM:
[root@k8s-master-node1 KubeVirt]# kubectl apply -f vm-fedora.yaml
virtualmachine.kubevirt.io/vm-fedora created
列出VM:
[root@k8s-master-node1 KubeVirt]# kubectl get vm
NAME     AGE   STATUS    READY
vm-fedora   16s    Stopped     False
检索虚拟机定义:
[root@k8s-master-node1 KubeVirt]# kubectl get vm vm-fedora -o yaml
(2)启动
启动VM:
[root@k8s-master-node1 KubeVirt]# virtctl start vm-fedora
VM vm-fedora was scheduled to start
查看VM和VMI:
[root@k8s-master-node1 KubeVirt]# kubectl get vm
NAME     AGE   STATUS    READY
vm-fedora   53s     Running    True
[root@k8s-master-node1 KubeVirt]# kubectl get vmi
NAME     AGE   PHASE    IP            NODENAME      READY
vm-fedora   25s    Running    10.244.1.24    k8s-worker-node1   True
(3)暂停和取消暂停
暂停VM:
[root@k8s-master-node1 KubeVirt]# virtctl pause vm vm-fedora
VMI vm-fedora was scheduled to pause
或者:
[root@k8s-master-node1 KubeVirt]# virtctl pause vmi vm-fedora
查看暂停后的VMI状态:
[root@k8s-master-node1 KubeVirt]# kubectl get vm vm-fedora -o=jsonpath='{.status.conditions[?(@.type=="Paused")].message}'
VMI was paused by user
取消暂停VM:
[root@k8s-master-node1 KubeVirt]# virtctl unpause vm vm-fedora
VM vm-fedora was scheduled to unpause
或者
[root@k8s-master-node1 KubeVirt]# virtctl unpause vmi vm-fedora
(4)重启
重启VM:
[root@k8s-master-node1 KubeVirt]# virtctl restart vm-fedora
VM vm-fedora was scheduled to restart
(5)删除
删除VM:
[root@k8s-master-node1 KubeVirt]# kubectl delete -f vm-fedora.yaml
virtualmachine.kubevirt.io "vm-fedora" deleted
4.运行策略
VM配置文件中有一个Running字段,用来确定是否运行客户端。KubeVirt总是会立即使用“spec.running: true”重启VM的VMI,一个简单的布尔值并不总是足以完全描述所需的行为。例如,在某些情况下,用户希望能够从虚拟机内部关闭客户端。使用“spec.running: true”,KubeVirt将立即重启VMI。
为了允许用户状态的更大变化,引入了RunStrategy字段。这与Running是相互排斥的,因为它们有一些重叠的条件。目前定义了四种运行策略:
	Always:始终运行一个VMI,如果VMI故障,将生成一个新的。这与“spec.running: true”的行为相同。
	RerunOnFailure:如果前一个VMI在错误状态下失败,将生成一个新的VMI。如果客户端成功停止(例如从客户端内部关闭),则不会重新创建。
	Manual:VMI的运行状态完全由启动/停止/重启VM子资源端点控制。
	Halted:不存在VMI。如果一个VMI已经在运行,它将被停止。这与“spec.running: false”的行为相同。
Always运行策略的示例用法如下:
[root@k8s-master-node1 KubeVirt]# vi vm-fedora-always.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  labels:
    kubevirt.io/vm: vm-fedora
  name: vm-fedora
spec:
  runStrategy: Always
  template:
    metadata:
      labels:
        kubevirt.io/vm: vm-fedora
    spec:
      domain:
        resources:
          requests:
            memory: 1Gi
        devices:
          disks:
          - name: containerdisk
            disk:
              bus: virtio
      volumes:
      - name: containerdisk
        containerDisk:
          image: 10.24.2.7/library/fedora-virt:v1.0
创建VM:
[root@k8s-master-node1 KubeVirt]# kubectl apply -f vm-fedora-always.yaml 
virtualmachine.kubevirt.io/vm-fedora created
查看VM和VMI:
[root@k8s-master-node1 KubeVirt]# kubectl get vm
NAME     AGE   STATUS    READY
vm-fedora   25s    Running    True
[root@k8s-master-node1 KubeVirt]# kubectl get vmi
NAME     AGE   PHASE     IP          NODENAME       READY
vm-fedora   27s    Running    10.244.0.20   k8s-master-node1    True
可以看到,VM创建后的状态为Running,对应的VMI也已运行。
使用virtctl的start、stop和restart方法会调用VM的子资源会对VM的运行策略产生如下影响:
运行策略	start	stop	restart
Always		Halted	Always
RerunOnFailure		Halted	RerunOnFailure
Manual	Manual	Manual	Manual
Halted	Always		
使用virtctl工具对该VM执行stop操作:
[root@k8s-master-node1 KubeVirt]# virtctl stop vm-fedora
VM vm-fedora was scheduled to stop
此时VMI应该已经消失,再次查看VM和VMI:
[root@k8s-master-node1 KubeVirt]# kubectl get vm
NAME     AGE    STATUS    READY
vm-fedora   2m7s   Stopped     False
[root@k8s-master-node1 KubeVirt]# kubectl get vmi
No resources found in default namespace.
删除VM:
[root@k8s-master-node1 KubeVirt]# kubectl delete -f vm-fedora-always.yaml
5.存储
虚拟机镜像(磁盘)是启动虚拟机必不可少的部分,KubeVirt 中提供多种方式的虚拟机磁盘,虚拟机镜像(磁盘)使用方式非常灵活。
KubeVirt支持在spec.volumes下指定多种类型的卷:
	cloudInitConfigDrive:在通过给VM挂载一个文件系统,给cloud-init提供meta-data和user-data。
	cloudInitNoCloud:在通过给VM挂载一个文件系统,给cloud-init提供meta-data和user-data,生成的文件格式与ConfigDrive不同。
	containerDisk:指定一个包含qcow2或raw格式的Docker镜像,重启VM数据会丢失。
	dataVolume:动态创建一个PVC,并用指定的磁盘映像填充该PVC,重启vm数据不会丢失。
	emptyDisk:从宿主机上分配固定容量的空间,映射到VM中的一块磁盘,与emptyDir一样,emptyDisk的生命周期与VM等同,重启VM数据会丢失。
	ephemeral:在VM启动时创建一个临时卷,VM关闭后自动销毁,临时卷在不需要磁盘持久性的任何情况下都很有用。
	hostDisk:在宿主机上创建一个img镜像文件给VM使用。重启VM数据不会丢失。
	persistentVolumeClaim:指定一个PVC创建一个块设备。重启VM数据不会丢失。
	secret:使用Kubernetes的secret来存储和管理一些敏感数据,比如密码,token,密钥等敏感信息,并把这些信息注入给VM,可以动态更新到Pod,但是不能修改Pod中生成的iso文件,更不能更新到VM。要想更新到VM,需重启VM。
	configMap:功能类似于secret,把configMap里的信息写入到iso磁盘中,挂给VM。
	serviceAccount:功能类似为secret,把serviceAccount里的信息写入到iso磁盘中,挂给VM。
	sysprep:以secret或configMap的形式,往VM写入sysprep。

虚拟机镜像采用容器镜像形式存放在镜像仓库中。将Linux发行版本的镜像文件存放到基础镜像的/disk目录内,镜像格式支持qcow2、raw、img。通过Dockerfile文件将虚拟机镜像制作成容器镜像,然后分别推送到不同的registry镜像仓库中。客户在创建虚拟机时,根据配置的优先级策略拉取registry中的虚拟机容器镜像。
编写Dockerfile:
[root@k8s-master-node1 KubeVirt]# vi images/Dockerfile
FROM scratch
ADD cirros-0.3.4-x86_64-disk.img /disk/
构建并上传镜像:
[root@k8s-master-node1 KubeVirt]# docker build -t 10.24.2.7/library/cirros-test:v1.0 -f images/Dockerfile images/
Sending build context to Docker daemon  911.4MB
Step 1/2 : FROM scratch
 ---> 
Step 2/2 : ADD cirros-0.3.4-x86_64-disk.img /disk/
 ---> 98a42855f27c
Successfully built 98a42855f27c
Successfully tagged 10.24.2.7/library/cirros-test:v1.0
[root@k8s-master-node1 KubeVirt]# docker push 10.24.2.7/library/cirros-test:v1.0
使用新构建的镜像,并将ContainerDisk作为临时磁盘附加到VM:
[root@k8s-master-node1 KubeVirt]# vi vmi-containerdisk.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachineInstance
metadata:
  labels:
    special: vmi-cirros
  name: vmi-cirros
spec:
  domain:
    devices:
      disks:
      - disk:
          bus: virtio
        name: containerdisk
    resources:
      requests:
        memory: 1024M
  terminationGracePeriodSeconds: 0
  volumes:
  - containerDisk:
      image: 10.24.2.7/library/fedora-virt:v1.0
    name: containerdisk
创建VMI:
[root@k8s-master-node1 KubeVirt]# kubectl apply -f vmi-containerdisk.yaml 
virtualmachineinstance.kubevirt.io/vmi-cirros created
查看VMI:
[root@k8s-master-node1 KubeVirt]# kubectl get vmi
NAME     AGE   PHASE     IP          NODENAME      READY
vmi-cirros   39s    Running    10.244.0.23   k8s-master-node1   True
删除VMI:
[root@k8s-master-node1 KubeVirt]# kubectl delete -f vmi-containerdisk.yaml
(2)cloudInitNoCloud
cloudInitNoCloud利用cloud-init对虚拟机做初始化,类似于nova中的configdrive,包含元数据meta-data和用户数据user-data。meta-data能实现一些固定功能设置,如主机名称,user-data则可以实现更多灵活的功能,如生成文件、执行脚本等。
如基于cloudInitNoCloud设置VMI密码:
[root@k8s-master-node1 KubeVirt]# vi vmi-cloud.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachineInstance
metadata:
  labels:
    special: vmi-fedora
  name: vmi-fedora
spec:
  domain:
    devices:
      disks:
      - disk:
          bus: virtio
        name: containerdisk
      - disk:
          bus: virtio
        name: cloudinitdisk
      rng: {}
    resources:
      requests:
        memory: 1024M
  terminationGracePeriodSeconds: 0
  volumes:
  - containerDisk:
      image: 10.24.2.7/library/fedora-virt:v1.0
    name: containerdisk
  - cloudInitNoCloud:
      userData: |-
        #cloud-config
        password: fedora
        chpasswd: { expire: False }
    name: cloudinitdisk
创建vmi:
[root@k8s-master-node1 KubeVirt]# kubectl apply -f vmi-cloud.yaml 
virtualmachineinstance.kubevirt.io/vmi-fedora created
查看vmi:
[root@k8s-master-node1 KubeVirt]# kubectl get vmi
NAME      AGE   PHASE     IP          NODENAME     READY
vmi-fedora   41s    Running    10.244.0.21   k8s-master-node1   True
创建VMI的时候通过cloudInitNoCloud设置系统密码为fedora,使用该密码连接VMI:
[root@k8s-master-node1 KubeVirt]# virtctl console vmi-fedora
Successfully connected to vmi-fedora console. The escape sequence is ^]

vmi-fedora login: fedora
Password: 
[fedora@vmi-fedora ~]$
使用“Ctrl + ]”组合即可退出console控制台界面。
删除VMI:
[root@k8s-master-node1 KubeVirt]# kubectl delete -f vmi-cloud.yaml
(3)emptyDisk
emptyDisk的工作原理类似于Kubernetes中的emptyDir,将分配一个额外的磁盘,emptyDisk的生命周期与VM等同。
例如为VMI额外挂载一块2Gi大小的磁盘:
[root@k8s-master-node1 KubeVirt]# vi vmi-emptydisk.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachineInstance
metadata:
  labels:
    special: vmi-fedora
  name: vmi-fedora
spec:
  domain:
    devices:
      disks:
      - disk:
          bus: virtio
        name: containerdisk
      - name: emptydisk
        disk:
          bus: virtio
      - disk:
          bus: virtio
        name: cloudinitdisk
    resources:
      requests:
        memory: 1024M
  terminationGracePeriodSeconds: 0
  volumes:
  - containerDisk:
      image: 10.24.2.7/library/fedora-virt:v1.0
    name: containerdisk
  - name: emptydisk
    emptyDisk:
      capacity: 2Gi
  - cloudInitNoCloud:
      userData: |-
        #cloud-config
        password: fedora
        chpasswd: { expire: False }
    name: cloudinitdisk
创建VMI:
[root@k8s-master-node1 KubeVirt]# kubectl get vmi
NAME      AGE   PHASE    IP          NODENAME      READY
vmi-fedora   2m4s   Running   10.244.0.25   k8s-master-node1   True
验证磁盘:
[root@k8s-master-node1 KubeVirt]# virtctl console vmi-fedora 
Successfully connected to vmi-fedora console. The escape sequence is ^]

vmi-fedora login: fedora
Password: 
[fedora@vmi-fedora ~]$ lsblk
NAME   MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
vda    252:0    0   4G  0 disk 
└─vda1 252:1    0   4G  0 part /
vdb    252:16   0   2G  0 disk 
vdc    252:32   0   1M  0 disk 
[fedora@vmi-fedora ~]$
可以看到VMI中挂载了一块磁盘vdb,大小为2G,说明emptyDisk挂载成功。
删除VMI:
[root@k8s-master-node1 KubeVirt]# kubectl delete -f vmi-emptydisk.yaml
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。