容器云原生虚拟化-Kubevirt
【摘要】 Kubevirt 是 Red Hat 开源的以容器方式运行虚拟机的项目,通过 CRD 的方式来管理虚拟机实例,它的所有概念都和一般的 Kubernetes 容器应用差不多,不需要增加学习成本。1. Kubevirt 架构设计Kubevirt 主要实现了下面几种资源,以实现对虚拟机的管理:VirtualMachineInstance(VMI) : 类似于 kubernetes Pod,是管理虚...
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 等。
虚拟机创建流程
- client 发送创建VMI命令达到 k8s API server.
- K8S API 创建VMI 对象
- virt-controller监听到VMI创建时,根据VMI spec生成pod spec文件,创建pods
- k8s调度创建pods
- virt-controller监听到pods创建后,根据pods的调度node,更新VMI 的nodeName
- 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直接作为块设备给虚拟机少了本地文件系统层 单从存储效率讲都能提高不少。至于librbd和rbd.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
迁移虚拟机:
功能门参考:https://kubevirt.io/user-guide/virtual_machines/disks_and_volumes/#sharing-directories-with-vms
[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)