HMK's blog

保持思考|00后|等待


  • Home
  • Archive
  • Tags
  •   

© 2026 Hekang

Theme Typography by Makito

Proudly published with Hexo

kubevirt安装

Posted at 2026-07-14 云原生 

kubevirt安装

一、KubeVirt 概述

KubeVirt 是 Kubernetes 的一个扩展,允许在 Kubernetes 集群中管理虚拟机(VM)。它将虚拟机工作负载与容器工作负载统一在同一个平台上,为传统应用提供云原生的管理能力。

核心特性

  • 在 Kubernetes 中运行和管理虚拟机
  • 支持热迁移(Live Migration)
  • 支持 SR-IOV、MAC 地址池等高级网络功能
  • 与容器网络(CNI)和容器存储(CSI)集成
  • 提供 virtctl 命令行工具管理虚拟机

二、环境准备

2.1 前置条件

组件 版本 说明
Kubernetes v1.34.1 需要启用 Webhook 和 CRD
kubectl v1.34.1 与集群版本匹配
Containerd/Docker 最新稳定版 容器运行时
CPU 2核+ 需要支持硬件虚拟化
内存 4GB+ 建议 8GB 以上

2.2 检查硬件虚拟化支持

1
2
3
4
# Linux 系统检查
grep -E '(vmx|svm)' /proc/cpuinfo

# 如果输出包含 vmx (Intel) 或 svm (AMD),则支持硬件虚拟化

2.3 加载 KVM 内核模块

1
2
3
4
5
6
7
8
9
# 加载 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 为例)

  1. 用户 提交创建 VM 的请求 ➔ virt-api(校验并通过 webhook 注入默认值,写入 etcd)。
  2. virt-controller 监听到新 VM ➔ 创建对应的 VMI 和底层的 Helper Pod。
  3. Kubernetes 调度器将该 Pod 调度到 Node-A。
  4. Node-A 上的 virt-handler 检测到本节点有新的 VMI 分配,开始与该 Pod 内的 virt-launcher 通信。
  5. virt-launcher 在 Pod 内部启动本地 libvirtd,并最终拉起 qemu-kvm(虚拟机正式运行)。

三、安装 KubeVirt

3.1 使用 kubectl 安装(推荐)

1
2
3
4
5
6
7
8
9
10
11
# 设置 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 验证安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 查看 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

预期输出:

1
2
NAME       AGE    PHASE
kubevirt 5m Deployed

四、安装 virtctl 客户端工具

4.1 下载 virtctl

1
2
3
4
5
# 设置版本
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

1
2
3
4
# 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 验证安装

1
2
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 创建虚拟机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 [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 并访问

1
2
3
4
5
6
7
8
9
10
11
12
13
 [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

image-20260714150704627

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 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
1
kubectl apply -f vm-ubuntu.yaml

在 KubeVirt 中,虚拟机(VirtualMachine/VMI)对存储卷的使用分为两个层次:Kubernetes 集群侧的存储提供(Volume) 以及 虚拟机内部感知的磁盘形态(Disk)。

以下是 KubeVirt 中最常用的存储卷类型(Volume Types)及其典型应用场景:

  1. persistentVolumeClaim (PVC)

这是生产环境中最核心、最常用的存储类型。它直接对接 Kubernetes 的存储生态(CSI 驱动),支持从本地磁盘、网络文件系统(NFS)、到分布式存储(如 Ceph、OpenEBS 等)。

  • 使用方式: 可以挂载为文件系统模式(Filesystem)(在 PVC 上创建一个磁盘镜像文件,如 disk.img)或原始块设备模式(Block)(直接将块设备映射给虚拟机,性能最佳)。
  • 应用场景: 虚拟机的系统盘、高性能数据盘、需要支持持久化和跨节点热迁移(通常需要 ReadWriteMany / RWX 属性)的场景。
  1. containerDisk

KubeVirt 独创的一种极其便利的存储类型。它允许将虚拟机的操作系统镜像(如 qcow2/raw 格式)直接打包到 Docker 镜像中,并托管在标准的 Container Registry(如 Harbor、Docker Hub)里。

  • 使用方式: 虚拟机启动时,会像拉取容器镜像一样拉取这个 containerDisk 镜像,并从中读取操作系统。
  • 应用场景: 只读系统盘、快速水平扩展的无状态虚拟机(如基础 CI/CD 节点)、共享的黄金镜像(Golden Image)。由于其默认只读或采用临时写时复制(Ephemeral),它非常适合用作虚拟机的启动模板。
  1. 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。
  1. cloudInitNoCloud

这是一个特殊的配置卷,专门用来向虚拟机注入初始化配置(网络、主机名、用户密码、SSH 密钥、初始脚本等)。

  • 使用方式: KubeVirt 会自动将用户定义的 userData 或 networkData 转换为标准的 cloud-init ISO 镜像,并挂载为虚拟机的只读光驱。
  • 应用场景: Linux 虚拟机的自动化首次开机配置、网络动态分配、公钥注入。
  1. emptyDir

与 Kubernetes 原生的 emptyDir 完全一致,是一个生命周期与当前 Pod(虚拟机实例)绑定的临时空目录。

  • 使用方式: 虚拟机内部会看到一个空白的、未格式化的物理磁盘。当虚拟机 Pod 被重建、漂移或删除时,其中的数据会彻底丢失。
  • 应用场景: 虚拟机的临时缓存盘、Swap 交换分区空间、无状态的 scratch 空间。
  1. hostDisk

直接映射宿主机(Node)本地的文件路径到虚拟机中(类似于 Kubernetes 的 hostPath)。

  • 使用方式: 在宿主机的指定路径下创建或使用一个独立的虚拟磁盘文件。

  • 应用场景: 单节点测试环境、边缘计算节点(节点数量少、不需要热迁移、追求极简的本地存储性能)。

  • ⚠️ 注意: 使用 hostDisk 会限制虚拟机的调度灵活性,导致虚拟机无法在集群内热迁移。

📝 典型配置结构示例 (YAML 视角)

在定义 VM CRD 时,这些存储卷在 spec.template.spec 里的基本映射逻辑如下:

YAML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 查看虚拟机

1
2
3
4
5
6
7
8
# 查看虚拟机状态
kubectl get vmi

# 查看虚拟机详情
kubectl describe vmi ubuntu-vm

# 使用 virtctl 查看
virtctl get vmi

5.3 使用 virtctl 暴露服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 暴露 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 管理虚拟机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 启动虚拟机
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

1
2
3
4
5
6
7
8
# 删除 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

七、部署CDI和web ui

1
2
kubectl apply -f https://github.com/kubevirt/containerized-data-importer/releases/download/v1.65.0/cdi-operator.yaml
kubectl apply -f https://github.com/kubevirt/containerized-data-importer/releases/download/v1.65.0/cdi-cr.yaml

web ui

1
2
kubectl apply -f https://raw.githubusercontent.com/kubevirt-manager/kubevirt-manager/main/kubernetes/bundled.yaml
# 修改svc 访问

image-20260714164043281

Share 

 Next post: gitlab 

© 2026 Hekang

Theme Typography by Makito

Proudly published with Hexo