KubeVirt 安装和使用
一、KubeVirt 概述
KubeVirt 是 Kubernetes 的一个扩展,允许在 Kubernetes 集群中管理虚拟机(VM)。它将虚拟机工作负载与容器工作负载统一在同一个平台上,为传统应用提供云原生的管理能力。
核心特性
- 在 Kubernetes 中运行和管理虚拟机
- 支持热迁移(Live Migration)
- 支持 SR-IOV、MAC 地址池等高级网络功能
- 与容器网络(CNI)和容器存储(CSI)集成
- 提供 virtctl 命令行工具管理虚拟机
二、环境准备
2.1 前置条件
| 组件 | 最低版本 | 说明 |
|---|---|---|
| Kubernetes | v1.22+ | 需要启用 Webhook 和 CRD |
| kubectl | v1.22+ | 与集群版本匹配 |
| Containerd/Docker | 最新稳定版 | 容器运行时 |
| CPU | 2核+ | 需要支持硬件虚拟化 |
| 内存 | 4GB+ | 建议 8GB 以上 |
2.2 检查硬件虚拟化支持
# Linux 系统检查
grep -E '(vmx|svm)' /proc/cpuinfo
# 如果输出包含 vmx (Intel) 或 svm (AMD),则支持硬件虚拟化
2.3 加载 KVM 内核模块
# 加载 kvm 模块
modprobe kvm
# 加载 kvm_intel 或 kvm_amd
modprobe kvm_intel # Intel CPU
modprobe kvm_amd # AMD CPU
# 验证
lsmod | grep kvm
kubevirt 组件
1. virt-api(入口与网关)
virt-api 是 KubeVirt 的统一入口点,作为 Kubernetes API Server 的扩展运行。
- 准入控制(Webhook): 充当
Mutating和Validating进站拦截器。在 VM/VMI 资源写入 etcd 之前,负责对其进行字段校验、默认值注入和合规性审查。 - API 聚合(Aggregator): 通过 Kubernetes 聚合 API,对外暴露虚拟机专属的非声明式操作接口。例如:虚拟机的启动(start)、停止(stop)、重启(restart)、热迁移(migrate)、冷/热冻结(freeze/unfreeze)和软重启(softReboot)等。
- 控制流路由: 接收到用户的操作指令后,将其转发给对应的
virt-handler或通过修改 CRD 状态触发控制循环。
2. virt-controller(大脑与协同器)
virt-controller 是 KubeVirt 的核心控制器,以 Deployment 形式运行(通常为多副本主备高可用)。
- 生命周期协同(VM ➔ Pod): 采用基于 Informer 的 List-Watch 机制,时刻监听 VM、VMI 等自定义资源的变化。
- 声明式状态转换: 当用户创建一个 VMI(Virtual Machine Instance)时,
virt-controller会在集群中创建并调度一个对应的底层容器 Pod(通常被称为 Helper Pod ),将虚拟机的配置转换为 Pod 的规格(Spec),从而利用 Kubernetes 原生的调度器(Scheduler)来决定虚拟机在哪个节点上运行。 - 集群级监控: 监控集群中所有 VM/VMI 的期望状态与实际状态,确保两者最终一致。
3. virt-handler(节点代理与守护进程)
virt-handler 是运行在每个物理节点上的 DaemonSet 代理。它以高权限(hostNetwork + hostPID)运行,负责处理该节点上所有虚拟机的具体落地工作。
- 下发底层操作(通过 Unix Socket): 监听本节点上的 VMI 资源变化。当发现有 VMI 被调度到本节点时,它不直接操作底层 Libvirtd,而是通过域套接字(Domain Socket)与该 VMI 对应的
virt-launcher容器进行通信,命令其启动或停止底层的 Libvirt 虚拟机。 - 直接控制接口(Console / VNC): 响应来自
virt-api的直接控制请求,提供控制台接入(Console)、图形界面接入(VNC)、暂停/恢复(pause/unpause)等本节点 VM 级别的底层对接。 - 状态收集与上报: 收集本节点上虚拟机的实际运行状态(心跳、网络、磁盘等指标),并更新回 VMI 的 Status 字段。
4. virt-launcher(沙箱与生命周期终点)
每个 VMI 对应的 Pod 内部都运行着一个 virt-launcher 进程。一个虚拟机实例对应一个单独的 Pod 容器。
- 虚拟机沙箱隔离:
virt-launcher运行在 Pod 的网络和 PID 命名空间中,为 QEMU/KVM 进程提供外部容器隔离。 - Libvirtd 承载者: 在 Pod 内部拉起一个本地的
libvirtd守护进程,并由virt-launcher进程负责管理这个libvirtd的生命周期。 - QEMU/KVM 进程生命周期管理: 接收来自同一节点上
virt-handler的指令,通过本地的libvirtd去真正创建、销毁和维护底层的qemu-kvm虚拟机进程。 - 异常处理: 如果底层的 QEMU 进程异常退出,
virt-launcher会捕获该信号并负责终止当前 Pod,以便触发 Kubernetes 的重建/自愈机制。
💡 核心组件协同工作流示例(以启动一个 VM 为例)
- 用户 提交创建 VM 的请求 ➔
virt-api(校验并通过 webhook 注入默认值,写入 etcd)。 virt-controller监听到新 VM ➔ 创建对应的 VMI 和底层的 Helper Pod。- Kubernetes 调度器将该 Pod 调度到
Node-A。 Node-A上的virt-handler检测到本节点有新的 VMI 分配,开始与该 Pod 内的virt-launcher通信。virt-launcher在 Pod 内部启动本地libvirtd,并最终拉起qemu-kvm(虚拟机正式运行)。
三、安装 KubeVirt
3.1 使用 kubectl 安装(推荐)
# 设置 KubeVirt 版本号
export KUBEVIRT_VERSION="v1.8.4"
# 创建 namespace
kubectl create namespace kubevirt
# 部署 KubeVirt Operator
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-operator.yaml
# 创建 KubeVirt CR(Custom Resource)
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-cr.yaml
3.2 验证安装
# 查看 KubeVirt 相关 Pod
[root@master1 ~ ]# kubectl get pods -n kubevirt
NAME READY STATUS RESTARTS AGE
virt-api-85bb9b4ffb-67556 1/1 Running 2 (24h ago) 4d
virt-api-85bb9b4ffb-8749q 1/1 Running 3 (24h ago) 4d1h
virt-controller-7cf5b94d5f-59kvl 1/1 Running 5 (24h ago) 4d1h
virt-controller-7cf5b94d5f-cb8lk 1/1 Running 3 (24h ago) 4d1h
virt-handler-2m2nl 1/1 Running 0 69m
virt-handler-79rcl 1/1 Running 3 (24h ago) 4d1h
virt-handler-8k829 1/1 Running 3 (24h ago) 4d1h
virt-handler-kjslj 1/1 Running 2 (24h ago) 4d1h
virt-handler-mv8z8 1/1 Running 1 (24h ago) 4d1h
virt-operator-576b8d796c-47m2s 1/1 Running 8 (24h ago) 4d2h
virt-operator-576b8d796c-gr9dl 1/1 Running 6 (24h ago) 4d2h
# 查看 KubeVirt CR 状态
[root@master1 ~ ]# kubectl get kubevirt.kubevirt.io -n kubevirt
NAME AGE PHASE
kubevirt 4d2h Deployed
# 等待所有组件就绪
kubectl wait --for=condition=Available --timeout=300s \
kubevirt/kubevirt -n kubevirt
预期输出:
NAME AGE PHASE
kubevirt 5m Deployed
四、安装 virtctl 客户端工具
4.1 下载 virtctl
# 设置版本
export KUBEVIRT_VERSION="v1.8.4"
# Linux 系统下载
wget https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/virtctl-${KUBEVIRT_VERSION}-linux-amd64
4.2 安装 virtctl
# Linux/macOS
[root@master1 ~ ]# chmod +x virtctl-v1.8.4-linux-amd64
[root@master1 ~ ]# mv virtctl-v1.8.4-linux-amd64 /usr/local/bin/virtctl
4.3 验证安装
[root@master1 ~ ]# virtctl version --client
Client Version: version.Info{
GitVersion:"v1.8.4", GitCommit:"dfaa398f086692d19e01be21d83a01036e07815f", GitTreeState:"clean", BuildDate:"2026-06-16T13:26:36Z", GoVersion:"go1.24.9 X:nocoverageredesign", Compiler:"gc", Platform:"linux/amd64"}
五、基本使用
VirtualMachine
vm,是在vmi基础之上 更完善的逻辑控制,可以认为是控制器;vmi(VirtualMachiineInstance),是虚拟机实例。vm与vmi之间的关系类似于deployment与pod之间的关系。
5.1 创建虚拟机
[root@master1 ~/kubevirt/test ]# cat test.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: vm-cirrors
namespace: default
spec:
runStrategy: Halted
template:
metadata:
labels:
kubevirt.io/vm: vm-cirros
spec:
domain:
devices:
disks:
- name: containerdisk
disk:
bus: virtio
- name: cloudinitdisk
disk:
bus: virtio
machine:
type: ""
resources:
requests:
memory: 64M
terminationGracePeriodSeconds: 0
volumes:
- name: containerdisk
containerDisk:
image: quay.io/kubevirt/cirros-container-disk-demo:latest
- name: cloudinitdisk
cloudInitNoCloud:
userDataBase64: I2Nsb3VkLWNvbmZpZwpwYXNzd29yZDogZ2xvbzEyMwpjaHBhc3N3ZDogeyBleHBpcmU6IEZhbHNlIH0KcnVuY21kOgogIC0gZWNobyAiSGVsbG8gS3ViZVZpcnQhIiA+IC9yb290L3Rlc3QudHh0Cg==
[root@master1 ~/kubevirt/test ]# kubectl get vm
NAME AGE STATUS READY
vm-cirrors 74s Stopped False
当前位置: ~/kubevirt/test
[root@master1 ~/kubevirt/test ]# virtctl start vm-cirrors
VM vm-cirrors was scheduled to start
[root@master1 ~/kubevirt/test ]# kubectl get vm
NAME AGE STATUS READY
vm-cirrors 94s Starting False
[root@master1 ~/kubevirt/test ]# kubectl get vmi
NAME AGE PHASE IP NODENAME READY
vm-cirrors 31s Running 172.20.166.137 node1 True
False
# 一个vmi对象对应一台虚拟机,实际上这台虚拟机是在一个特殊的kubernetes pod中,该pod里有virt-launcher、liibvirtd和qemu进程,因此也就是可以看作kubevirt把虚拟机运行在pod中了。
[root@master1 ~/kubevirt/test ]# kubectl get pods -A --field-selector spec.nodeName=node1 -owide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
default virt-launcher-vm-cirrors-cl5zq 3/3 Running 0 6m26s 172.20.166.137 node1 <none> 1/1
kube-system calico-node-dvmbz 1/1 Running 31 (27h ago) 159d 192.168.11.7 node1 <none> <none>
kube-system node-local-dns-nldq9 1/1 Running 31 (27h ago) 159d 192.168.11.7 node1 <none> <none>
kubevirt virt-handler-2m2nl 1/1 Running 0 4h8m 172.20.166.143 node1 <none> <none>
metallb-system speaker-292j5 1/1 Running 0 4h8m 192.168.11.7 node1 <none> <none>
traefik traefik-5mwff 1/1 Running 15 (27h ago) 89d 172.20.166.141 node1 <none> <none>
[root@master1 ~/kubevirt/test ]# virtctl console vm-cirrors
Successfully connected to vm-cirrors console. Press Ctrl+] or Ctrl+5 to exit console.
#
为虚拟机创建service 并访问
[root@master1 ~ ]# virtctl expose vm vm-cirrors --name vmiservice-node --target-port 22 --port 24 --type NodePort
Service vmiservice-node successfully created for vm vm-cirrors
[root@master1 ~ ]# ssh cirros@10.68.86.25 -p 24
cirros@10.68.86.25's password:
$
当前位置: ~
[root@master1 ~ ]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 159d
vmiservice-node NodePort 10.68.86.25 <none> 24:32482/TCP 6s
# vm-ubuntu.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: ubuntu-vm
namespace: default
spec:
running: true
template:
metadata:
labels:
kubevirt.io/vm: ubuntu-vm
spec:
domain:
cpu:
cores: 2
memory:
guest: 2Gi
devices:
disks:
- name: rootdisk
disk:
bus: virtio
- name: cloudinitdisk
disk:
bus: virtio
volumes:
- name: rootdisk
containerDisk:
image: kubevirt/ubuntu-container-disk:latest
- name: cloudinitdisk
cloudInitNoCloud:
userData: |-
#cloud-config
password: ubuntu
chpasswd: {
expire: False }
ssh_authorized_keys:
- ssh-rsa AAAA...your-key
kubectl apply -f vm-ubuntu.yaml
在 KubeVirt 中,虚拟机(VirtualMachine/VMI)对存储卷的使用分为两个层次:Kubernetes 集群侧的存储提供(Volume) 以及 虚拟机内部感知的磁盘形态(Disk)。
以下是 KubeVirt 中最常用的存储卷类型(Volume Types)及其典型应用场景:
persistentVolumeClaim(PVC)
这是生产环境中最核心、最常用的存储类型。它直接对接 Kubernetes 的存储生态(CSI 驱动),支持从本地磁盘、网络文件系统(NFS)、到分布式存储(如 Ceph、OpenEBS 等)。
- 使用方式: 可以挂载为文件系统模式(Filesystem)(在 PVC 上创建一个磁盘镜像文件,如
disk.img)或原始块设备模式(Block)(直接将块设备映射给虚拟机,性能最佳)。 - 应用场景: 虚拟机的系统盘、高性能数据盘、需要支持持久化和跨节点热迁移(通常需要
ReadWriteMany/ RWX 属性)的场景。
containerDisk
KubeVirt 独创的一种极其便利的存储类型。它允许将虚拟机的操作系统镜像(如 qcow2/raw 格式)直接打包到 Docker 镜像中,并托管在标准的 Container Registry(如 Harbor、Docker Hub)里。
- 使用方式: 虚拟机启动时,会像拉取容器镜像一样拉取这个
containerDisk镜像,并从中读取操作系统。 - 应用场景: 只读系统盘、快速水平扩展的无状态虚拟机(如基础 CI/CD 节点)、共享的黄金镜像(Golden Image)。由于其默认只读或采用临时写时复制(Ephemeral),它非常适合用作虚拟机的启动模板。
dataVolume(DV)
dataVolume 是由 KubeVirt 的子项目 CDI (Containerized Data Importer) 提供的增强型存储管理方式。它在内部本质上还是通过自动创建 PVC 来承载数据,但极大简化了镜像的导入过程。
- 使用方式: 你可以直接在
dataVolume中定义数据源(Source),CDI 会自动拉起一个 importer Pod,帮你把数据转换并写入 PVC。 - 支持的内置数据源:
http/s3:直接从外网或对象存储下载qcow2/raw镜像。registry:从 Container Registry 导入containerDisk的内容。pvc:克隆现有的另一个 PVC(快照克隆)。blank:创建一个指定大小的空白物理磁盘。
- 应用场景: 从零初始化虚拟机、批量通过基础镜像克隆并部署新 VM。
cloudInitNoCloud
这是一个特殊的配置卷,专门用来向虚拟机注入初始化配置(网络、主机名、用户密码、SSH 密钥、初始脚本等)。
- 使用方式: KubeVirt 会自动将用户定义的
userData或networkData转换为标准的cloud-initISO 镜像,并挂载为虚拟机的只读光驱。 - 应用场景: Linux 虚拟机的自动化首次开机配置、网络动态分配、公钥注入。
emptyDir
与 Kubernetes 原生的 emptyDir 完全一致,是一个生命周期与当前 Pod(虚拟机实例)绑定的临时空目录。
- 使用方式: 虚拟机内部会看到一个空白的、未格式化的物理磁盘。当虚拟机 Pod 被重建、漂移或删除时,其中的数据会彻底丢失。
- 应用场景: 虚拟机的临时缓存盘、Swap 交换分区空间、无状态的 scratch 空间。
hostDisk
直接映射宿主机(Node)本地的文件路径到虚拟机中(类似于 Kubernetes 的 hostPath)。
使用方式: 在宿主机的指定路径下创建或使用一个独立的虚拟磁盘文件。
应用场景: 单节点测试环境、边缘计算节点(节点数量少、不需要热迁移、追求极简的本地存储性能)。
-
⚠️ 注意: 使用
hostDisk会限制虚拟机的调度灵活性,导致虚拟机无法在集群内热迁移。
📝 典型配置结构示例 (YAML 视角)
在定义 VM CRD 时,这些存储卷在 spec.template.spec 里的基本映射逻辑如下:
YAML
spec:
domain:
devices:
disks:
- name: boot-disk
disk: {} # 虚拟机内看作标准磁盘
- name: init-data
cdrom: {} # 虚拟机内看作光驱
volumes:
- name: boot-disk
persistentVolumeClaim: # 对应 K8s 集群侧的存储卷类型
claimName: my-ubuntu-pvc
- name: init-data
cloudInitNoCloud:
userData: |
#cloud-config
password: my-secure-password
chpasswd: { expire: False }
5.2 查看虚拟机
# 查看虚拟机状态
kubectl get vmi
# 查看虚拟机详情
kubectl describe vmi ubuntu-vm
# 使用 virtctl 查看
virtctl get vmi
5.3 使用 virtctl 暴露服务
# 暴露 SSH 服务(NodePort)
virtctl expose virtualmachineinstance ubuntu-vm \
--name=ubuntu-vm-ssh \
--port=22 \
--target-port=22 \
--type=NodePort
# 暴露 HTTP 服务(ClusterIP)
virtctl expose virtualmachineinstance ubuntu-vm \
--name=ubuntu-vm-http \
--port=80 \
--target-port=80 \
--type=ClusterIP
# 查看暴露的服务
kubectl get svc
5.4 使用 virtctl 管理虚拟机
# 启动虚拟机
virtctl start ubuntu-vm
# 停止虚拟机
virtctl stop ubuntu-vm
# 重启虚拟机
virtctl restart ubuntu-vm
# 暂停虚拟机
virtctl pause vmi ubuntu-vm
# 恢复虚拟机
virtctl unpause vmi ubuntu-vm
# 查看虚拟机控制台(VNC)
virtctl vnc ubuntu-vm
# 查看虚拟机串口控制台
virtctl console ubuntu-vm
# SSH 连接虚拟机
virtctl ssh ubuntu-vm --username ubuntu
# 热迁移虚拟机
virtctl migrate ubuntu-vm
六、卸载 KubeVirt
# 删除 KubeVirt CR
kubectl delete -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-cr.yaml
# 删除 KubeVirt Operator
kubectl delete -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-operator.yaml
# 删除 namespace
kubectl delete namespace kubevirt