KubeVirt 让你在已有的 Kubernetes 集群上直接运行 虚拟机,把容器与 VM 放进同一套调度、网络与存储体系里统一编排。本文从架构到落地,手把手演示如何用 KubeVirt 把传统负载平滑搬上 K8s,而不必另起一套 OpenStack。
一、KubeVirt 是什么:架构与核心概念
KubeVirt 是 CNCF 的沙箱(已毕业为孵化)项目,本质是一组运行在 K8s 里的自定义控制器与 CRD。它利用 K8s 的声明式 API 扩展机制,把「虚拟机」变成和 Pod 平级的头等公民。底层虚拟机仍由每个节点上的 libvirt + QEMU/KVM 承载,但调度、生命周期、网络、存储全部交给 K8s 控制面统一管理。
理解三个核心对象,就掌握了 KubeVirt 的骨架:
- VirtualMachineInstance(VMI):等价于「运行中的一台虚拟机」,类似 Pod,是真正被调度到节点上跑起来的实体。
- VirtualMachine(VM):VMI 的「模板 + 状态机」,定义这台机器的规格,并维护开机/关机状态,类似 Deployment 与 Pod 的关系。
- VirtualMachineInstanceReplicaSet(VMIRS):虚拟机的多副本集合,适合无状态、可水平扩展的 VM 负载。
控制面关键组件是 virt-controller(负责把 VM 调成 VMI)、virt-api(暴露 KubeVirt 的 API)、以及每个节点上的 virt-handler(真正调用 libvirt 起 QEMU)。K8s 自身的 etcd 与调度器依然是大脑,想深入底层存储与高可用可参考 etcd 分布式键值存储实战。
一个反直觉但重要的细节:每台 VMI 在底层都会对应一个同名 Pod(由 virt-launcher 承载)。调度器其实是在给这个 Pod 安排节点,Pod 里再起 QEMU 进程。因此 K8s 的全部调度能力——节点亲和、污点容忍、资源配额、优先级抢占——对虚拟机同样生效,你不用学一套新调度语法。
二、环境准备与安装
KubeVirt 要求节点支持硬件虚拟化(/dev/kvm 存在)。云上 ECS 通常需在实例规格里开启「嵌套虚拟化」或选用带 VT-x 的机型;轻量边缘环境可参考 k3s 轻量 Kubernetes 实战 搭建低资源集群。安装只需两条 kubectl 命令:
# 1) 部署 KubeVirt Operator 与 CR(以 v1.2 为例)
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/v1.2.0/kubevirt-operator.yaml
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/v1.2.0/kubevirt-cr.yaml
# 2) 等待控制面就绪
kubectl -n kubevirt wait kv kubevirt --for condition=Available --timeout=300s
# 3) 安装 virtctl 命令行(VM 控制台/热迁移依赖它)
export VERSION=$(kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath='{.spec.installedVersion}')
echo "KubeVirt 版本: $VERSION"
三、运行你的第一台虚拟机
下面定义一个最简 VM:2 核 4G、从容器镜像中的云初始化磁盘启动。提交后 KubeVirt 会自动派生出 VMI 并调度到节点。
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: test-vm
spec:
runStrategy: Always # Always/RerunOnFailure/Manual
template:
metadata:
labels: { kubevirt.io/vm: test-vm }
spec:
domain:
cpu: { cores: 2 }
resources:
requests: { memory: 4Gi }
devices:
disks:
- name: rootdisk
disk: { bus: virtio }
volumes:
- name: rootdisk
containerDisk:
image: quay.io/containerdisks/fedora:39
创建并进入控制台:
kubectl apply -f test-vm.yaml
kubectl get vmi # 查看虚拟机实例运行状态
virtctl console test-vm # 进入串行控制台
virtctl ssh fedora@test-vm # 通过 virtctl 内置 SSH 登录
三(续)、用 cloud-init 注入初始化配置
裸镜像启动后还需装软件、写用户,生产环境应交给 cloud-init。在 VM 模板里挂一个 cloudInitNoCloud 卷,即可注入主机名、SSH 公钥与开机脚本:
volumes:
- name: cloudinit
cloudInitNoCloud:
userData: |
#cloud-config
hostname: test-vm
users:
- name: fedora
ssh_authorized_keys:
- ssh-rsa AAAAB3...your-key
runcmd:
- dnf install -y nginx
- systemctl enable --now nginx
这样 VM 一开机就已经带好账号与基础服务,配合 GitHub Actions CI/CD 流水线 还能把 VM 模板的渲染也纳入自动化。
四、镜像与存储:用 DataVolume 导入磁盘
生产环境很少用 containerDisk(它每次启动都重新拉镜像、不可持久化)。更规范的做法是用 DataVolume(DV) + CDI 把 qcow2/raw 镜像导入 PVC,实现持久化启动盘。Kubernetes Pod/Deployment/Service 基础 里的 PVC 概念在这里完全复用。
除了从容器镜像仓库拉取,CDI 还支持从外部 http/https 直链或对象存储(S3)导入现成 qcow2,也支持基于已有 PVC clone 快速派生多台同构机器:
# 方式 A:从公网镜像直链导入
spec:
source:
http: { url: "https://cloud.centos.org/centos/9-stream/x86_64/images/..." }
---
# 方式 B:克隆已有 PVC 派生新盘
spec:
source:
pvc: { name: golden-image, namespace: default }
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: fedora-dv
spec:
source:
registry:
url: docker://quay.io/containerdisks/fedora:39
pvc:
accessModes: [ ReadWriteOnce ]
resources:
requests: { storage: 10Gi }
---
# 在 VM 的 volumes 里改用 dataVolume 引用
volumes:
- name: rootdisk
dataVolume: { name: fedora-dv }
五、网络与对外访问
默认 VMI 接入 K8s 的 Pod 网络,拥有独立 Pod IP,可直接被集群内 Service 转发。若要从外部 SSH 或暴露 Web 服务,给 VMI 套一个 Service 即可,原理与 Nginx 反向代理与负载均衡 中暴露后端一致。需要对外域名可配合 ExternalDNS 自动管理 DNS 解析 实现 Ingress 域名自动注册。
当一台 VM 需要多张网卡(例如管理网与业务网隔离),可借助 Multus 挂第二个网络接口;临时调试则直接用 virtctl port-forward 把本地端口映射到 VMI 内部,免去每次改 Service:
# 本地 8080 转发到 VM 的 80 端口
virtctl port-forward vm/test-vm 8080:80
# 多网卡:在 spec.domain.devices.interfaces 增加 bridge 类型接口
interfaces:
- name: net1
bridge: {}
networks:
- name: net1
multus: { networkName: business-net }
apiVersion: v1
kind: Service
metadata:
name: test-vm-ssh
spec:
selector: { kubevirt.io/vm: test-vm }
ports:
- protocol: TCP
port: 22
targetPort: 22
六、生命周期运维:快照与热迁移
KubeVirt 把 VM 的运维动作也声明式化。快照用 VirtualMachineSnapshot,跨节点搬家用 Live Migration(内存热迁移,业务几乎无感)。
# 快照
kubectl apply -f - <
六(续)、节点亲和与资源规划
VM 对节点资源更「挑剔」:它独占分配的内存,错误的调度会让节点内存超卖。建议通过节点亲和把虚拟机集中到带 KVM 的专用节点池,并用 nodeSelector 隔离;对延迟敏感的业务可开启大页内存(HugePages)降低 TLB 缺失:
spec:
template:
spec:
nodeSelector: { node-role.kubernetes.io/vm: "true" }
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: feature.node.kubernetes.io/virtualization-kvm
operator: In
values: ["true"]
domain:
memory:
hugepages: { pageSize: 2Mi }
资源规划不当会引发节点压力与驱逐,相关排障思路可对照 K8s 节点 NotReady 排障 一文。
七、融入 K8s 可观测与治理生态
KubeVirt 的精髓在于「一套底座管两种负载」。VM 的 CPU、内存、网络指标由节点导出器统一采集,直接接入 Prometheus + Grafana 监控告警;安全基线可用 Kyverno 策略即代码 或 OPA 统一策略授权 约束 VM 规格(例如禁止特权、限制镜像来源);整套 VM 定义还能纳入 ArgoCD 的 GitOps 流水线,实现虚拟机配置的版本化与一键回滚。
八、典型排障清单
| 现象 | 常见根因 | 处理命令 |
|---|---|---|
| VMI 一直 Pending | 节点无 /dev/kvm 或资源不足 | kubectl describe vmi 看 Events |
| 启动报软虚拟化 | 嵌套虚拟化未开 | 主机开启 VT-x 后重启 |
| DataVolume 卡 Import | CDI 存储类不支持 | kubectl get dv -o yaml 查 phase |
| 控制台连不上 | virtctl 未装 / 串口未配 | 确认 Virtio 串行设备 |
九、适用边界与选型建议
| 适合 KubeVirt | 不适合 / 需谨慎 |
|---|---|
| 遗留单体应用难容器化 | 超大规模 VM(数千台需评估调度压力) |
| 同一团队既要容器又要 VM | 强隔离多租户(考虑独立集群) |
| VM 需 GitOps 版本管理 | 对启动延迟极度敏感(冷启动慢于 Pod) |
结语
KubeVirt 的价值不是「用 K8s 跑虚拟机」这一个炫技动作,而是把容器与虚拟机的运维范式彻底统一:同一套调度、网络、存储、监控与策略体系,覆盖从云原生新应用到历史遗留系统的全部负载。对正在推进 Kubernetes 化的团队,它是平滑迁移、降低运维分裂成本的关键拼图。




