Skip to content

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.mdkubevirt-nas-storage-guide.mdkubevirt-storage-ha-guide.md


目录

  1. 背景与目标
  2. 为什么做单文件简化清单
  3. 清单内容与镜像列表
  4. 部署步骤
  5. 部署验证
  6. 端到端实践:DataVolume 导入 qcow2 并启动 VM
  7. 机制说明:HonorWaitForFirstConsumer 与 launcher Pod
  8. 生产建议与踩坑记录
  9. 卸载与清理
  10. 附录:交付物清单

1. 背景与目标

  • 目标集群处于隔离网络,无法访问 quay.io,内部仅有私有仓库 harbor.yundasys.com
  • KubeVirt 虚拟机需要持久化磁盘:通过 CDI(Containerized Data Importer)实现 镜像导入(HTTP/S3/Registry)、PVC 克隆、镜像上传、空白盘创建
  • 交付目标:
    1. 一份可直接 kubectl apply 的单文件部署清单 cdi-deploy.yaml
    2. 7 个离线镜像(统一推到内部 Harbor)
    3. 已在 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 个对象)
  • 清洗内容:statusresourceVersionuidcreationTimestampgeneration
  • 已通过源集群 server-side dry-run 验证:51/51 对象全部被 API Server 接受
  • 该清单与官方 cdi-operator.yaml + cdi-cr.yaml 二选一,不要混用

3. 清单内容与镜像列表

3.1 对象构成(共 51 个)

类型数量说明
Namespace1cdi
CustomResourceDefinition12DataVolume、CDI、CDIConfig、StorageProfile、DataSource 等
ClusterRole9operator / controller / apiserver 等
ClusterRoleBinding6同上绑定
Role5cdi 命名空间内
RoleBinding5同上绑定
ServiceAccount5cdi-operator / cdi-sa / cdi-apiserver 等
Service3apiserver / uploadproxy 等
Deployment4cdi-operatorcdi-apiservercdi-deployment(controller)、cdi-uploadproxy
CDI(CR)1spec.config.honorWaitForFirstConsumer: truefilesystemOverhead: 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

有外网的机器上可用 skopeocranequay.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
done

4. 部署步骤

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=false

4.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}'
# 期望: Deployed

5. 部署验证

检查项命令期望结果
命名空间kubectl get ns cdiActive
组件kubectl -n cdi get deploy4 个 Deployment 全部 READY
CR 状态kubectl get cdi cdiPHASE=Deployed
StorageProfilekubectl get storageprofile每个 SC 自动生成对应 profile
CRDkubectl get crd | grep cdi.kubevirt.io12 个

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: 20Gi
bash
kubectl apply -f example-datavolume-vm.yaml
kubectl get dv -w        # ImportInProgress → Succeeded

实测时间线:

阶段耗时现象
PVC Pending~10sWaitForFirstConsumer,CDI 创建 cdi-launcher-* 消费 Pod
PVC Bound~20slauncher 触发 local-path 在目标节点建 PV
Import~2.5 minimporter-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-dv

6.3 实测结果

  • rocky9-dv-vm VMI 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. 生产建议与踩坑记录

  1. importer 别落在控制面节点:本次实测导入器调度到控制面节点(.31), qcow2 解压 + 格式转换瞬间吃满 CPU/内存,短暂影响 APIServer 响应。 生产集群控制面节点应加污点,或让"第一个消费者"(临时消费 Pod / VM)通过 nodeSelector/nodeAffinity 钉在 worker 上。
  2. 文件系统开销:Filesystem 模式默认预留 5.5%(CR spec.config.filesystemOverhead), 20Gi PVC 实际可用 ~18.9Gi,规划磁盘容量时计入。
  3. scratch 空间:导入大镜像需要临时空间,默认走空目录;空间紧张的节点可配 spec.config.scratchSpaceStorageClass 指向富余的 SC。
  4. 镜像预载:隔离集群部署前务必确认 7 个镜像在 harbor.yundasys.com/kubevirt/kubevirt/ 且节点 containerd 可拉取(注意 Harbor 证书信任)。
  5. 版本搭配: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.txt7 个离线镜像清单
/u02/repo/kubevirt/cdi/example-datavolume-vm.yamlDataVolume + VM 端到端示例