KubeVirt 简介
KubeVirt 是一个在 Kubernetes 上运行虚拟机(VM)的开源项目。它让 Kubernetes 不仅能管理容器,还能管理虚拟机,实现容器和虚拟机的混合管理。
之前只写了 KubeVirt 怎么安装,一直没写 KubeVirt 是什么,可以做什么,今天简单写下。
KVM
介绍 KubeVirt 之前先回顾一下 KVM。
KVM(Kernel-based Virtual Machine) 是 Linux 内核 内置的一个虚拟化解决方案,它可以将 Linux 变成一个完整的虚拟机管理程序(Hypervisor),可以在 Linux 系统上运行多个完全隔离的虚拟机(VM)。
在 Linux 上创建 KVM 虚拟机本质上就是启动一个 KVM 相关的进程:
[root@remote-rhel ~]# virsh list
Id Name State
----------------------------
1 DNS_Server running
3 ldap running
[root@remote-rhel ~]# ps -ef | grep kvm | grep ldap
qemu 2206 1 2 Jan28 ? 23:35:26 /usr/libexec/qemu-kvm -name guest=ldap,debug-threads=on -S -object {"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain-3-ldap/master-key.aes"} -machine pc-q35-rhel9.4.0,usb=off,dump-guest-core=off,memory-backend=pc.ram,hpet=off,acpi=on -accel kvm -cpu host,migratable=on -m size=2097152k -object {"qom-type":"memory-backend-ram","id":"pc.ram","size":2147483648} -overcommit mem-lock=off -smp 2,sockets=2,cores=1,threads=1 -uuid 1be408f4-f8f1-4a49-a878-5e8acd73cfa2 -no-user-config -nodefaults -chardev socket,id=charmonitor,fd=30,server=on,wait=off -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc,driftfix=slew -global kvm-pit.lost_tick_policy=delay -no-shutdown -global ICH9-LPC.disable_s3=1 -global ICH9-LPC.disable_s4=1 -boot strict=on -device {"driver":"pcie-root-port","port":16,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x2"} -device {"driver":"pcie-root-port","port":17,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x2.0x1"} -device {"driver":"pcie-root-port","port":18,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x2.0x2"} -device {"driver":"pcie-root-port","port":19,"chassis":4,"id":"pci.4","bus":"pcie.0","addr":"0x2.0x3"} -device {"driver":"pcie-root-port","port":20,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x2.0x4"} -device {"driver":"pcie-root-port","port":21,"chassis":6,"id":"pci.6","bus":"pcie.0","addr":"0x2.0x5"} -device {"driver":"pcie-root-port","port":22,"chassis":7,"id":"pci.7","bus":"pcie.0","addr":"0x2.0x6"} -device {"driver":"pcie-root-port","port":23,"chassis":8,"id":"pci.8","bus":"pcie.0","addr":"0x2.0x7"} -device {"driver":"pcie-root-port","port":24,"chassis":9,"id":"pci.9","bus":"pcie.0","multifunction":true,"addr":"0x3"} -device {"driver":"pcie-root-port","port":25,"chassis":10,"id":"pci.10","bus":"pcie.0","addr":"0x3.0x1"} -device {"driver":"pcie-root-port","port":26,"chassis":11,"id":"pci.11","bus":"pcie.0","addr":"0x3.0x2"} -device {"driver":"pcie-root-port","port":27,"chassis":12,"id":"pci.12","bus":"pcie.0","addr":"0x3.0x3"} -device {"driver":"pcie-root-port","port":28,"chassis":13,"id":"pci.13","bus":"pcie.0","addr":"0x3.0x4"} -device {"driver":"pcie-root-port","port":29,"chassis":14,"id":"pci.14","bus":"pcie.0","addr":"0x3.0x5"} -device {"driver":"qemu-xhci","p2":15,"p3":15,"id":"usb","bus":"pci.2","addr":"0x0"} -device {"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.3","addr":"0x0"} -blockdev {"driver":"file","filename":"/data/vm_pool/ldap.qcow2","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"} -blockdev {"node-name":"libvirt-1-format","read-only":false,"driver":"qcow2","file":"libvirt-1-storage","backing":null} -device {"driver":"virtio-blk-pci","bus":"pci.4","addr":"0x0","drive":"libvirt-1-format","id":"virtio-disk0","bootindex":1} -netdev {"type":"tap","fd":"31","vhost":true,"vhostfd":"33","id":"hostnet0"} -device {"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:74:b9:92","bus":"pci.1","addr":"0x0"} -chardev pty,id=charserial0 -device {"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":0} -chardev socket,id=charchannel0,fd=28,server=on,wait=off -device {"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"org.qemu.guest_agent.0"} -device {"driver":"usb-tablet","id":"input0","bus":"usb.0","port":"1"} -audiodev {"id":"audio1","driver":"none"} -vnc 127.0.0.1:2,audiodev=audio1 -device {"driver":"virtio-vga","id":"video0","max_outputs":1,"bus":"pcie.0","addr":"0x1"} -global ICH9-LPC.noreboot=off -watchdog-action reset -device {"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.5","addr":"0x0"} -object {"qom-type":"rng-random","id":"objrng0","filename":"/dev/urandom"} -device {"driver":"virtio-rng-pci","rng":"objrng0","id":"rng0","bus":"pci.6","addr":"0x0"} -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny -msg timestamp=on
容器
说完 KVM 我们再说说容器。
容器(Container) 是一种轻量级、可移植的虚拟化技术,它可以在同一个操作系统内核上运行多个独立的应用程序,而不会相互影响。容器提供了进程级别的隔离,但不会像传统虚拟机(VM)那样运行完整的操作系统,因此更高效、更轻量。
所以容器的本质也是运行进程,只不过通过 Linux 的一些功能(Namespace、Cgroups、UnionFS、Container Runtime 等)实现了隔离,但是运行一个容器就是运行一个进程,只不过容器在实现隔离的时候性能损耗较少。
[root@prometheus ~]# podman exec -it prometheus ps -ef
PID USER TIME COMMAND
1 nobody 4h58 /bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/prometheus --web.enable-admin-api --web.enable-lifecycle --web.console.libraries=/usr/share/prometheus/console_libraries --web.console
20 nobody 0:00 ps -ef
Kubernetes
再说说 Kubernetes。现在容器很强也很火,Kubernetes 现在也是容器编排的事实标准,但是 Kubernetes 的出现是解决多节点的容器编排,Kubernetes 有很多优点:
- 支持自动化部署,回滚
- 天生支持高可用和自愈
- 支持水平垂直扩展
- 自带启动、存活和就绪探针,支持健康检测
- 自带 Service,还可以部署 Ingress,实现四、七层的负载均衡
还有很多,不一一列举了。可以看到 Kubernetes 很强大,但是想要把业务运行在 Kubernetes 就必须要把原有的业务做容器化处理。
如果有一个业务,现在没办法立刻容器化或者由于其他的一些原因无法去做容器化。那么就很可惜,没办法享受到 Kubernetes 带来的便利,有没有一种办法让 Kubernetes 来管理虚拟机呢?答案是有。
KubeVirt
Kubernetes 为什么能够运行虚拟机?
上边说了 KVM 和 容器。KVM 是一个进程,容器又能以一个很低的性能损耗来运行进程,那么容器运行 KVM 进程不就相当于容器运行虚拟机了么,容器和 KVM 都是比较成熟的技术了,所以容器运行虚拟机的稳定性也不是问题。
既然容器能运行虚拟机,Kubernetes 又是容器编排工具,那 Kubernetes 运行虚拟机也不是问题,麻烦的是怎么透过 POD 管理虚拟机。
为了解决这个问题,在 2016 年就有三个 Red Hat 的工程师探索 Kubernetes 运行虚拟机是否可行。然后,开源项目 KubeVirt 就出现了,简单说说这个项目的历史:
时间 | 事件 & 发展 |
---|---|
2016 | Red Hat 内部启动 KubeVirt,探索在 Kubernetes 运行虚拟机的可能性。 |
2017 年 6 月 | Red Hat 开源 KubeVirt,提交到 GitHub,项目正式启动。 |
2018 年 | Red Hat 发布的 OpenShift 版本开始推出容器原生虚拟化解决方案(OpenShift Virtualization) |
2019 年 9 月 | 进入 CNCF(云原生计算基金会)Sandbox 阶段,成为云原生生态的一部分。 |
2022 年 4 月 | CNCF 将 KubeVirt 提升为孵化项目,成为主流 Kubernetes 生态的一部分。 |
2025+ | 广泛集成到 OpenShift、OKD、Rancher,支持 K8s 上的混合应用(VM + Pod)。 |
KubeVirt 可以实现什么?
图片来源官网:https://kubevirt.io/user-guide/architecture/
在上图可以看到有了 KubeVirt 后,虚拟机和容器共存变成了可能,这样有以下好处:
- 通过 Kubernetes 统一管理虚拟机和容器,不再需要虚拟机一个平台,容器一个平台,简化了管理
- 虚拟机可以通过 K8S 的探针检查和容器编排实现自动回复和高可用
- 虚拟机可以使用 K8S 的 Service 和 Ingress 的四层和七层代理
- 可以先以虚拟机的方式将业务迁移到 K8S,作为业务上 K8S 的过度
- 使用的组件均是开源的,特别是 KVM,多数云和虚拟化厂商都是以 KVM 为基础,不存在厂商绑定。
KubeVirt 架构
图片来源官网:https://kubevirt.io/user-guide/architecture/
通过官网的图可以看到,KubeVirt 为 Kubernetes 添加了一些新的资源,在管理节点添加了 virt-api
和 virt-controller
,在工作节点添加了 virt-handler
。
-
virt-api
作用:virt-api
是 KubeVirt 的 API 服务,提供 VM 相关的 REST API(类似kube-apiserver
)接口。 -
virt-controller
作用:virt-controller
是 KubeVirt 的核心控制器,负责调度和管理虚拟机(VM & VMI)(类似kube-controller-manager
)。 -
virt-handler
作用:virt-handler
是 KubeVirt 的 VM 运行时管理器,负责创建管理虚拟机(VMI)(类似kubelet
)
组件 | 作用 | 关键功能 |
---|---|---|
virt-controller |
负责 VM 生命周期管理 | 创建、删除、调度 VMI;Live Migration;协调 virt-handler |
virt-api |
提供 KubeVirt API 接口 | 处理 kubectl 和 REST API 请求;权限控制;与 virt-controller 交互 |
virt-handler |
运行 VM | 在节点上创建 KVM/QEMU 进程,管理 VM 运行状态,执行迁移 |
所以创建流程如下:
- 用户通过
kubectl
通过API Server
对virt-api
发起创建虚拟机任务; virt-controller
监听VirtualMachine
资源(通过 Kubernetes 的 watcher 机制),创建VMI
资源,并通过kube-scheduler
进行调度;virt-handler
通过 watcher 机制监听VMI
资源,来创建对应的virt-launcher
Pod,virt-launcher
Pod 内运行虚拟机。
virt-launcher
Pod 内的容器运行 virt-launcher
进程来启动并控制 libvirtd
和 qemu
进程。
[root@base-k8s-master-1 ~]# kubectl exec -it virt-launcher-my-vm-hszfs -- /bin/bash
bash-5.1$ ps -ef
UID PID PPID C STIME TTY TIME CMD
qemu 1 0 0 Mar04 ? 00:00:00 /usr/bin/virt-launcher-monitor --qemu-timeout 285s --name my-vm --uid 76d04b17-b1e1-4abd-94da-c290fbfc2e93 --namespace default --kubevirt-share-dir /var/run/kubevirt --ephemeral-disk-d
qemu 12 1 0 Mar04 ? 00:00:32 /usr/bin/virt-launcher --qemu-timeout 285s --name my-vm --uid 76d04b17-b1e1-4abd-94da-c290fbfc2e93 --namespace default --kubevirt-share-dir /var/run/kubevirt --ephemeral-disk-dir /var/
qemu 23 12 0 Mar04 ? 00:00:00 /usr/sbin/virtlogd -f /etc/libvirt/virtlogd.conf
qemu 24 12 0 Mar04 ? 00:00:22 /usr/sbin/virtqemud -f /var/run/libvirt/virtqemud.conf
qemu 68 1 0 Mar04 ? 00:05:05 /usr/libexec/qemu-kvm -name guest=default_my-vm,debug-threads=on -S -object {"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/run/kubevirt-private/libvirt/qemu/lib/dom
qemu 80 0 0 04:35 pts/0 00:00:00 /bin/bash
qemu 86 80 0 04:35 pts/0 00:00:00 ps -ef
这里多说一个,关闭虚拟机可以通过
virtctl stop
来关闭,除了这个方法通过kubectl delete pod
正常删除virt-launcher
Pod 也是正常关闭,删除时 Pod 会给内部容器发送 SIGTERM 信号,SIGTERM 信号会触发虚拟机正常关机,强制关机就在删除时加--force
参数。