KubeVirt 容器镜像仓库磁盘(Container Registry Disks)实战指南:从镜像推送、VMI 挂载到运行时原理

发布时间:2026/10/8 14:04:20
KubeVirt 容器镜像仓库磁盘(Container Registry Disks)实战指南:从镜像推送、VMI 挂载到运行时原理 云原生【免费下载链接】kubevirtKubernetes Virtualization API and runtime in order to define and manage virtual machines.项目地址https://gitcode.com/gh_mirrors/ku/kubevirt点击查看免费下载KubeVirt 允许用户像管理 Pod 一样管理虚拟机实例VMI。为了让 VMI 磁盘的存储与分发也复用 Kubernetes 生态中成熟的容器镜像工作流KubeVirt 提供了容器镜像仓库磁盘Container Registry Disk机制用户把磁盘镜像打包进容器镜像并推送到镜像仓库KubeVirt 运行时自动拉取该镜像、将磁盘以本地文件形式暴露给 Libvirt从而挂载到 VMI 上。读完本文你将掌握把 qcow2/raw 磁盘推送进容器镜像仓库的标准流程、在 VMI 定义中声明containerDisk卷的正确写法以及从 virt-controller 生成包装容器到 virt-handler 注入 Domain XML 的完整运行时链路。背景与动机把容器的工作流延伸到虚拟机磁盘KubeVirt 的目标是在现有 Kubernetes 集群上以即插即用的方式运行 VMI其中一个核心设计理念就是复用 Kubernetes 为容器建立的工具与工作流并将其应用到虚拟化负载上。例如VMI 像 Pod 一样被提交到 Kubernetes 并接受管理VMI 未来可以像 Pod 一样接入 Kubernetes Overlay 网络。沿着这条思路下一个要攻克的问题就是让 VMI 磁盘的分发机制与 Pod 保持一致。容器世界里应用以镜像形式存储于容器镜像仓库通过 Pod 引用镜像名即可在任何节点启动。KubeVirt 将同样的模式引入虚拟机领域把 VMI 磁盘打包进容器镜像存入容器镜像仓库运行时按需拉取并挂载。这便是本文要介绍的 Container Registry Disk 功能。核心需求该功能需要同时满足两端用户侧必须有一条标准工作流能把 VMI 磁盘推送到容器镜像仓库运行时侧KubeVirt 运行时必须能够消费存储在镜像仓库中的 VMI 磁盘并将其挂载到 VMI 上。适用场景本地临时磁盘Ephemeral Disk容器镜像仓库磁盘在架构上定位为本地临时磁盘主要有三类典型使用场景场景说明特点不可变 VMI 从临时磁盘启动用户希望启动由本地临时存储支撑的 VMI 负载负载无需在 VMI 重启后保留也不要求实时迁移支持不可变 VMI 副本从本地临时磁盘启动用户希望基于本地临时存储横向扩展 VMI 负载同一镜像可被集群中任意数量的 VMI 引用该能力在原设计文档中标注为Pending introduction and implementation of VMIGroup object即当时尚待 VMIGroup 对象的引入与实现非启动的本地临时数据盘给 VMI 附加一块预置数据的非启动磁盘数据可以是配置、RPM 包或任何需要随 VMI 分发的文件从仓库当前实现看容器磁盘已不止于不支持迁移的早期形态pkg/virt-handler/migration-target.go中已存在针对迁移目标节点的容器磁盘挂载处理说明运行时链路已演进得更加完整上述场景仍是容器磁盘最典型的使用方式。高层的架构设计标准化的 VMI 磁盘包装容器整个方案的基石是 KubeVirt 提供的一个标准化的基础包装容器镜像Base Wrapper Container。该镜像内部不携带任何 VMI 磁盘只包含以 Libvirt 可消费的形式提供 VMI 磁盘所需的一切。默认约定如下只要把任意 VMI 磁盘放进包装容器的/disk目录KubeVirt 就会把它作为一个可供 Libvirt 消费的块设备暴露出来。/disk这一约定路径在源码中有明确体现pkg/os/disk/validation.go定义了DiskSourceFallbackPath /disk而pkg/storage/container-disk/container-disk.go的GetImage()在未指定自定义路径时会回退到该目录读取磁盘文件并约束该目录内只能有一个磁盘文件——多于一个文件会报错more than one file found in folder /disk, only one disk is allowed空目录则会报no file found in folder /disk, no disk present。用户工作流把 VMI 磁盘推送到容器镜像仓库制作并推送磁盘镜像原设计文档给出的标准做法是基于scratch基础镜像把磁盘文件放进/disk/目录并构建、推送。仓库中cmd/container-disk-v2alpha/README.md给出了兼容并更完整的版本cat END Dockerfile FROM scratch ADD fedora25.qcow2 /disk/ END docker build -t vmdisks/fedora25:latest . docker push vmdisks/fedora25:latest要点说明磁盘格式qcow2 和 raw 两种格式均受支持直接拷贝进/disk/目录即可目录约定磁盘必须放在/disk/即源码中的DiskSourceFallbackPath且该目录内只允许放置一个磁盘文件推送目的地可以是 Docker Hub 等公共仓库也可以是集群内可访问的私有 registry——examples/vmi-ephemeral.yaml中的示例即引用了registry:5000/kubevirt/cirros-container-disk-demo:devel这样的集群内镜像仓库地址推送完成后集群内任意位置的任意数量 VMI都可以引用同一镜像启动天然支持横向扩展。提示文档示例中使用了--chown107:107指定文件属主。容器磁盘包装容器以非 root 用户运行源码中RunAsNonRoot固定为 trueRunAsUser使用util.NonRootUID确保镜像内磁盘文件权限与运行用户匹配可避免挂载/读取阶段的权限问题。在 VMI 定义中引用容器磁盘推送到仓库后在 VMI 的spec.domain.devices.disks中引用镜像名即可。原设计文档当时格式ContainerDisk:v1alpha中的示例为kind: VirtualMachineInstance spec: domain: devices: disks: - type: ContainerDisk:v1alpha - source: name: vmdisks/fedora25:latest - target: device: sda仓库当前更完整、可直接运行的写法ContainerDisk:v2alpha格式如下来自cmd/container-disk-v2alpha/README.mdapiVersion: kubevirt.io/v1 kind: VirtualMachine metadata: creationTimestamp: null name: vm-ephemeral spec: domain: devices: disks: - disk: bus: virtio name: containerdisk volumeName: registryvolume machine: type: resources: requests: memory: 64M terminationGracePeriodSeconds: 0 volumes: - name: registryvolume containerDisk: image: kubevirt/cirros-container-disk-demo:devel status: {}创建后启动 VM/VMI 与启动 Pod 一样简单kubectl create -f vm.yaml仓库中还提供了一份最小可运行的 VMI 示例examples/vmi-ephemeral.yamlkind: VirtualMachineInstance其volumes段声明如下volumes: - containerDisk: image: registry:5000/kubevirt/cirros-container-disk-demo:devel name: containerdiskcontainerDisk卷的可配置字段containerDisk卷的类型定义位于 staging/src/kubevirt.io/api/core/v1/schema.go通过ContainerDiskSource结构体暴露以下字段字段JSON 键说明Imageimage内嵌磁盘的镜像名称必填ImagePullSecretimagePullSecret拉取私有镜像所需的 Docker registry Secret 名称Secret 必须已存在可选Pathpath磁盘文件在容器内的路径可选为空时回退到/disk目录并遵循单文件约束ImagePullPolicyimagePullPolicy镜像拉取策略取值为Always、Never、IfNotPresent之一。默认带:latest标签时为Always否则为IfNotPresent该字段不可更新运行时实现从拉取镜像到挂载磁盘的完整链路第一步virt-controller 生成包装容器当 virt-controller 发现一个 VMI 对象带有ContainerDisk类型的卷时它会在 virt-launcher Pod 中放置镜像包装容器与监控 VMI 进程的容器一同运行。这一逻辑的核心实现在pkg/storage/container-disk/container-disk.goGenerateInitContainers()/GenerateContainers()遍历vmi.Spec.Volumes对每个containerDisk卷调用generateContainerFromVolume()生成对应的包装容器 spec生成的容器以/usr/bin/container-disk为启动命令并区分两种运行模式见下节--no-op与--copy-path容器以非 root 用户运行RunAsNonRoot: true、AllowPrivilegeEscalation: false并Drop: [ALL]全部 Linux capabilities符合安全加固预期容器名称由卷名推导volumevolumeName如卷containerdisk对应容器volumecontainerdiskinit 版本追加-init后缀。第二步virt-launcher Pod 启动包装容器拷贝磁盘当 virt-launcher Pod 启动时包装容器负责把用户镜像中的磁盘以文件形式拷贝到共享的主机目录。真正执行拷贝的是cmd/container-disk-v2alpha/main.c编译出的container-disk二进制它支持两个命令行参数--copy-path path指定拷贝目标路径二进制会创建目标目录并把自身持有的磁盘服务出来--no-op空操作立即退出——init 容器使用该模式作用是先于主容器完成镜像拉取与磁盘就位保证后续容器启动时磁盘已可用。该程序实现上使用 UNIX domain socket 作为生命周期信号virt-launcher侧的pkg/virt-handler/container-disk/mount.go通过containerdisk.NewSocketPathGetter()轮询disk_idx.sock之类的 socket 路径等待拷贝完成而main.c中socket_check()线程会周期性检查 socket 文件是否存在一旦 socket 被移除或收到 SIGTERM即认为容器应退出——注释中说明这是为了应对不同容器运行时下信号送达不及时的兜底措施。从 virt-controller 视角看各组件共享的目录与文件命名约定定义于pkg/storage/container-disk/container-disk.go挂载目录/var/run/kubevirt/container-disks/vmi-uid/GetVolumeMountDirOnGuest/GetLegacyVolumeMountDirOnHost磁盘文件命名disk_volumeIndex.imgGetDiskTargetName拷贝目标--copy-path 挂载目录/disk_volumeIndex最终以.img形式呈现给 Libvirt。第三步virt-handler 注入 Domain XML当 virt-handler 发现 VMI 被调度到本节点且带有ContainerDisk卷时它会向 Domain XML 注入必要配置把磁盘以本地文件的形式附加给虚拟机。相关挂载与文件定位逻辑集中在pkg/virt-handler/container-disk/mount.go通过GetDiskTargetDirFromHostView()从 kubelet 的 emptyDir 卷路径.../pods/pod-uid/volumes/kubernetes.io~empty-dir/container-disks解析出宿主机视图下的磁盘目录通过 socket 路径探测确认磁盘已拷贝完成再将实际磁盘文件路径接入 virt-launcher 生成的 Domain 定义。附带细节镜像 digest 锁定与资源默认值digest 锁定ExtractImageIDsFromSourcePod()会从源 Pod 的容器状态中提取镜像 digestsha256:...把volume.ContainerDisk.Image更新为带 digest 的精确引用toImageWithDigest保证迁移等场景下跨节点拉取到的是同一份内容资源默认值getMinimalInitContainerDiskResources()为每个磁盘包装容器设置了最小资源请求/限制CPU 请求1m、限制10m内存请求1M、限制40M临时存储开销50M并允许通过集群配置的GetSupportContainerRequest/Limit(v1.ContainerDisk, ...)覆盖。版本演进与兼容性v1alpha 与 v2alpha原设计文档将磁盘类型记为ContainerDisk:v1alpha并说明其中的v1 部分代表 virt-handler 磁盘转换过程所采用的标准随着功能演进KubeVirt 可能为磁盘的容器包装方式制定新标准同时保持向后兼容。仓库中的实现印证了这一演进方向cmd/container-disk-v2alpha目录下的基础容器即命名为v2alpha其 README 明确说明该基础容器与ContainerDisk:v1alpha磁盘类型兼容并给出了新的containerDisk卷 YAML 写法见上文kind: VirtualMachine示例。也就是说用户在 VMI 定义中声明的是containerDisk卷与镜像名具体的 v1/v2 协议差异对用户透明由 KubeVirt 运行时的包装容器版本决定。限制与注意事项临时性容器镜像仓库磁盘本质是 ephemeral 存储数据随 VMI 生命周期存在不适用于需要跨重启持久化的负载持久化需求应改用persistentVolumeClaim等卷类型单磁盘约束未指定path时镜像/disk目录中只允许一个磁盘文件私有仓库凭据拉取私有镜像时需预先创建imagePullSecret并在containerDisk卷中引用镜像拉取策略默认行为与 Kubernetes 容器一致:latest为Always否则为IfNotPresent可通过imagePullPolicy显式指定。小结容器镜像仓库磁盘是 KubeVirt像管理 Pod 一样管理虚拟机理念在存储分发层面的落地磁盘以镜像形式进入容器镜像仓库由 virt-controller 生成包装容器、virt-launcher 拉取并拷贝、virt-handler 注入 Domain XML最终以本地文件挂载给 Libvirt 消费。本文给出的 Dockerfile 构建流程与containerDisk卷声明可直接投入实践深入pkg/storage/container-disk/container-disk.go、pkg/virt-handler/container-disk/mount.go与cmd/container-disk-v2alpha/main.c可以进一步理解其底层实现细节。赞分享云原生【免费下载链接】kubevirtKubernetes Virtualization API and runtime in order to define and manage virtual machines.项目地址https://gitcode.com/gh_mirrors/ku/kubevirt点击查看免费下载相关推荐KubeVirt 容器磁盘镜像container-disk完全指南官方镜像清单与自定义测试镜像构建实战KubeVirt 容器磁盘镜像container disk完全指南官方镜像清单与自定义测试镜像构建实战 本文围绕 KubeVirt 项目中的 contai云原生MassTransit与Azure Container Registry集成容器镜像仓库MassTransit与Azure Container Registry集成容器镜像仓库 在分布式系统架构中消息传递框架与容器化部署的结合已成为企业级应用的后端消息队列微服务消息路由MassTransit与Bitbucket Container Registry集成容器镜像仓库MassTransit与Bitbucket Container Registry集成容器镜像仓库 在分布式系统架构中容器化部署已成为主流方案。MassTra后端消息队列微服务消息路由上一篇SiYuan v3.1.14 版本深度解析数据库计算增强、编辑器交互打磨与数据同步性能优化下一篇Dash-iOS编译器优化LLVM与编译参数调整创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考