主题
KubeVirt 实战:部署实施与运维手册
日期: 2026-09-03 环境基线: RKE2 v1.35.6(6 节点)+ KubeVirt v1.9.0 + CDI v1.60.4,Rocky Linux 9.8,Calico VXLAN(Pod CIDR 10.22.0.0/16) 入口节点:
root@192.168.122.31(sza122031.local) 镜像仓库: Harbor192.168.122.156:30000/kubevirt/kubevirt/*(代理 quay.io/kubevirt) 配套教材:kubevirt-tutorial-beginner-to-master.md关联文档:../rke2-kubevirt-deployment-guide.md、../cdi-offline-deployment-guide.md、../kubevirt-nas-storage-guide.md、../kubevirt-storage-ha-guide.md、../kubevirt-network-ssh-password-guide.md、./jumpserver-in-cluster-deployment-guide.md(堡垒机接入虚拟机)
目录
- 部署实施
- 虚拟机 YAML 字段详解
- 网络运维专题
- 3.1 静态 IP 如何配置
- 3.2 配置为 Pod IP 还是节点 IP
- 3.3 如何修改网络网段
- 3.4 固定 IP 与热迁移共存
- 3.5 钉节点 + 固定 Pod IP(放弃热迁移场景)
- 存储运维专题
- 4.1 如何使用 NAS 做存储
- 4.2 如何扩容资源(CPU/内存/磁盘)
- 如何将普通 KVM 转为 KubeVirt 可运行的镜像
- 如何备份
- 如何克隆
- 如何灾备
- 附录:命令速查与 FAQ
1. 部署实施
1.0 部署总览
阶段一 节点准备 → 嵌套虚拟化/设备权限/节点标签
阶段二 镜像准备 → Harbor 代理缓存 kubevirt + CDI 镜像
阶段三 KubeVirt 安装 → operator + KubeVirt CR(离线用内部仓库)
阶段四 CDI 安装 → 磁盘导入/克隆能力
阶段五 Multus 网络 → (可选)物理网段直连能力
阶段六 验证 → 组件、建盘、起 VM 全链路1.1 阶段一:节点准备
在每个将运行虚拟机的节点执行:
bash
# 1. 开启嵌套虚拟化(Intel 示例,AMD 换 kvm_amd)
echo 'options kvm_intel nested=1' > /etc/modprobe.d/kvm-nested.conf
modprobe -r kvm_intel && modprobe kvm_intel nested=1
cat /sys/module/kvm_intel/parameters/nested # 输出 1
# 2. 持久化模块
cat > /etc/modules-load.d/kvm.conf <<'EOF'
kvm
kvm_intel
EOF
# 3. 设备权限
ls -l /dev/kvm && chmod 666 /dev/kvm
# 4. (可选)给节点打标签,便于 VM nodeSelector
kubectl label node sza122031.local vm-role=true1.2 阶段二:镜像准备(离线)
集群无法直连 quay.io,通过 Harbor 代理缓存:
# Harbor 上创建代理缓存项目:名 kubevirt,上游 quay.io/kubevirt
# RKE2 的 /etc/rancher/rke2/registries.yaml 配置 mirror 指向 Harbor
# 需要的镜像(KubeVirt v1.9.0):
virt-operator / virt-api / virt-controller / virt-handler / virt-launcher / virt-exportproxy
# CDI v1.60.4(7 个):
cdi-apiserver / cdi-cloner / cdi-controller / cdi-importer / cdi-operator / cdi-uploadproxy / cdi-uploadserver有外网的中转机可用 skopeo 同步:
bash
for i in virt-operator virt-api virt-controller virt-handler virt-launcher; do
skopeo copy docker://quay.io/kubevirt/${i}:v1.9.0 \
docker://192.168.122.156:30000/kubevirt/kubevirt/${i}:v1.9.0
done1.3 阶段三:安装 KubeVirt
bash
# ① 创建命名空间并安装 operator(离线版已把镜像替换为 Harbor 地址)
kubectl create namespace kubevirt
kubectl apply -f kubevirt-operator.yaml # 含 22 个 CRD + RBAC + operator Deployment
# ② (现网踩坑)RBAC 不足时补充通配权限
kubectl patch clusterrole kubevirt-operator --type='json' \
-p='[{"op":"add","path":"/rules/-","value":{"apiGroups":["*"],"resources":["*"],"verbs":["*"]}}]'
# ③ 创建 KubeVirt CR(关键:imageRegistry 指向内部仓库)
kubectl apply -f - <<'EOF'
apiVersion: kubevirt.io/v1
kind: KubeVirt
metadata:
name: kubevirt
namespace: kubevirt
spec:
imagePullPolicy: IfNotPresent
imageRegistry: 192.168.122.156:30000/kubevirt/kubevirt
imageTag: v1.9.0
EOF
# ④ 等待就绪
kubectl -n kubevirt wait kv kubevirt --for condition=Available --timeout=600s
kubectl get pods -n kubevirt # 16 个 Pod Running完整部署记录与故障修复见
../rke2-kubevirt-deployment-guide.md。
1.4 阶段四:安装 CDI(磁盘导入/克隆能力)
隔离环境采用运行时导出的单文件清单(51 个对象),详见 ../cdi-offline-deployment-guide.md:
bash
kubectl apply -f cdi-deploy.yaml # 与官方 operator+cr 二选一
kubectl -n cdi wait cdi cdi --for condition=Available --timeout=300s
kubectl get pods -n cdi关键 CDI 配置(CDI CR 内):
yaml
spec:
config:
honorWaitForFirstConsumer: true # 等 VM 调度后再导入,避免导入到错误节点
# 默认文件系统开销 5.5%1.5 阶段五:安装 Multus(可选,物理网段直连需要)
bash
kubectl get pods -n kube-system | grep multus # RKE2 通常自带
kubectl get crd network-attachment-definitions.k8s.cni.cncf.io
# 缺失时用 Helm 安装
helm repo add rke2-charts https://rancher.github.io/rke2-charts
helm install multus rke2-charts/rke2-multus -n kube-system
# bridge 模式还需在每个节点部署 bridge CNI 插件(/opt/cni/bin/bridge)1.6 阶段六:部署验证清单
bash
# 平台组件
kubectl get kubevirt -n kubevirt # PHASE=Deployed
kubectl get cdi # PHASE=Deployed
kubectl get pods -n kubevirt -n cdi # 全 Running
# 存储
kubectl get storageclass # 有可用的共享存储 SC(如 vm-nas)
# 端到端:导入镜像 → 起 VM → 网络可达
# (流程见 4.1 NAS 存储章节与教材第 5 章)1.7 部署常见问题(现网实测)
| 问题 | 现象 | 解决 |
|---|---|---|
| operator RBAC 不足 | 日志 forbidden | 见 1.3 步骤② |
| Calico 跨节点不通 | webhook 502 | IPPool 改 vxlanMode: Always,重启 calico-node |
| virt-handler 证书缺失 | handler 起不来 | 重新触发部署/运行修复脚本 |
/dev/kvm 不存在 | VMI 卡 Scheduling | 开嵌套虚拟化(1.1) |
| CDI CRD 存根干扰 | 创建 DV 报错 | 清理旧 CRD 后重装 |
2. 虚拟机 YAML 字段详解
2.1 生产级完整示例(逐行注释)
以下 YAML 汇总了生产环境最常用的字段(NAS 存储 + 固定 IP + HA):
yaml
apiVersion: kubevirt.io/v1 # KubeVirt API 组,v1 为稳定版
kind: VirtualMachine # 虚拟机定义(长期存在,可反复启停)
metadata:
name: rocky9-vm2 # VM 名称(也是默认 VMI/launcher 名前缀)
namespace: default # 命名空间
labels: # VM 对象标签,用于检索/分组
app: rocky9-vm2
kubevirt.io/vm: rocky9-vm2
spec:
running: true # 期望运行状态(与 runStrategy 二选一)
# runStrategy: Always # 生产推荐:崩溃/节点故障自动拉起
template: # ★ VMI 模板,结构与 Pod spec 类似
metadata:
labels: # ★ launcher Pod/VMI 的标签
app: rocky9-vm2 # Service selector 必须匹配这里
kubevirt.io/vm: rocky9-vm2
spec:
evictionStrategy: LiveMigrate # 节点维护/驱逐时热迁移(需 RWX 磁盘)
terminationGracePeriodSeconds: 60 # 优雅关机等待秒数
nodeSelector: # 调度:限定节点(可选)
kubernetes.io/hostname: sza122031.local
tolerations: # 容忍节点污点(短暂失联不急于杀 VM)
- key: node.kubernetes.io/unreachable
operator: Exists
effect: NoExecute
tolerationSeconds: 600
domain: # ★★★ 客户机硬件定义(对应 libvirt domain)
cpu:
cores: 2 # vCPU 核数(也可用 sockets/threads 描述拓扑)
# model: host-passthrough # 透传宿主 CPU 特性(热迁移需同构)
# dedicatedCpuPlacement: true # 独占物理核(性能敏感场景)
memory:
guest: 2Gi # 客户机内存
# hugepages: { pageSize: 1Gi } # 大页(需节点预留)
machine:
type: q35 # 机型:q35(推荐)/ pc / 可指定版本
firmware:
# bootMenu: { enabled: true } # 开机启动菜单(调试用)
# bootloader: { efi: { secureBoot: false } } # UEFI 启动(按需)
clock:
utc: {} # 时钟源:utc(Linux)/ timezone
timer:
hpet: { present: false }
hyperv: {} # Windows 需要
kvm: {}
pit: { tickPolicy: delay }
rtc: { tickPolicy: catchup }
features:
acpi: {} # 电源管理(关机靠它)
smm: { enabled: true } # 安全模式(UEFI SecureBoot 需要)
devices: # ★ 设备列表
disks: # 磁盘设备
- name: rootdisk # ★ 与 volumes[].name 对应
disk:
bus: virtio # virtio(最快)/sata/scsi
cache: none # none/writeback/writethrough
# serial: DISK001 # 磁盘序列号(客户机识别用)
- name: cloudinit
disk:
bus: virtio
interfaces: # 网卡设备
- name: default # ★ 与 networks[].name 对应
masquerade: {} # 绑定方式:masquerade(NAT 借用 Pod IP)
# bridge: {} # 或 bridge(直通,配合 Multus)
# sriov: {} # 或 SR-IOV 直通
model: virtio # 网卡型号:virtio(推荐)/e1000e
# macAddress: de:00:00:00:00:01 # 固定 MAC(可选)
# 其他可选设备:
# rng: {} # 随机数设备
# watchdog: { name: w1, i6300esb: {} } # 看门狗
# inputs: # 输入设备(VNC 鼠标用 tablet 更准)
# - { type: tablet, bus: virtio, name: tablet }
# autoattachPodInterface: true # 自动挂 Pod 网络(默认)
# autoattachGraphicsDevice: true # 自动挂显卡(VNC 用)
networks: # ★ 网络定义(与 interfaces 对应)
- name: default
pod: {} # 使用 K8s Pod 网络
# - name: physnet # Multus 辅助网络示例
# multus:
# networkName: vm-physnet # 指向 NetworkAttachmentDefinition
volumes: # ★ 卷定义(与 disks 对应)
- name: rootdisk
persistentVolumeClaim:
claimName: rocky9-vm2-disk # 已存在的 PVC
# dataVolume: { name: xxx } # 或用 CDI DataVolume
# containerDisk: { image: ... } # 或容器内嵌磁盘(无状态)
- name: cloudinit
cloudInitNoCloud:
userData: |- # 首次启动配置(主机名/密码/SSH)
#cloud-config
hostname: rocky9-vm2
password: xxx
chpasswd: { expire: false }
ssh_pwauth: true
disable_root: false
networkData: |- # 静态 IP 配置(见 3.1)
version: 1
config:
- type: physical
name: eth1
subnets:
- type: static
address: 192.168.122.100/24
gateway: 192.168.122.12.2 字段分组速查表
顶层与调度
| 字段 | 类型 | 说明 | 常用值 |
|---|---|---|---|
spec.running | bool | 期望运行状态 | true/false(与 runStrategy 二选一) |
spec.runStrategy | string | 运行策略 | Always(生产)/Manual/Halted/RerunOnFailure/Once |
spec.instancetype | ref | 规格引用 | 抽离 CPU/内存 |
spec.dataVolumeTemplates | [] | 随 VM 建盘 | 见教材 9.3 |
template.spec.nodeSelector | map | 限定节点 | kubernetes.io/hostname: xxx |
template.spec.affinity | obj | 复杂调度/反亲和 | 多 VM 分散 |
template.spec.tolerations | [] | 容忍污点 | 节点失联延迟杀 |
template.spec.evictionStrategy | string | 驱逐策略 | LiveMigrate(推荐)/None |
template.spec.terminationGracePeriodSeconds | int | 关机宽限 | 60 |
domain.cpu / memory / machine / firmware
| 字段 | 说明 | 备注 |
|---|---|---|
cpu.cores | vCPU 核数 | 扩容改这里 |
cpu.sockets/threads | CPU 拓扑 | 配合许可证/NUMA |
cpu.model | CPU 型号 | host-passthrough/host-model/指定型号 |
cpu.dedicatedCpuPlacement | 绑核 | 需 CPUManager 特性 |
cpu.numa | NUMA 拓扑 | 大内存性能 |
memory.guest | 客户机内存 | 扩容改这里 |
memory.hugepages | 大页 | 需节点预留 |
machine.type | 机型 | q35 推荐 |
firmware.serial | 序列号 | 唯一标识 |
firmware.bootloader.efi | UEFI 启动 | SecureBoot 可选 |
firmware.bootMenu | 启动菜单 | 调试用 |
domain.devices
| 字段 | 说明 | 备注 |
|---|---|---|
disks[].name | 设备名 | 必须匹配 volumes[].name |
disks[].disk.bus | 总线 | virtio/sata/scsi |
disks[].cdrom | 光驱 | 挂 ISO |
disks[].cache | 缓存模式 | none 安全 |
disks[].dedicatedIOThread | 独立 IO 线程 | 低延迟 |
disks[].serial | 序列号 | 客户机识别 |
interfaces[].name | 网卡名 | 必须匹配 networks[].name |
interfaces[].masquerade | NAT 绑定 | 借 Pod IP |
interfaces[].bridge | 桥接绑定 | 配合 Multus |
interfaces[].sriov | SR-IOV | 高性能 |
interfaces[].model | 型号 | virtio/e1000e |
interfaces[].macAddress | 固定 MAC | 可选 |
interfaces[].ports | 放行端口 | masquerade 下有效 |
rng | 随机数设备 | 建议加 |
watchdog | 看门狗 | 客户机挂死重启 |
inputs | 键鼠/平板 | VNC 交互 |
gpus / hostDevices | GPU/设备直通 | 需硬件支持 |
tpm | 可信平台 | Windows 11 需要 |
volumes(与 disks 一一对应)
| 卷类型 | 字段 | 持久性 | 用途 |
|---|---|---|---|
| 持久卷 | persistentVolumeClaim.claimName | ✅ | 生产首选 |
| CDI 盘 | dataVolume.name | ✅ | 导入/克隆生成 |
| 容器盘 | containerDisk.image | ❌ | 无状态演示 |
| 临时盘 | emptyDisk.capacity | ❌ | 缓存 |
| cloud-init | cloudInitNoCloud | - | 首次配置 |
| 配置盘 | configMap/secret/serviceAccount | - | 挂资源进客户机 |
| 主机盘 | hostDisk | 节点相关 | 谨慎 |
networks(与 interfaces 一一对应)
| 字段 | 说明 |
|---|---|
pod: {} | K8s Pod 网络(默认) |
multus.networkName | Multus 网络(指向 NAD) |
完整字段清单以官方 API 参考为准:https://kubevirt.io/api-reference/。
3. 网络运维专题
先建立一张"网络模式决策表",后面所有操作都围绕它展开:
| 模式 | VM 拿到的 IP | 谁能直连 | 固定性 | 适用 |
|---|---|---|---|---|
| masquerade(默认) | 借用 Pod IP(NAT) | 仅集群内 | ❌ 重建即变 | 内部服务 |
| Pod 网络 bridge | 自己的 Pod IP | 集群内 | ❌ 随 Pod 变 | 集群内直连 |
| Multus bridge/macvlan | 物理网段固定 IP | 外部直连 | ✅ 写死静态 | 外部按业务 IP 直连 |
| Service NodePort | 节点 IP:端口 | 外部 | ✅ 端口固定 | 稳定入口 |
3.1 静态 IP 如何配置
核心结论(现网验证):KubeVirt 不会把 Multus NAD 中 IPAM 分配的 IP 注入客户机。 要给 VM 固定静态 IP,标准做法是 cloud-init networkData 在客户机内写死。 共 5 步。
步骤 ①:确认 Multus 已就绪
bash
kubectl get pods -n kube-system | grep multus
kubectl get crd network-attachment-definitions.k8s.cni.cncf.io步骤 ②:(bridge 模式)节点上建网桥
把物理网卡加入网桥 br-vm,让 VM 流量直接进物理网络:
bash
# 以 nmcli 为例(Rocky 9)
nmcli con add type bridge ifname br-vm
nmcli con add type bridge-slave ifname eth1 master br-vm # eth1=上联网卡
nmcli con down eth1 && nmcli con up br-vm
ip addr show br-vm # 确认网桥拿到原网卡 IP⚠️ 若节点只有一个上联网卡,建桥会导致短暂断网,务必带外(IPMI/控制台)操作。
步骤 ③:创建 NetworkAttachmentDefinition
yaml
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: vm-physnet
namespace: default
spec:
config: '{
"cniVersion": "0.3.1",
"type": "bridge",
"bridge": "br-vm",
"ipam": {}
}'bash
kubectl apply -f nad.yaml
kubectl get net-attach-def # 确认出现步骤 ④:VM 增加第二块网卡引用 NAD
yaml
domain:
devices:
interfaces:
- name: default
masquerade: {} # 保留默认网络(可选,用于集群内通信/SSH 兜底)
- name: physnet
bridge: {} # 绑到网桥
model: virtio
networks:
- name: default
pod: {}
- name: physnet
multus:
networkName: vm-physnet # ★ 指向 NAD步骤 ⑤:cloud-init 写死静态 IP
yaml
volumes:
- name: cloudinit
cloudInitNoCloud:
networkData: |-
version: 1
config:
- type: physical
name: eth1 # 第二块网卡在客户机内的名称
subnets:
- type: static
address: 192.168.122.100/24
gateway: 192.168.122.1
dns_nameservers: [223.5.5.5]网卡名需与客户机一致。不确定时先起 VM 用
ip a看 MAC/名称,再回填。 改networkData后,需清除客户机/var/lib/cloud/instances或重建磁盘才重新应用。
验证
bash
kubectl get vmi rocky9-vm2 -o jsonpath='{.status.interfaces}' # 平台视角
# 从外部机器
ping 192.168.122.100 && ssh root@192.168.122.1003.2 配置为 Pod IP 还是节点 IP
这是"VM 对外的身份"选择题。两种含义要分清:
(A) 让 VM 用 Pod IP(集群内访问)
默认 masquerade 即是。VM 出网源 = Pod IP(10.22.x.x),集群内可直接访问, 但 Pod IP 不稳定,外部不可直连。适合纯内部服务。
yaml
interfaces:
- name: default
masquerade: {}
networks:
- name: default
pod: {}集群内访问:kubectl get vmi <vm> -o jsonpath='{.status.interfaces[0].ipAddress}'
(B) 让 VM 走"节点侧入口"(对外访问)
这里有两种不同含义,按需选:
B-1:节点 IP + 端口(NodePort,零节点改造,推荐起步)
VM 仍在 Pod 网络,但通过 Service 在每个节点的固定端口暴露, 外部用 节点IP:端口 访问,永远不变:
yaml
apiVersion: v1
kind: Service
metadata:
name: rocky9-vm2-ssh
spec:
type: NodePort
selector:
app: rocky9-vm2 # 匹配 VM template.labels
ports:
- name: ssh
port: 22
targetPort: 22
nodePort: 31022 # 固定端口(30000-32767)bash
kubectl apply -f svc.yaml
ssh root@192.168.122.31 -p 31022 # 任意节点 IP 都可特点:外部看到的源是节点(NAT),VM 内感知不到真实客户端 IP。 适合"只要稳定连上",不想动节点网络的场景。
B-2:节点物理网段固定 IP(Multus bridge,外部真直连)
即 3.1 的方案。VM 拥有物理网段 IP(如 192.168.122.100),外部按业务 IP 直连, 无 NAT。适合外部系统必须按固定业务 IP 访问的场景。
选型速记
只要稳定访问、不想改节点网络 → B-1 NodePort(10 分钟搞定)
外部必须按物理网段固定 IP 直连 → B-2 Multus bridge(5 步)
纯集群内部服务 → A masquerade完整三方案对比与踩坑见
../kubevirt-network-ssh-password-guide.md。
3.3 如何修改网络网段
"改网段"分三种情况,风险从低到高,先判断自己属于哪一种:
| 情况 | 改什么 | 影响面 | 风险 |
|---|---|---|---|
| ① VM 静态 IP 网段 | cloud-init 的 address/gateway | 单台 VM | 低 |
| ② Multus 网桥网段 | 节点网桥 + NAD + 静态 IP | 该网桥所有 VM | 中 |
| ③ Pod 网络网段(Calico CIDR) | IPPool + 可能动 API Server | 整个集群 | 高(谨慎) |
① 改单台 VM 的静态 IP 网段(最常见、最安全)
适用:物理网络规划变了,或把 VM 挪到新网段。
bash
# 1. 编辑 VM 的 cloud-init networkData
kubectl edit vm rocky9-vm2
# 修改 address: 新网段/掩码、gateway、dns
# 2. 让 cloud-init 重新生效:清客户机实例标记后重启
virtctl stop rocky9-vm2
# (可选)进盘清理 /var/lib/cloud/instances,或直接重建网络配置
virtctl start rocky9-vm2若 VM 里已经手工配过网络(cloud-init 不再管),则直接进客户机改
/etc/NetworkManager/system-connections/或nmcli,无需动 YAML。
② 改 Multus 网桥对应的网段
网段由物理网络决定(网桥挂在哪个 VLAN/上联),改网段本质是改节点网桥的 上联和 IP 规划:
bash
# 1. 停掉该网桥上所有 VM
# 2. 调整节点网桥上联(换 VLAN 口 / 改 IP)
nmcli con mod br-vm ipv4.addresses 新网段/掩码 ...
# 3. 更新 NAD(若涉及 VLAN 号)
kubectl edit net-attach-def vm-physnet
# 4. 逐台改 VM 静态 IP(回到情况①)并验证③ 改 Pod 网络网段(Calico CIDR)——高危
强烈建议:生产集群尽量不要在线改 Pod CIDR,能新建集群就不改存量。 若必须改,要点如下:
bash
# 1. 备份 etcd 与全部 IPPool 定义
kubectl get ippool -o yaml > ippool-backup.yaml
# 2. 新建目标网段的 IPPool(不要直接改旧池,先并存)
kubectl apply -f - <<'EOF'
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: new-ipv4-ippool
spec:
cidr: 10.33.0.0/16 # 新网段
blockSize: 26
ipipMode: Never
vxlanMode: Always # ★ 与现网一致,保持封装
natOutgoing: true
nodeSelector: all()
EOF
# 3. 把旧池设为不再分配新 IP(禁用而非删除,避免存量 Pod 失联)
kubectl patch ippool default-ipv4-ippool --type=merge \
-p '{"spec":{"disabled":true}}'
# 4. 滚动重启工作负载,让 Pod 逐步用新池;确认无异常后
# 再删除旧池(务必确认没有 Pod 还在用旧段)⚠️ 现网踩坑记录(误删
ippoolsCRD 导致全网断网)见../rke2-kubevirt-deployment-guide.md§5.2。改 Pod CIDR 前必须演练 + 备份 + 回滚预案。 改 CIDR 会影响 Service、CNI、API Server 关联配置,务必评估全部依赖。
网段修改检查清单
- [ ] 明确属于①②③哪一种,从低风险开始
- [ ] 已备份相关对象(VM YAML / IPPool / NAD)
- [ ] 物理网络(网关、VLAN、路由)已同步调整
- [ ] 逐台验证连通性后再批量
- [ ] 更新文档与监控里记录的网段信息
3.4 固定 IP 与热迁移共存
关键认知
热迁移不依赖任何固定 IP:迁移通道走的是 Pod 网络(masquerade),Pod IP 在目标节点重建时变化是正常现象,不影响迁移。官方硬性约束(Live Migration 文档):
- Pod 网络必须
masquerade绑定——bridge 绑定 Pod 网络禁止热迁移; - 磁盘 PVC 必须
ReadWriteMany; - virt-launcher 需要 49152/49153 端口可用(masquerade 接口不要显式占用)。
所以正确思路不是"固定 Pod IP",而是:Pod IP 随它变,另加一块网卡专门承载 固定 IP(双网卡架构)。
方案对比
| # | 方案 | 固定 IP 方式 | 迁移后 IP | 适用场景 | 备注 |
|---|---|---|---|---|---|
| 1 | Multus bridge 第二网卡 + cloud-init networkData 静态 IP(推荐) | 客户机内写死 | ✅ 不变(写在客户机配置里) | 外部系统需按物理网段固定 IP 直连 | 所有候选节点都要建好同名网桥、同 L2 |
| 2 | Multus 第二网卡 + whereabouts IPAM | 集群级 IPAM 自动分配 | ⚠️ 网络侧 IP 由 IPAM 预留保持,但现网验证 IPAM 分配的 IP 不会注入客户机,客户机仍需静态配置,效果等同方案 1 | 希望集中管理地址池 | 多引入组件,现网直接用方案 1 即可 |
| 3 | 固定 VMI macAddress + DHCP 服务器 MAC 预留 | 客户机 DHCP 获得预留固定 IP | ✅ 不变 | 物理网段有 DHCP 服务器、希望集中管理 | VM spec 需指定 interfaces[].macAddress |
| 4 | Service/NodePort(或 MetalLB LoadBalancer) | 入口固定:节点IP:端口 或 VIP | ✅ 入口不变(Pod IP 随便变) | 访问稳定即可,不必把固定 IP 放进客户机 | 改造最小,迁移完全透明 |
| 5 | passt 绑定(v1.8+ 新) | 客户机直接持有 Pod IP | ✅ 官方:客户机保留原 IP 直至 DHCP 续租 | 单网卡简化场景 | Beta、不支持辅助网络、额外 +250Mi 内存 |
推荐架构(方案 1)
┌─ eth0(masquerade,Pod IP 会变)──> KubeVirt 控制面 / 热迁移通道
VM(rocky9-vm2)─────┤
└─ eth1(Multus bridge br-vm)──────> 192.168.122.102 固定,物理网段直达- eth0:保留默认
pod: {}+masquerade: {}——热迁移数据通道,绝不能去掉 (现网排障记录:双网卡后无法热迁移,原因就是丢了默认pod: {}网络); - eth1:
multus+bridge: {}挂物理网桥,IP 由networkData写死。 完整 YAML 见../kubevirt-network-ssh-password-guide.md第 6 章(rocky9-vm2 实例)。
前提与注意事项
- 磁盘为 RWX 共享存储(NFS
vm-nas)——热迁移本身的前提; - 所有候选节点上存在同一 NAD、建好同名网桥(
br-vm),桥成员接入同一 物理网络 / L2 域;任一节点缺桥,迁移到该节点会失败; - 不要用
nodeSelector把 VM 钉死在特定节点(否则迁移没有意义); - 切换后客户机发送 Gratuitous ARP 刷新交换机 MAC 表(标准内核自动完成), 秒级丢包属正常现象;
- 不要用
host-localIPAM:地址池按节点本地分配,跨节点迁移后 IP 必变。
验证
bash
virtctl migrate rocky9-vm2 && kubectl get vmim -w
# 迁移完成后:
kubectl get vm rocky9-vm2 -o wide # 观察 NODE 已切换
ping -c 3 192.168.122.102 # 固定 IP 仍可达
ssh root@192.168.122.102 # 业务连接不中断3.5 钉节点 + 固定 Pod IP(放弃热迁移场景)
结论:能做,三个环节都有官方支持
| 需求 | 手段 | 依据(已在源码核实) |
|---|---|---|
| 固定 Pod IP | Calico 注解 cni.projectcalico.org/ipAddrs | Calico CNI 的 k8s.go 直接解析该注解,向 calico-ipam 申请指定 IP(RKE2 默认即 calico-ipam,无需 feature gate) |
| 注解放到 launcher Pod | VM spec.template.metadata.annotations | KubeVirt filterVMIAnnotationsForPod 把 VMI 注解原样复制到 virt-launcher Pod(仅过滤 kubectl.kubernetes.io* 等 3 个前缀) |
| 钉节点 | spec.template.spec.nodeSelector | 标准 K8s 放置能力 |
| 放弃热迁移 | 没有显式开关,也不需要:钉死后迁移找不到目标节点必然失败;可再叠加 RWO 本地盘双重封死 | — |
完整 YAML
yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: rocky9-static
spec:
running: true
template:
metadata:
annotations:
# Calico 申请固定 Pod IP:须在 Pod CIDR 10.22.0.0/16 内、未被其他 Pod 占用
cni.projectcalico.org/ipAddrs: '["10.22.100.10"]'
spec:
nodeSelector:
kubernetes.io/hostname: rke2-worker-3 # ★ 钉到固定节点
domain:
devices:
interfaces:
- name: default
masquerade: {}
resources:
requests: { memory: 2Gi }
networks:
- name: default
pod: {}💡 钉节点后不再依赖共享存储,磁盘可改用本地盘 RWO(无需 NFS), 这反过来又天然阻断了热迁移——两件事互相加强。
IP 稳定性矩阵(什么情况下变、什么情况下不变)
| 事件 | Pod IP |
|---|---|
| 容器重启 / kubelet 或节点重启(Pod 对象还在) | ✅ 不变 |
virtctl restart / 客户机内重启(重建 launcher Pod) | ✅ 不变——新 Pod 仍声明同一地址,旧地址已释放,Calico 原样再分配 |
| 手动删除 virt-launcher Pod | ✅ 不变(同上) |
| 钉住的节点宕机 | ❌ Pod 无法调度到别处,VM 中断到节点恢复——这就是"放弃迁移"的代价 |
防 IP 冲突约定
动态分配按需借 /26 块、从低地址开始,因此把固定 Pod IP 集中规划在高位段 (如 10.22.100.0/24)并维护一张登记表(哪台 VM 用哪个 IP),实际冲突概率极低。 分配前也可先确认:
bash
kubectl get workloadendpoints -A | grep 10.22.100.10 || echo "未占用"验证
bash
kubectl apply -f rocky9-static.yaml
kubectl get vmi rocky9-static -o wide
# launcher Pod 的 IP 应等于 10.22.100.10:
kubectl get pods -l vm.kubevirt.io/name=rocky9-static -o wide
kubectl get pod -l vm.kubevirt.io/name=rocky9-static \
-o jsonpath='{.items[0].metadata.annotations.cni\.projectcalico\.org/ipAddrs}'
# 节点上直连(Pod IP 只在集群内路由):
ping -c 2 10.22.100.10
# 确认迁移已被"事实封死":
virtctl migrate rocky9-static # 应失败:找不到合格目标节点与其他固定 IP 手段的定位对比
| 手段 | 可达范围 | 兼容热迁移 | 适用 |
|---|---|---|---|
| 本节:固定 Pod IP + 钉节点 | 仅集群内(出方向带 SNAT) | ❌ | 集群内白名单/互访场景,零额外组件 |
| 3.4:Multus 物理网段固定 IP | 物理网段直达 | ✅ | 对外固定入口(推荐) |
| Service ClusterIP/NodePort | 入口级固定 | ✅ | 服务化暴露 |
4. 存储运维专题
4.1 如何使用 NAS 做存储
原理:KubeVirt 虚拟机磁盘本质是一个 PVC。任何能供应 PVC 的存储后端 (NFS / iSCSI / 厂商 CSI)都可以。商用 NAS 最常用 NFS。
三种接入方式对比
| 方式 | 适用 | 优点 | 限制 |
|---|---|---|---|
| NFS(推荐起步) | 所有商用 NAS | 通用、天然 RWX、支持热迁移 | 延迟高于块存储 |
| 厂商 CSI(Trident/Synology/华为等) | 中高端 NAS | 快照/在线扩容/精简配置 | 需装厂商组件 |
| iSCSI | IOPS/延迟敏感 | 块级性能最好 | RWO 单挂载,不能热迁移 |
落地四步
步骤 ①:NAS 侧创建 NFS 导出
导出目录:/exports/kubevirt
选项: rw, sync, no_root_squash, no_subtree_check⚠️
no_root_squash很关键:virt-launcher 内 qemu 以非 root(uid 107)运行, 若开了 root_squash 会导致磁盘写Permission denied。
步骤 ②:部署 nfs-subdir-external-provisioner 指向 NAS
yaml
env:
- name: NFS_SERVER
value: "<NAS IP>"
- name: NFS_PATH
value: "/exports/kubevirt"步骤 ③:创建专用 StorageClass
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: vm-nas
provisioner: cluster.local/nfs-subdir-external-provisioner
reclaimPolicy: Retain # VM 盘建议 Retain 防误删
allowVolumeExpansion: true # ★ 允许扩容(4.2 用到)
mountOptions:
- nfsvers=4.1
- hard # ★ NAS 故障时 IO 挂起等待,恢复后自愈
- timeo=600
- retrans=5
- noatime步骤 ④:验证
bash
# 各节点手动挂载
mount -t nfs4 <NAS IP>:/exports/kubevirt /mnt/t && touch /mnt/t/x && rm /mnt/t/x
# PVC 秒级 Bound、写文件无 Permission denied用 NAS 起一台 VM(完整链路)
NAS 上放 qcow2 镜像 → 用 HTTP 暴露 → CDI DataVolume 导入(qcow2 自动转 raw)
→ 生成 PVC → VM 从该 PVC 启动yaml
# DataVolume 导入示例
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: vm001-rootdisk
spec:
source:
http:
url: "http://<NAS或文件服务器>/images/rocky9.qcow2"
pvc:
storageClassName: vm-nas
accessModes: [ReadWriteMany] # ★ 需要热迁移用 RWX
resources:
requests:
storage: 20Gibash
kubectl get dv vm001-rootdisk # PHASE=Succeeded 即导入完成⚠️ 注意:CDI 不支持
nfs://直接导入,NAS 上的 qcow2 需先用 HTTP 暴露。 导入产物在 Filesystem PVC 上固定为disk.img(raw)。 更多容量规划、IOPS 估算见../kubevirt-nas-storage-guide.md。
4.2 如何扩容资源(CPU/内存/磁盘)
扩容分两类:计算资源(CPU/内存)和磁盘。改法不同。
(A) 扩 CPU / 内存
方式一:在线扩(需客户机支持热插)
bash
virtctl vm expand rocky9-vm2 --memory 4Gi --cpu 4方式二:改 YAML 后重启(最通用、最稳)
bash
kubectl edit vm rocky9-vm2
# 修改 spec.template.spec.domain.cpu.cores
# 修改 spec.template.spec.domain.memory.guest
kubectl apply -f rocky9-vm2.yaml
virtctl restart rocky9-vm2 # 生效需重启说明:
domain属于"硬件规格",多数字段改了要重启才能生效。 扩内存注意节点余量;绑核(dedicatedCpuPlacement)场景扩容受节点物理核限制。
(B) 扩磁盘
前提:StorageClass 已 allowVolumeExpansion: true(见 4.1 步骤③)。
bash
# 1. 扩大 PVC
kubectl patch pvc vm001-rootdisk -p \
'{"spec":{"resources":{"requests":{"storage":"40Gi"}}}}'
# 或 kubectl edit pvc vm001-rootdisk
# 2. 等文件系统层扩容完成
kubectl get pvc vm001-rootdisk -o jsonpath='{.status.capacity.storage}'进客户机扩文件系统(关键,PVC 扩容不会自动扩客户机内文件系统):
bash
# 在客户机内
growpart /dev/vda 1 # 扩分区(若是整盘则跳过)
# ext4:
resize2fs /dev/vda1
# xfs:
xfs_growfs /
df -h # 确认容量生效⚠️ NFS 盘扩容本质是"允许写更大",无真实容量校验;但为一致性与 配额管理,仍应走 PVC 扩容流程并同步改客户机文件系统。 缩容(减小磁盘)风险极高,不建议在线做,需备份后重建。
扩容检查清单
- [ ] 节点有足够 CPU/内存余量(计算扩容)
- [ ] StorageClass
allowVolumeExpansion: true(磁盘扩容) - [ ] 磁盘扩容后进客户机执行了
growpart+resize2fs/xfs_growfs - [ ] 扩容后监控确认容量/性能正常
5. 如何将普通 KVM 转为 KubeVirt 可运行的镜像
把现有 libvirt/KVM 虚拟机搬上 KubeVirt,核心是"导出一块干净的磁盘镜像 → 导入为 PVC → 用 VM 引用"。完整流程如下。
5.1 流程总览
① 源 KVM 关机、导出磁盘(qcow2)
② 清理镜像(主机名/网络/SSH host key,避免克隆冲突)
③ (可选)压缩/瘦身
④ 暴露为 HTTP 或上传
⑤ CDI DataVolume 导入为 PVC
⑥ 创建 VM 引用该 PVC,验证启动5.2 步骤①:导出源磁盘
bash
# 源 KVM 宿主机
virsh list --all # 找到目标域名
virsh shutdown <domain> # 优雅关机(保证磁盘一致性)
virsh domblklist <domain> # 查磁盘路径,如 /var/lib/libvirt/images/vm.qcow2
qemu-img info /var/lib/libvirt/images/vm.qcow2 # 确认格式与虚拟大小5.3 步骤②:清理镜像(关键,避免"克隆后遗症")
直接复制的镜像带有源机的主机名、SSH host key、固定网卡配置,批量使用会冲突。 用 virt-sysprep 清理:
bash
dnf install -y libguestfs-tools
virt-sysprep -a vm.qcow2 \
--operations machine-id,ssh-hostkeys,udev-net-addr,net-hostname,logfiles,tmp-files如果源机网络是写死的静态配置,而目标要走 DHCP/masquerade, 还需要清掉固定网卡配置(或用
virt-customize --run-command改回 DHCP)。
5.4 步骤③:瘦身(可选,减小导入体积)
bash
virt-sparsify --compress vm.qcow2 vm-clean.qcow2
qemu-img info vm-clean.qcow2 # 确认实际占用变小5.5 步骤④:暴露镜像供 CDI 拉取
CDI 支持 http/https、registry、upload 等。内网最常用 HTTP:
bash
# 把镜像放到任意 HTTP 服务(Nginx/文件服务器)
# 现网示例:http://192.168.122.9:9001/repo/osiso/5.6 步骤⑤:DataVolume 导入为 PVC
yaml
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: migrated-vm-rootdisk
namespace: default
spec:
source:
http:
url: "http://192.168.122.9:9001/repo/osiso/vm-clean.qcow2"
pvc:
storageClassName: vm-nas # 用共享存储,方便迁移
accessModes: [ReadWriteMany]
resources:
requests:
storage: 40Gi # ≥ 源磁盘虚拟大小bash
kubectl apply -f dv.yaml
kubectl get dv migrated-vm-rootdisk # 等到 PHASE=Succeeded本地文件不想走 HTTP 时,可直接上传:
virtctl imageupload dv migrated-vm-rootdisk --image-path vm-clean.qcow2
5.7 步骤⑥:创建 VM 并验证
引用 4.1/2.1 的 VM 模板,volumes 指向刚导入的 PVC:
yaml
volumes:
- name: rootdisk
persistentVolumeClaim:
claimName: migrated-vm-rootdiskbash
kubectl apply -f migrated-vm.yaml
virtctl console migrated-vm # 观察引导,确认无文件系统/网络报错5.8 转换注意事项
| 检查项 | 说明 |
|---|---|
| 驱动 | 客户机需有 virtio 驱动;现代 Linux 自带,Windows 需预装 virtio-win |
| 磁盘总线 | 统一用 bus: virtio,性能最佳 |
| 固件 | 源机若是 UEFI 启动,VM 需配 firmware.bootloader.efi |
| 网络 | 默认走 masquerade,客户机网卡建议改回 DHCP |
| 容量 | PVC storage ≥ 源磁盘虚拟大小(不是实际占用) |
| cloud-init | 若源机装过,注意清理,避免与注入配置冲突 |
| License/绑定 | 检查软件是否绑定 MAC/CPU,必要时固定 macAddress |
批量迁移时:先转一台做"金盘",验证通过后用第 7 章克隆快速复制。
6. 如何备份
6.1 备份对象与策略分层
| 对象 | 备份方式 | 频率建议 |
|---|---|---|
| VM 定义(YAML) | 入库 Git(GitOps) | 每次变更 |
| 磁盘数据 | VM Snapshot / Export / Velero | 每日 |
| 平台配置(KubeVirt CR/CRD) | kubectl get -o yaml 入库 | 每次变更 |
| etcd | RKE2 自带快照 + 异地 | 每日 |
原则(3-2-1):3 份数据、2 种介质、1 份异地。快照和原数据同机不算备份。
6.2 方案一:VirtualMachineSnapshot(磁盘支持快照时首选)
要求 StorageClass 支持 CSI VolumeSnapshot。NFS subdir provisioner 不支持, 此时跳到方案二。
bash
# 建议先停或静默客户机(装了 qemu-guest-agent 可文件系统冻结)
virtctl stop rocky9-vm2yaml
apiVersion: snapshot.kubevirt.io/v1beta1
kind: VirtualMachineSnapshot
metadata:
name: rocky9-snap-20260903
spec:
source:
apiGroup: kubevirt.io
kind: VirtualMachine
name: rocky9-vm2bash
kubectl apply -f snap.yaml
kubectl get virtualmachinesnapshot rocky9-snap-20260903 # PHASE=Succeeded恢复(先停目标):
yaml
apiVersion: snapshot.kubevirt.io/v1beta1
kind: VirtualMachineRestore
metadata:
name: rocky9-restore
spec:
target:
apiGroup: kubevirt.io
kind: VirtualMachine
name: rocky9-vm2
virtualMachineSnapshotName: rocky9-snap-202609036.3 方案二:VirtualMachineExport 导出(NFS 环境通用)
把磁盘导出为文件/镜像,天然适合异地备份与跨集群迁移:
yaml
apiVersion: export.kubevirt.io/v1beta1
kind: VirtualMachineExport
metadata:
name: rocky9-export
spec:
source:
apiGroup: kubevirt.io
kind: VirtualMachine
name: rocky9-vm2bash
kubectl apply -f export.yaml
kubectl get virtualmachineexport rocky9-export # 等待 Ready,拿到导出 URL/secret
# 用 virtctl 拉取
virtctl export vm rocky9-vm2 --output=rocky9-backup.tar.gz
# 再把文件放到 NAS/对象存储异地保存6.4 方案三:Velero(集群级、含 PVC 数据)
用 Velero + node-agent(restic/kopia)备份整个命名空间的 VM 与 PVC:
bash
# 备份前先停 VM 保证一致性(或用 fs-freeze)
velero backup create kubevirt-vms-20260903 \
--include-namespaces default \
--include-resources virtualmachines.kubevirt.io,persistentvolumeclaims
velero schedule create kubevirt-daily --schedule="0 2 * * *" --ttl 168h6.5 备份验证(必做)
备份不演练等于没备份:
bash
# 定期从备份恢复到临时命名空间/新 VM,启动验证数据完整
kubectl get vm -n restore-test
virtctl console restored-vm7. 如何克隆
7.1 整台克隆:VirtualMachineClone(推荐)
一条 CR 克隆 VM + 所有磁盘 + 相关 Secret/ConfigMap:
yaml
apiVersion: clone.kubevirt.io/v1alpha1
kind: VirtualMachineClone
metadata:
name: clone-rocky9
spec:
source:
apiGroup: kubevirt.io
kind: VirtualMachine
name: rocky9-vm2 # 源
target:
apiGroup: kubevirt.io
kind: VirtualMachine
name: rocky9-vm3 # 目标(新名字)bash
kubectl apply -f clone.yaml
kubectl get virtualmachineclone clone-rocky9 # PHASE=Succeeded
virtctl start rocky9-vm3克隆出的 VM 拥有独立磁盘,与源互不影响。注意目标标签要改唯一, 避免 Service 误匹配。
7.2 只克隆磁盘:CDI DataVolume
yaml
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: newvm-rootdisk
spec:
source:
pvc:
namespace: default
name: rocky9-vm2-disk # 源 PVC
pvc:
storageClassName: vm-nas
accessModes: [ReadWriteMany]
resources:
requests:
storage: 20GiCDI 智能克隆优先走 CSI 快照(秒级),不支持快照时自动退化为网络复制。
7.3 黄金镜像批量发放(生产最佳实践)
源 qcow2 导入一次 → 金盘(golden-rocky9)→ DataSource 版本化
→ 每台新 VM 从 DataSource 克隆(秒级开盘)yaml
# ① DataSource 指向金盘
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataSource
metadata:
name: rocky9-golden
spec:
source:
pvc: { namespace: default, name: golden-rocky9 }
---
# ② 新 VM 的 dataVolumeTemplates 里引用
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: web-vm-001
spec:
dataVolumeTemplates:
- metadata: { name: web-vm-001-root }
spec:
sourceRef:
kind: DataSource
name: rocky9-golden # ★ 指向金盘
pvc:
storageClassName: vm-nas
accessModes: [ReadWriteMany]
resources: { requests: { storage: 20Gi } }配合
DataImportCron可定时从镜像库刷新金盘版本,实现"镜像升级一键下发"。
8. 如何灾备
8.1 灾备分层模型
第一层:组件高可用 → virt-* 多副本 + PDB,控制面自愈(现网已具备)
第二层:VM 故障自愈 → runStrategy: Always + 共享存储 + 容忍度,节点宕机分钟级恢复
第三层:维护零停机 → evictionStrategy: LiveMigrate,滚动维护不中断
第四层:数据级恢复 → 快照/导出/异地备份(第 6 章)
第五层:站点级灾备 → 跨集群/异地重建(本节重点)8.2 站点级灾备:跨集群恢复
目标:主集群整体不可用时,在备用集群恢复业务。
前置:平时就要做好的三件事
| 事项 | 说明 |
|---|---|
| VM 定义入 Git | 所有 VM/Service/NAD YAML 版本化,可一键重放 |
| 磁盘定期异地备份 | 用第 6 章方案把磁盘导出到独立存储/对象存储 |
| 备用集群就绪 | 备用集群预装同版本 KubeVirt + CDI + 相同 StorageClass |
灾难发生时的恢复流程
① 确认主集群不可恢复,宣布切换
② 备用集群 apply Git 中的 VM/网络定义
③ 从异地备份恢复磁盘:
- 方案 A:DataVolume source http 重新导入镜像
- 方案 B:从备份的 PVC 快照/导出文件恢复
④ 修正存储类/网段差异(StorageClassName、静态 IP)
⑤ 逐台启动验证,切流量(DNS/负载均衡指向备用集群)恢复磁盘示例(从导出文件重新导入):
yaml
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: dr-vm-rootdisk
spec:
source:
http:
url: "http://<备份存储>/backups/rocky9-backup.disk.img"
pvc:
storageClassName: vm-nas-dr # 备用集群的 SC
accessModes: [ReadWriteMany]
resources: { requests: { storage: 40Gi } }8.3 数据保护增强(可选)
- 存储级复制:商用 NAS 远程复制 / Ceph 跨集群镜像,RPO 可达分钟级;
- 厂商 CSI 快照异地化:快照导出到第二台存储;
- RPO/RTO 定义:
- RPO(数据丢失容忍):决定备份/复制频率;
- RTO(恢复时间目标):决定是冷备重建还是热备双活。
8.4 灾备演练(必须定期做)
| 演练项 | 频率 | 验证点 |
|---|---|---|
| 单节点宕机自愈 | 季度 | VM 自动在其他节点恢复 |
| 热迁移 | 季度 | 维护窗口零中断 |
| 快照/备份恢复 | 季度 | 数据完整、可启动 |
| 站点级切换 | 半年/年 | 备用集群端到端恢复 |
现网风险盘点与加固清单见
../kubevirt-storage-ha-guide.md。
9. 附录:命令速查与 FAQ
9.1 命令速查
bash
# --- 生命周期 ---
virtctl start/stop/restart/pause/unpause <vm>
virtctl migrate <vm> # 热迁移
virtctl vm expand <vm> --memory --cpu # 在线扩规格
virtctl console/vnc/ssh <vm> # 接入
# --- 磁盘 ---
kubectl get dv # DataVolume 状态
virtctl imageupload dv <name> -f x.qcow2 # 上传镜像
virtctl guestfs <pvc> # 临时 Pod 挂载改盘
# --- 快照/克隆/导出 ---
kubectl get virtualmachinesnapshot / virtualmachineclone / virtualmachineexport
# --- 网络 ---
kubectl get net-attach-def # Multus 网络
virtctl expose vm <vm> --port 22 --type NodePort
kubectl get vmi <vm> -o jsonpath='{.status.interfaces}' # VM IP
# --- 排障 ---
kubectl describe vmi <vm>
kubectl logs virt-launcher-<vm>-xxx -c compute
kubectl get events -n default --sort-by=.lastTimestamp9.2 FAQ
Q1:Pod IP 能当固定业务 IP 吗? 不能。Pod IP 随重建变化,固定访问用 NodePort 或 Multus 静态 IP(见 3.2)。
Q2:为什么我的 VM 不能热迁移? 多数是磁盘非 ReadWriteMany(本地盘/RWO)。换共享存储或接受冷迁移。
Q3:改了 userData/networkData 为什么没生效? cloud-init 只在首次启动生效。需清理客户机 /var/lib/cloud/instances 或重建磁盘。
Q4:NFS 上能做快照备份吗? nfs-subdir-provisioner 不支持 CSI 快照,用 VirtualMachineExport 或 Velero(见第 6 章)。
Q5:导入后磁盘文件叫什么? Filesystem PVC 上固定为 disk.img(raw 格式),勿手工塞 qcow2。
Q6:怎么知道宿主机资源不够了? 看两类信号:① 节点真实水位——CPU/内存/磁盘利用率、节点 MemoryPressure/DiskPressure condition、kubelet 驱逐;② 调度容量—— requests/allocatable 比值 > 85%、virt-launcher Pod Unschedulable、 VMI 卡 Scheduling。完整 PrometheusRule 告警规则与定位命令见教材 kubevirt-tutorial-beginner-to-master.md 16.5~16.7。
Q6:如何批量建同配置 VM? 金盘 + DataSource + dataVolumeTemplates(见 7.3)。
Q7:VM 卡在 Pending/Scheduling? 看 kubectl describe vmi:多为 /dev/kvm 缺失、资源不足、PVC 未 Bound。
9.3 相关文档索引
| 主题 | 文档 |
|---|---|
| 系统理论 | kubevirt-tutorial-beginner-to-master.md |
| 完整部署记录 | ../rke2-kubevirt-deployment-guide.md |
| CDI 离线部署 | ../cdi-offline-deployment-guide.md |
| NAS 存储细节 | ../kubevirt-nas-storage-guide.md |
| 存储高可用 | ../kubevirt-storage-ha-guide.md |
| 网络/SSH/密码 | xxx |
文档维护:本文档基于 2026-09-03 现网环境编写。KubeVirt/CDI 升级后, 请以官方文档复核 API 字段;重大变更请同步更新本文档并记录日期。