主题
CDI v1.60.4 离线简化部署指南(隔离环境 / 私有镜像仓库)
日期: 2026-09-03 环境: RKE2 v1.35.6(6 节点)+ KubeVirt v1.9.0,Rocky Linux 9.8 入口节点:
root@192.168.122.31(sza122031.local) CDI 版本: v1.60.4,镜像统一为harbor.yundasys.com/kubevirt/kubevirt/*:v1.60.4关联文档:rke2-kubevirt-deployment-guide.md、kubevirt-nas-storage-guide.md、kubevirt-storage-ha-guide.md
目录
- 背景与目标
- 为什么做单文件简化清单
- 清单内容与镜像列表
- 部署步骤
- 部署验证
- 端到端实践:DataVolume 导入 qcow2 并启动 VM
- 机制说明:HonorWaitForFirstConsumer 与 launcher Pod
- 生产建议与踩坑记录
- 卸载与清理
- 附录:交付物清单
1. 背景与目标
- 目标集群处于隔离网络,无法访问
quay.io,内部仅有私有仓库harbor.yundasys.com - KubeVirt 虚拟机需要持久化磁盘:通过 CDI(Containerized Data Importer)实现 镜像导入(HTTP/S3/Registry)、PVC 克隆、镜像上传、空白盘创建
- 交付目标:
- 一份可直接
kubectl apply的单文件部署清单cdi-deploy.yaml - 7 个离线镜像(统一推到内部 Harbor)
- 已在 192.168.122.31 集群完成端到端验证(导入 → 起 VM → 网络连通)
- 一份可直接
CDI 与 KubeVirt 是松耦合关系:CDI 可以独立安装(operator 只要求 K8s API 可用), KubeVirt 通过 DataVolume/PVC 使用 CDI 提供的磁盘,两者版本无需严格一致。
2. 为什么做单文件简化清单
官方安装方式是两份清单 cdi-operator.yaml + cdi-cr.yaml,在隔离环境有几个不便:
| 问题 | 说明 |
|---|---|
官方 cdi-cr.yaml 体积大 | 内含大量示例 DataVolume / example 定义,隔离环境用不上 |
镜像地址固定 quay.io | 需要逐个替换为内部 Harbor 地址 |
| 两次 apply 有启动顺序 | operator 先起、CR 后建,离线排障链路长 |
因此采用运行时导出简化方案:
源集群标准安装并验证 → 导出全部 CDI 运行时对象 → 清洗运行时字段
→ 替换镜像地址 → 单文件 cdi-deploy.yaml(51 个对象)- 清洗内容:
status、resourceVersion、uid、creationTimestamp、generation等 - 已通过源集群 server-side dry-run 验证:51/51 对象全部被 API Server 接受
- 该清单与官方
cdi-operator.yaml + cdi-cr.yaml二选一,不要混用
3. 清单内容与镜像列表
3.1 对象构成(共 51 个)
| 类型 | 数量 | 说明 |
|---|---|---|
| Namespace | 1 | cdi |
| CustomResourceDefinition | 12 | DataVolume、CDI、CDIConfig、StorageProfile、DataSource 等 |
| ClusterRole | 9 | operator / controller / apiserver 等 |
| ClusterRoleBinding | 6 | 同上绑定 |
| Role | 5 | cdi 命名空间内 |
| RoleBinding | 5 | 同上绑定 |
| ServiceAccount | 5 | cdi-operator / cdi-sa / cdi-apiserver 等 |
| Service | 3 | apiserver / uploadproxy 等 |
| Deployment | 4 | cdi-operator、cdi-apiserver、cdi-deployment(controller)、cdi-uploadproxy |
| CDI(CR) | 1 | spec.config.honorWaitForFirstConsumer: true、filesystemOverhead: 5.5% |
importer / cloner / uploadserver 不是常驻 Deployment,由 controller 在 导入/克隆/上传任务发生时动态创建,任务结束后自动清理。
3.2 镜像列表(7 个,tag 均为 v1.60.4)
harbor.yundasys.com/kubevirt/kubevirt/cdi-apiserver:v1.60.4
harbor.yundasys.com/kubevirt/kubevirt/cdi-cloner:v1.60.4
harbor.yundasys.com/kubevirt/kubevirt/cdi-controller:v1.60.4
harbor.yundasys.com/kubevirt/kubevirt/cdi-importer:v1.60.4
harbor.yundasys.com/kubevirt/kubevirt/cdi-operator:v1.60.4
harbor.yundasys.com/kubevirt/kubevirt/cdi-uploadproxy:v1.60.4
harbor.yundasys.com/kubevirt/kubevirt/cdi-uploadserver:v1.60.4有外网的机器上可用 skopeo 或 crane 从 quay.io/kubevirt/*:v1.60.4 同步:
bash
for i in cdi-apiserver cdi-cloner cdi-controller cdi-importer \
cdi-operator cdi-uploadproxy cdi-uploadserver; do
skopeo copy --all \
docker://quay.io/kubevirt/$i:v1.60.4 \
docker://harbor.yundasys.com/kubevirt/kubevirt/$i:v1.60.4
done4. 部署步骤
4.1 前置条件
- kubectl 可访问目标集群,RKE2/K8s ≥ 1.28
- 集群有可用的 StorageClass(
local-path或 NAS 的 NFS SC 均可) - 7 个镜像已推入内部 Harbor(隔离集群节点可拉取)
4.2 清理历史残留(重要)
如果集群曾装过 CDI 又卸载不干净,cluster-scoped RBAC / CRD 残留会导致新安装冲突, 先清理:
bash
kubectl get crd -o name | grep -E 'cdi\.kubevirt\.io' | xargs -r kubectl delete --ignore-not-found
kubectl delete clusterrole cdi-operator-cluster-role cdi-cluster-role \
cdi-apiserver-cluster-role --ignore-not-found
kubectl delete clusterrolebinding cdi-operator-role-binding cdi-cluster-rolebinding \
cdi-apiserver-cluster-rolebinding --ignore-not-found
kubectl delete ns cdi --ignore-not-found --wait=false4.3 一键部署
bash
kubectl apply -f cdi-deploy.yaml
# 等待全部 Deployment 就绪(约 30~60 秒)
kubectl -n cdi get deploy
# 期望: cdi-apiserver / cdi-deployment / cdi-operator / cdi-uploadproxy 全部 READY 1/1
# CDI CR 状态
kubectl get cdi cdi -o jsonpath='{.status.phase}'
# 期望: Deployed5. 部署验证
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| 命名空间 | kubectl get ns cdi | Active |
| 组件 | kubectl -n cdi get deploy | 4 个 Deployment 全部 READY |
| CR 状态 | kubectl get cdi cdi | PHASE=Deployed |
| StorageProfile | kubectl get storageprofile | 每个 SC 自动生成对应 profile |
| CRD | kubectl get crd | grep cdi.kubevirt.io | 12 个 |
6. 端到端实践:DataVolume 导入 qcow2 并启动 VM
在 192.168.122.31 集群实测通过,完整链路:HTTP 上的 qcow2 → CDI 导入 PVC → VM 启动 → 网络连通。
6.1 DataVolume(导入 Rocky9 根盘)
yaml
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: rocky9-root-dv
namespace: default
spec:
source:
http:
url: "http://192.168.122.9:9001/images/rocky9-server-root.qcow2"
pvc:
storageClassName: local-path
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 20Gibash
kubectl apply -f example-datavolume-vm.yaml
kubectl get dv -w # ImportInProgress → Succeeded实测时间线:
| 阶段 | 耗时 | 现象 |
|---|---|---|
| PVC Pending | ~10s | WaitForFirstConsumer,CDI 创建 cdi-launcher-* 消费 Pod |
| PVC Bound | ~20s | launcher 触发 local-path 在目标节点建 PV |
| Import | ~2.5 min | importer-rocky9-root-dv Pod 下载并转换 qcow2 |
| Succeeded | - | PVC 上生成 disk.img(raw 格式) |
6.2 引用 DataVolume 的虚拟机
yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: rocky9-dv-vm
spec:
running: true
template:
spec:
domain:
devices:
disks:
- name: rootdisk
disk:
bus: virtio
resources:
requests:
memory: 2Gi
volumes:
- name: rootdisk
dataVolume:
name: rocky9-root-dv6.3 实测结果
rocky9-dv-vmVMI Running,Pod 网络 10.22.0.101- 从其他节点
ping 10.22.0.101连通 ✅ - VM 重启后磁盘数据保留 ✅
7. 机制说明:HonorWaitForFirstConsumer 与 launcher Pod
local-path / hostPath 这类本地存储的 PV 带节点亲和性,若 PVC 提前绑定到错误节点, 导入出来的盘就无法被 VM 使用。CDI 的处理机制:
① DataVolume 创建 PVC(WaitForFirstConsumer,保持 Pending)
② CDI controller 创建极简的 cdi-launcher-<pvc> Pod 作为"第一个消费者"
③ 调度器为 launcher 选节点 → local-path 在该节点创建 PV → PVC Bound
④ launcher 退出,CDI 在同一节点创建 importer Pod 写盘
⑤ 导入完成,后续消费该 PVC 的 VM 被调度到同一节点(节点亲和自动生效)cdi-deploy.yaml中 CDI CR 已设spec.config.honorWaitForFirstConsumer: true- 对**共享存储(NFS)**无此问题:PVC 可被任意节点挂载,无需 launcher 定点
8. 生产建议与踩坑记录
- importer 别落在控制面节点:本次实测导入器调度到控制面节点(.31), qcow2 解压 + 格式转换瞬间吃满 CPU/内存,短暂影响 APIServer 响应。 生产集群控制面节点应加污点,或让"第一个消费者"(临时消费 Pod / VM)通过
nodeSelector/nodeAffinity钉在 worker 上。 - 文件系统开销:Filesystem 模式默认预留 5.5%(CR
spec.config.filesystemOverhead), 20Gi PVC 实际可用 ~18.9Gi,规划磁盘容量时计入。 - scratch 空间:导入大镜像需要临时空间,默认走空目录;空间紧张的节点可配
spec.config.scratchSpaceStorageClass指向富余的 SC。 - 镜像预载:隔离集群部署前务必确认 7 个镜像在
harbor.yundasys.com/kubevirt/kubevirt/且节点containerd可拉取(注意 Harbor 证书信任)。 - 版本搭配:CDI v1.60.4 与 KubeVirt v1.9.0 已实测兼容;升级时任一组件前先读 官方 release note 的存储兼容性说明。
9. 卸载与清理
bash
# 删除 CDI(PVC/磁盘数据不会随 CDI 删除,按需自行清理)
kubectl delete -f cdi-deploy.yaml
# 确认无 cluster-scoped 残留
kubectl get crd -o name | grep cdi.kubevirt.io # 应为空
kubectl get clusterrole,clusterrolebinding -o name | grep -i cdi # 应为空10. 附录:交付物清单
| 文件 | 说明 |
|---|---|
/u02/repo/kubevirt/cdi/cdi-deploy.yaml | 部署主文件(51 对象单文件,隔离环境直接 apply) |
/u02/repo/kubevirt/cdi/cdi-ns-all-raw.yaml | 清洗前的原始导出(留档参考) |
/u02/repo/kubevirt/cdi/README.md | 部署与验证说明 |
/u02/repo/kubevirt/cdi/images.txt | 7 个离线镜像清单 |
/u02/repo/kubevirt/cdi/example-datavolume-vm.yaml | DataVolume + VM 端到端示例 |